mirror of
https://github.com/fluencelabs/js-libp2p-crypto
synced 2025-04-25 10:52:32 +00:00
* chore: add error codes * chore: create errors with new Error() * fix: better error testin * refactor: simplify random bytes error checks
122 lines
2.7 KiB
JavaScript
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
|
|
}
|