feat(keys): implement generateKeyPairFromSeed for ed25519

Implement generateKeyPairFromSeed for ed25519 - this will produce the same keypair for the same seed (or first 32 bytes of reader) as given to GenerateKeyPairWithReader in go-libp2p.
This commit is contained in:
Jack Kleeman 2017-03-03 20:38:51 +00:00 committed by Friedel Ziegelmayer
parent 20326199d9
commit e5b7c1f622
4 changed files with 116 additions and 1 deletions

View File

@ -22,6 +22,22 @@ exports.generateKey = function (callback) {
done(null, keys) done(null, keys)
} }
// seed should be a 32 byte uint8array
exports.generateKeyFromSeed = function (seed, callback) {
const done = (err, res) => setImmediate(() => {
callback(err, res)
})
let keys
try {
keys = nacl.sign.keyPair.fromSeed(seed)
} catch (err) {
done(err)
return
}
done(null, keys)
}
exports.hashAndSign = function (key, msg, callback) { exports.hashAndSign = function (key, msg, callback) {
setImmediate(() => { setImmediate(() => {
callback(null, Buffer.from(nacl.sign.detached(msg, key))) callback(null, Buffer.from(nacl.sign.detached(msg, key)))

View File

@ -30,6 +30,19 @@ exports.generateKeyPair = (type, bits, cb) => {
key.generateKeyPair(bits, cb) key.generateKeyPair(bits, cb)
} }
// Generates a keypair of the given type and bitsize
// seed is a 32 byte uint8array
exports.generateKeyPairFromSeed = (type, seed, bits, cb) => {
let key = keys[type.toLowerCase()]
if (!key) {
return cb(new Error('invalid or unsupported key type'))
}
if (type.toLowerCase() !== 'ed25519') {
return cb(new Error('Seed key derivation is unimplemented for RSA or secp256k1'))
}
key.generateKeyPairFromSeed(seed, bits, cb)
}
// Converts a protobuf serialized public key into its // Converts a protobuf serialized public key into its
// representative object // representative object
exports.unmarshalPublicKey = (buf) => { exports.unmarshalPublicKey = (buf) => {

View File

@ -117,6 +117,27 @@ function generateKeyPair (_bits, cb) {
}) })
} }
function generateKeyPairFromSeed (seed, _bits, cb) {
if (cb === undefined && typeof _bits === 'function') {
cb = _bits
}
crypto.generateKeyFromSeed(seed, (err, keys) => {
if (err) {
return cb(err)
}
let privkey
try {
privkey = new Ed25519PrivateKey(keys.secretKey, keys.publicKey)
} catch (err) {
cb(err)
return
}
cb(null, privkey)
})
}
function ensure (cb) { function ensure (cb) {
if (typeof cb !== 'function') { if (typeof cb !== 'function') {
throw new Error('callback is required') throw new Error('callback is required')
@ -138,5 +159,6 @@ module.exports = {
Ed25519PrivateKey, Ed25519PrivateKey,
unmarshalEd25519PrivateKey, unmarshalEd25519PrivateKey,
unmarshalEd25519PublicKey, unmarshalEd25519PublicKey,
generateKeyPair generateKeyPair,
generateKeyPairFromSeed
} }

View File

@ -35,6 +35,70 @@ describe('ed25519', () => {
}) })
}) })
it('generates a valid key from seed', (done) => {
var seed = crypto.randomBytes(32)
crypto.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey) => {
if (err) return done(err)
expect(
seededkey
).to.be.an.instanceof(
ed25519.Ed25519PrivateKey
)
seededkey.hash((err, digest) => {
if (err) {
return done(err)
}
expect(digest).to.have.length(34)
done()
})
})
})
it('generates the same key from the same seed', (done) => {
var seed = crypto.randomBytes(32)
crypto.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey1) => {
if (err) return done(err)
crypto.generateKeyPairFromSeed('Ed25519', seed, 512, (err, seededkey2) => {
if (err) return done(err)
expect(
seededkey1.equals(seededkey2)
).to.be.eql(
true
)
expect(
seededkey1.public.equals(seededkey2.public)
).to.be.eql(
true
)
done()
})
})
})
it('generates different keys for different seeds', (done) => {
var seed1 = crypto.randomBytes(32)
crypto.generateKeyPairFromSeed('Ed25519', seed1, 512, (err, seededkey1) => {
if (err) return done(err)
var seed2 = crypto.randomBytes(32)
crypto.generateKeyPairFromSeed('Ed25519', seed2, 512, (err, seededkey2) => {
if (err) return done(err)
expect(
seededkey1.equals(seededkey2)
).to.be.eql(
false
)
expect(
seededkey1.public.equals(seededkey2.public)
).to.be.eql(
false
)
done()
})
})
})
it('signs', (done) => { it('signs', (done) => {
const text = crypto.randomBytes(512) const text = crypto.randomBytes(512)