mirror of
https://github.com/fluencelabs/js-libp2p-crypto
synced 2025-07-02 23:11:34 +00:00
feat: replace lib multihashing with multihashing-async
This commit is contained in:
committed by
Friedel Ziegelmayer
parent
cecadba14d
commit
b088bab80f
@ -1,7 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = new Buffer(`
|
||||
enum KeyType {
|
||||
module.exports = `enum KeyType {
|
||||
RSA = 0;
|
||||
}
|
||||
|
||||
@ -13,5 +12,4 @@ message PublicKey {
|
||||
message PrivateKey {
|
||||
required KeyType Type = 1;
|
||||
required bytes Data = 2;
|
||||
}
|
||||
`)
|
||||
}`
|
||||
|
@ -2,6 +2,11 @@
|
||||
|
||||
const crypto = require('./webcrypto')()
|
||||
const nodeify = require('nodeify')
|
||||
const BN = require('asn1.js').bignum
|
||||
|
||||
const util = require('./util')
|
||||
const toBase64 = util.toBase64
|
||||
const toBn = util.toBn
|
||||
|
||||
exports.generateEphmeralKeyPair = function (curve, callback) {
|
||||
nodeify(crypto.subtle.generateKey(
|
||||
@ -21,8 +26,8 @@ exports.generateEphmeralKeyPair = function (curve, callback) {
|
||||
|
||||
const privateKey = forcePrivate || pair.privateKey
|
||||
nodeify(crypto.subtle.importKey(
|
||||
'spki',
|
||||
theirPub,
|
||||
'jwk',
|
||||
unmarshalPublicKey(curve, theirPub),
|
||||
{
|
||||
name: 'ECDH',
|
||||
namedCurve: curve
|
||||
@ -46,13 +51,51 @@ exports.generateEphmeralKeyPair = function (curve, callback) {
|
||||
}
|
||||
|
||||
return crypto.subtle.exportKey(
|
||||
'spki',
|
||||
'jwk',
|
||||
pair.publicKey
|
||||
).then((publicKey) => {
|
||||
return {
|
||||
key: Buffer.from(publicKey),
|
||||
key: marshalPublicKey(publicKey),
|
||||
genSharedKey
|
||||
}
|
||||
})
|
||||
}), callback)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,13 @@
|
||||
'use strict'
|
||||
|
||||
const multihashing = require('multihashing')
|
||||
const nodeify = require('nodeify')
|
||||
const BN = require('bn.js')
|
||||
const asn1 = require('asn1.js')
|
||||
|
||||
const util = require('./util')
|
||||
const toBase64 = util.toBase64
|
||||
const toBn = util.toBn
|
||||
const crypto = require('./webcrypto')()
|
||||
|
||||
const sha2256 = multihashing.createHash('sha2-256')
|
||||
|
||||
exports.generateKey = function (bits, callback) {
|
||||
nodeify(crypto.subtle.generateKey(
|
||||
{
|
||||
@ -21,12 +20,10 @@ exports.generateKey = function (bits, callback) {
|
||||
['sign', 'verify']
|
||||
)
|
||||
.then(exportKey)
|
||||
.then((keys) => {
|
||||
return {
|
||||
privateKey: keys[0],
|
||||
publicKey: Buffer.from(keys[1])
|
||||
}
|
||||
}), callback)
|
||||
.then((keys) => ({
|
||||
privateKey: keys[0],
|
||||
publicKey: keys[1]
|
||||
})), callback)
|
||||
}
|
||||
|
||||
// Takes a jwk key
|
||||
@ -44,18 +41,14 @@ exports.unmarshalPrivateKey = function (key, callback) {
|
||||
|
||||
nodeify(Promise.all([
|
||||
privateKey,
|
||||
derivePublicFromPrivate(privateKey)
|
||||
]).then((keys) => {
|
||||
return exportKey({
|
||||
privateKey: keys[0],
|
||||
publicKey: keys[1]
|
||||
})
|
||||
}).then((keys) => {
|
||||
return {
|
||||
privateKey: keys[0],
|
||||
publicKey: Buffer.from(keys[1])
|
||||
}
|
||||
}), callback)
|
||||
derivePublicFromPrivate(key)
|
||||
]).then((keys) => exportKey({
|
||||
privateKey: keys[0],
|
||||
publicKey: keys[1]
|
||||
})).then((keys) => ({
|
||||
privateKey: keys[0],
|
||||
publicKey: keys[1]
|
||||
})), callback)
|
||||
}
|
||||
|
||||
exports.getRandomValues = function (arr) {
|
||||
@ -63,67 +56,53 @@ exports.getRandomValues = function (arr) {
|
||||
}
|
||||
|
||||
exports.hashAndSign = function (key, msg, callback) {
|
||||
sha2256(msg, (err, digest) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
nodeify(crypto.subtle.importKey(
|
||||
'jwk',
|
||||
key,
|
||||
{
|
||||
name: 'RSASSA-PKCS1-v1_5',
|
||||
hash: {name: 'SHA-256'}
|
||||
},
|
||||
false,
|
||||
['sign']
|
||||
).then((privateKey) => {
|
||||
return crypto.subtle.sign(
|
||||
{name: 'RSASSA-PKCS1-v1_5'},
|
||||
privateKey,
|
||||
Uint8Array.from(digest)
|
||||
)
|
||||
}).then((sig) => Buffer.from(sig)), callback)
|
||||
})
|
||||
nodeify(crypto.subtle.importKey(
|
||||
'jwk',
|
||||
key,
|
||||
{
|
||||
name: 'RSASSA-PKCS1-v1_5',
|
||||
hash: {name: 'SHA-256'}
|
||||
},
|
||||
false,
|
||||
['sign']
|
||||
).then((privateKey) => {
|
||||
return crypto.subtle.sign(
|
||||
{name: 'RSASSA-PKCS1-v1_5'},
|
||||
privateKey,
|
||||
Uint8Array.from(msg)
|
||||
)
|
||||
}).then((sig) => Buffer.from(sig)), callback)
|
||||
}
|
||||
|
||||
exports.hashAndVerify = function (key, sig, msg, callback) {
|
||||
sha2256(msg, (err, digest) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
nodeify(crypto.subtle.importKey(
|
||||
'spki',
|
||||
Uint8Array.from(key),
|
||||
{
|
||||
name: 'RSASSA-PKCS1-v1_5',
|
||||
hash: {name: 'SHA-256'}
|
||||
},
|
||||
false,
|
||||
['verify']
|
||||
).then((publicKey) => {
|
||||
return crypto.subtle.verify(
|
||||
{name: 'RSASSA-PKCS1-v1_5'},
|
||||
publicKey,
|
||||
Uint8Array.from(sig),
|
||||
Uint8Array.from(digest)
|
||||
)
|
||||
}), callback)
|
||||
})
|
||||
nodeify(crypto.subtle.importKey(
|
||||
'jwk',
|
||||
key,
|
||||
{
|
||||
name: 'RSASSA-PKCS1-v1_5',
|
||||
hash: {name: 'SHA-256'}
|
||||
},
|
||||
false,
|
||||
['verify']
|
||||
).then((publicKey) => {
|
||||
return crypto.subtle.verify(
|
||||
{name: 'RSASSA-PKCS1-v1_5'},
|
||||
publicKey,
|
||||
sig,
|
||||
msg
|
||||
)
|
||||
}), callback)
|
||||
}
|
||||
|
||||
function exportKey (pair) {
|
||||
return Promise.all([
|
||||
crypto.subtle.exportKey('jwk', pair.privateKey),
|
||||
crypto.subtle.exportKey('spki', pair.publicKey)
|
||||
crypto.subtle.exportKey('jwk', pair.publicKey)
|
||||
])
|
||||
}
|
||||
|
||||
function derivePublicFromPrivate (privatePromise) {
|
||||
return privatePromise.then((privateKey) => {
|
||||
return crypto.subtle.exportKey('jwk', privateKey)
|
||||
}).then((jwKey) => crypto.subtle.importKey(
|
||||
function derivePublicFromPrivate (jwKey) {
|
||||
return crypto.subtle.importKey(
|
||||
'jwk',
|
||||
{
|
||||
kty: jwKey.kty,
|
||||
@ -138,7 +117,7 @@ function derivePublicFromPrivate (privatePromise) {
|
||||
},
|
||||
true,
|
||||
['verify']
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
const RSAPrivateKey = asn1.define('RSAPrivateKey', function () {
|
||||
@ -155,6 +134,35 @@ const RSAPrivateKey = asn1.define('RSAPrivateKey', function () {
|
||||
)
|
||||
})
|
||||
|
||||
const AlgorithmIdentifier = asn1.define('AlgorithmIdentifier', function () {
|
||||
this.seq().obj(
|
||||
this.key('algorithm').objid({
|
||||
'1.2.840.113549.1.1.1': 'rsa'
|
||||
}),
|
||||
this.key('none').optional().null_(),
|
||||
this.key('curve').optional().objid(),
|
||||
this.key('params').optional().seq().obj(
|
||||
this.key('p').int(),
|
||||
this.key('q').int(),
|
||||
this.key('g').int()
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
const PublicKey = asn1.define('RSAPublicKey', function () {
|
||||
this.seq().obj(
|
||||
this.key('algorithm').use(AlgorithmIdentifier),
|
||||
this.key('subjectPublicKey').bitstr()
|
||||
)
|
||||
})
|
||||
|
||||
const RSAPublicKey = asn1.define('RSAPublicKey', function () {
|
||||
this.seq().obj(
|
||||
this.key('modulus').int(),
|
||||
this.key('publicExponent').int()
|
||||
)
|
||||
})
|
||||
|
||||
// Convert a PKCS#1 in ASN1 DER format to a JWK key
|
||||
exports.pkcs1ToJwk = function (bytes) {
|
||||
const asn1 = RSAPrivateKey.decode(bytes, 'der')
|
||||
@ -174,6 +182,7 @@ exports.pkcs1ToJwk = function (bytes) {
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a JWK key into PKCS#1 in ASN1 DER format
|
||||
exports.jwkToPkcs1 = function (jwk) {
|
||||
return RSAPrivateKey.encode({
|
||||
version: 0,
|
||||
@ -188,18 +197,32 @@ exports.jwkToPkcs1 = function (jwk) {
|
||||
}, 'der')
|
||||
}
|
||||
|
||||
// Convert a BN.js instance to a base64 encoded string without padding
|
||||
// Adapted from https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#appendix-C
|
||||
function toBase64 (bn) {
|
||||
let s = bn.toBuffer('be').toString('base64')
|
||||
// Convert a PKCIX in ASN1 DER format to a JWK key
|
||||
exports.pkixToJwk = function (bytes) {
|
||||
const ndata = PublicKey.decode(bytes, 'der')
|
||||
const asn1 = RSAPublicKey.decode(ndata.subjectPublicKey.data, 'der')
|
||||
|
||||
return s
|
||||
.replace(/(=*)$/, '') // Remove any trailing '='s
|
||||
.replace(/\+/g, '-') // 62nd char of encoding
|
||||
.replace(/\//g, '_') // 63rd char of encoding
|
||||
return {
|
||||
kty: 'RSA',
|
||||
n: toBase64(asn1.modulus),
|
||||
e: toBase64(asn1.publicExponent),
|
||||
alg: 'RS256',
|
||||
kid: '2011-04-29'
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a base64 encoded string to a BN.js instance
|
||||
function toBn (str) {
|
||||
return new BN(Buffer.from(str, 'base64'))
|
||||
// Convert a JWK key to PKCIX in ASN1 DER format
|
||||
exports.jwkToPkix = function (jwk) {
|
||||
return PublicKey.encode({
|
||||
algorithm: {
|
||||
algorithm: 'rsa',
|
||||
none: null
|
||||
},
|
||||
subjectPublicKey: {
|
||||
data: RSAPublicKey.encode({
|
||||
modulus: toBn(jwk.n),
|
||||
publicExponent: toBn(jwk.e)
|
||||
}, 'der')
|
||||
}
|
||||
}, 'der')
|
||||
}
|
||||
|
19
src/crypto/util.js
Normal file
19
src/crypto/util.js
Normal file
@ -0,0 +1,19 @@
|
||||
'use strict'
|
||||
|
||||
const BN = require('asn1.js').bignum
|
||||
|
||||
// Convert a BN.js instance to a base64 encoded string without padding
|
||||
// Adapted from https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#appendix-C
|
||||
exports.toBase64 = function toBase64 (bn) {
|
||||
let s = bn.toBuffer('be').toString('base64')
|
||||
|
||||
return s
|
||||
.replace(/(=*)$/, '') // Remove any trailing '='s
|
||||
.replace(/\+/g, '-') // 62nd char of encoding
|
||||
.replace(/\//g, '_') // 63rd char of encoding
|
||||
}
|
||||
|
||||
// Convert a base64 encoded string to a BN.js instance
|
||||
exports.toBn = function toBn (str) {
|
||||
return new BN(Buffer.from(str, 'base64'))
|
||||
}
|
@ -2,7 +2,8 @@
|
||||
|
||||
module.exports = function getWebCrypto () {
|
||||
if (typeof window !== 'undefined') {
|
||||
require('webcrypto-shim')
|
||||
// This is only a shim for interfaces, not for functionality
|
||||
require('webcrypto-shim')(window)
|
||||
|
||||
if (window.crypto) {
|
||||
return window.crypto
|
||||
|
@ -1,6 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const multihashing = require('multihashing')
|
||||
const multihashing = require('multihashing-async')
|
||||
const protobuf = require('protocol-buffers')
|
||||
|
||||
const crypto = require('../crypto').rsa
|
||||
@ -17,7 +17,7 @@ class RsaPublicKey {
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return this._key
|
||||
return crypto.jwkToPkix(this._key)
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
@ -103,7 +103,9 @@ function unmarshalRsaPrivateKey (bytes, callback) {
|
||||
}
|
||||
|
||||
function unmarshalRsaPublicKey (bytes) {
|
||||
return new RsaPublicKey(bytes)
|
||||
const jwk = crypto.pkixToJwk(bytes)
|
||||
|
||||
return new RsaPublicKey(jwk)
|
||||
}
|
||||
|
||||
function generateKeyPair (bits, cb) {
|
||||
|
Reference in New Issue
Block a user