js-libp2p-crypto/src/keys/ecdh-browser.js
dirkmc 0b686d363c chore: add error codes (#155)
* chore: add error codes

* chore: create errors with new Error()

* fix: better error testin

* refactor: simplify random bytes error checks
2019-07-22 11:16:02 +01:00

122 lines
2.7 KiB
JavaScript

'use strict'
const errcode = require('err-code')
const webcrypto = require('../webcrypto.js')
const BN = require('asn1.js').bignum
const { toBase64, toBn } = require('../util')
const validateCurveType = require('./validate-curve-type')
const bits = {
'P-256': 256,
'P-384': 384,
'P-521': 521
}
exports.generateEphmeralKeyPair = async function (curve) {
validateCurveType(Object.keys(bits), curve)
const pair = await webcrypto.subtle.generateKey(
{
name: 'ECDH',
namedCurve: curve
},
true,
['deriveBits']
)
// forcePrivate is used for testing only
const genSharedKey = async (theirPub, forcePrivate) => {
let privateKey
if (forcePrivate) {
privateKey = await webcrypto.subtle.importKey(
'jwk',
unmarshalPrivateKey(curve, forcePrivate),
{
name: 'ECDH',
namedCurve: curve
},
false,
['deriveBits']
)
} else {
privateKey = pair.privateKey
}
const keys = [
await webcrypto.subtle.importKey(
'jwk',
unmarshalPublicKey(curve, theirPub),
{
name: 'ECDH',
namedCurve: curve
},
false,
[]
),
privateKey
]
return Buffer.from(await webcrypto.subtle.deriveBits(
{
name: 'ECDH',
namedCurve: curve,
public: keys[0]
},
keys[1],
bits[curve]
))
}
const publicKey = await webcrypto.subtle.exportKey('jwk', pair.publicKey)
return {
key: marshalPublicKey(publicKey),
genSharedKey
}
}
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.from([4]), // uncompressed point
toBn(jwk.x).toArrayLike(Buffer, 'be', byteLen),
toBn(jwk.y).toArrayLike(Buffer, '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.from([4]))) {
throw errcode(new Error('Cannot unmarshal public key - invalid key format'), 'ERR_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, byteLen),
y: toBase64(y, byteLen),
ext: true
}
}
function unmarshalPrivateKey (curve, key) {
const result = unmarshalPublicKey(curve, key.public)
result.d = toBase64(new BN(key.private))
return result
}