2016-09-13 13:23:11 +02:00
|
|
|
'use strict'
|
|
|
|
|
|
|
|
const crypto = require('./webcrypto')()
|
|
|
|
const nodeify = require('nodeify')
|
2016-10-03 23:15:21 +11:00
|
|
|
const BN = require('asn1.js').bignum
|
|
|
|
|
|
|
|
const util = require('./util')
|
|
|
|
const toBase64 = util.toBase64
|
|
|
|
const toBn = util.toBn
|
2016-09-13 13:23:11 +02:00
|
|
|
|
|
|
|
exports.generateEphmeralKeyPair = function (curve, callback) {
|
|
|
|
nodeify(crypto.subtle.generateKey(
|
|
|
|
{
|
|
|
|
name: 'ECDH',
|
|
|
|
namedCurve: curve
|
|
|
|
},
|
|
|
|
true,
|
|
|
|
['deriveBits']
|
|
|
|
).then((pair) => {
|
|
|
|
// forcePrivate is used for testing only
|
|
|
|
const genSharedKey = (theirPub, forcePrivate, cb) => {
|
|
|
|
if (typeof forcePrivate === 'function') {
|
|
|
|
cb = forcePrivate
|
|
|
|
forcePrivate = undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
const privateKey = forcePrivate || pair.privateKey
|
|
|
|
nodeify(crypto.subtle.importKey(
|
2016-10-03 23:15:21 +11:00
|
|
|
'jwk',
|
|
|
|
unmarshalPublicKey(curve, theirPub),
|
2016-09-13 13:23:11 +02:00
|
|
|
{
|
|
|
|
name: 'ECDH',
|
|
|
|
namedCurve: curve
|
|
|
|
},
|
|
|
|
false,
|
|
|
|
[]
|
|
|
|
).then((publicKey) => {
|
|
|
|
return crypto.subtle.deriveBits(
|
|
|
|
{
|
|
|
|
name: 'ECDH',
|
|
|
|
namedCurve: curve,
|
|
|
|
public: publicKey
|
|
|
|
},
|
|
|
|
privateKey,
|
|
|
|
256
|
|
|
|
)
|
|
|
|
}).then((bits) => {
|
|
|
|
// return p.derive(pub.getPublic()).toBuffer('be')
|
|
|
|
return Buffer.from(bits)
|
|
|
|
}), cb)
|
|
|
|
}
|
|
|
|
|
|
|
|
return crypto.subtle.exportKey(
|
2016-10-03 23:15:21 +11:00
|
|
|
'jwk',
|
2016-09-13 13:23:11 +02:00
|
|
|
pair.publicKey
|
|
|
|
).then((publicKey) => {
|
|
|
|
return {
|
2016-10-03 23:15:21 +11:00
|
|
|
key: marshalPublicKey(publicKey),
|
2016-09-13 13:23:11 +02:00
|
|
|
genSharedKey
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}), callback)
|
|
|
|
}
|
2016-10-03 23:15:21 +11:00
|
|
|
|
|
|
|
const curveLengths = {
|
|
|
|
'P-256': 32,
|
|
|
|
'P-384': 48,
|
|
|
|
'P-521': 66
|
|
|
|
}
|
|
|
|
|
|
|
|
// Marshal converts a jwk encodec ECDH public key into the
|
|
|
|
// form specified in section 4.3.6 of ANSI X9.62. (This is the format
|
|
|
|
// go-ipfs uses)
|
|
|
|
function marshalPublicKey (jwk) {
|
|
|
|
const byteLen = curveLengths[jwk.crv]
|
|
|
|
|
|
|
|
return Buffer.concat([
|
|
|
|
Buffer([4]), // uncompressed point
|
|
|
|
toBn(jwk.x).toBuffer('be', byteLen),
|
|
|
|
toBn(jwk.y).toBuffer('be', byteLen)
|
|
|
|
], 1 + byteLen * 2)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unmarshal converts a point, serialized by Marshal, into an jwk encoded key
|
|
|
|
function unmarshalPublicKey (curve, key) {
|
|
|
|
const byteLen = curveLengths[curve]
|
|
|
|
|
|
|
|
if (!key.slice(0, 1).equals(Buffer([4]))) {
|
|
|
|
throw new Error('Invalid key format')
|
|
|
|
}
|
|
|
|
const x = new BN(key.slice(1, byteLen + 1))
|
|
|
|
const y = new BN(key.slice(1 + byteLen))
|
|
|
|
|
|
|
|
return {
|
|
|
|
kty: 'EC',
|
|
|
|
crv: curve,
|
|
|
|
x: toBase64(x),
|
|
|
|
y: toBase64(y),
|
|
|
|
ext: true
|
|
|
|
}
|
|
|
|
}
|