I am wanting to make deterministic (i.e. always the same for the same inputs) Ed25519 key from a master Ed25519 key and a target string (e.g. a website name, ala SQRL). My algorithm takes a 32-byte Argon2 hash using the password as the 32-byte master private key and the salt built from SHA-512 of the target string. It then uses that for Ed25519 generation. Here it is in Go code:

package identity

import (


// KeyDeriver is a simple interface for deterministically deriving keys.
type KeyDeriver interface {
    // Derive a key for the target from the master and target string. For the same input, this must give the exact same
    // output.
    Derive(master ed25519.PrivateKey, target string) ed25519.PrivateKey

// DefaultKeyDeriver uses common Argon2 params with ArgonKeyDeriver
var DefaultKeyDeriver KeyDeriver = &ArgonKeyDeriver{Time: 1, Memory: 64 * 1024, Threads: 4}

// ArgonKeyDeriver derives keys based on Argon2 hashes of master and target values.
type ArgonKeyDeriver struct {
    Time    uint32
    Memory  uint32
    Threads uint8

// Derive implements KeyDeriver.Derive
func (a *ArgonKeyDeriver) Derive(master ed25519.PrivateKey, target string) ed25519.PrivateKey {
    // Build a 32-byte argon2 hash with the private part of the master key as the password, the SHA-512 target as
    // the salt, and the other argon params as given. Then use that 32-byte hash as the input for an ed25519 key.
    // I.e. ed25519-gen(argon2(P=master, S=sha512(target), ...))
    targetHash := sha512.Sum512([]byte(target))
    argonHash := argon2.IDKey(master[:32], targetHash[:], a.Time, a.Memory, a.Threads, 32)
    // Based on reading, only reason this returns an err is if random does which it shouldn't
    _, ret, err := ed25519.GenerateKey(bytes.NewReader(argonHash))
    if err != nil {
    return ret

Ignoring the Argon2 time, memory, and threads parameters, is this a reasonably secure approach? Would I be better served switching the password and salt values?

  • 1
    $\begingroup$ This may be fine, but why is a master Ed25519 key involved? Why don't you just use a 32-byte secret string that is not an Ed25519 key at all? Do you need the master public key to be related to the derived public keys? It would help to give more details about your protocol—not just how you generate keys, but what you intend to do with them. Generally, reusing key material for two different purposes is a bad idea. $\endgroup$ May 2 '18 at 21:40
  • $\begingroup$ Sorry, should have given more detail. The master key has value as a Tor v3 onion service name and is AES'd and stored on disk. But yes, could be anything in the general sense. I'm not using it for any kind of validation, just a common base. For the purposes of this question, please pretend it's a random 32 byte array. $\endgroup$
    – Chad Retz
    May 2 '18 at 22:48
  • $\begingroup$ I can't pretend it's a random 32-byte array if you're also using that for something else. When you use a key for two purposes, the combination of those two purposes can leak the key. You can't just sweep this under the rug unless you want to shoot yourself in the foot. $\endgroup$ May 2 '18 at 22:54
  • $\begingroup$ Ack. In my case, I don't have to have a master key and I can derive from any value. I suppose I started from a master key because it's what I had generated for the user. But it has no real relation to the child keys and I can change it to random 32 bytes that I generate for the user (or something else/larger). I am using the child keys, not the master for anything of note...not changing the code in this question atm, but point taken. $\endgroup$
    – Chad Retz
    May 2 '18 at 23:09
  • $\begingroup$ Deriving subkeys from a master key with a KDF is an eminently reasonable thing to do. $\endgroup$ May 2 '18 at 23:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.