mirror of
https://github.com/fluencelabs/js-libp2p-crypto
synced 2025-06-25 10:51:42 +00:00
feat: refactor to use async/await (#131)
BREAKING CHANGE: API refactored to use async/await feat: WIP use async await fix: passing tests chore: update travis node.js versions fix: skip ursa optional tests on windows fix: benchmarks docs: update docs fix: remove broken and intested private key decrypt chore: update deps
This commit is contained in:
@ -1,12 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
const webcrypto = require('../webcrypto')
|
||||
const nodeify = require('../nodeify')
|
||||
const webcrypto = require('../webcrypto.js')
|
||||
const BN = require('asn1.js').bignum
|
||||
|
||||
const util = require('../util')
|
||||
const toBase64 = util.toBase64
|
||||
const toBn = util.toBn
|
||||
const { toBase64, toBn } = require('../util')
|
||||
|
||||
const bits = {
|
||||
'P-256': 256,
|
||||
@ -14,72 +10,66 @@ const bits = {
|
||||
'P-521': 521
|
||||
}
|
||||
|
||||
exports.generateEphmeralKeyPair = function (curve, callback) {
|
||||
nodeify(webcrypto.subtle.generateKey(
|
||||
exports.generateEphmeralKeyPair = async function (curve) {
|
||||
const pair = await webcrypto.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
|
||||
}
|
||||
)
|
||||
|
||||
let privateKey
|
||||
// forcePrivate is used for testing only
|
||||
const genSharedKey = async (theirPub, forcePrivate) => {
|
||||
let privateKey
|
||||
|
||||
if (forcePrivate) {
|
||||
privateKey = webcrypto.subtle.importKey(
|
||||
'jwk',
|
||||
unmarshalPrivateKey(curve, forcePrivate),
|
||||
{
|
||||
name: 'ECDH',
|
||||
namedCurve: curve
|
||||
},
|
||||
false,
|
||||
['deriveBits']
|
||||
)
|
||||
} else {
|
||||
privateKey = Promise.resolve(pair.privateKey)
|
||||
}
|
||||
|
||||
const keys = Promise.all([
|
||||
webcrypto.subtle.importKey(
|
||||
'jwk',
|
||||
unmarshalPublicKey(curve, theirPub),
|
||||
{
|
||||
name: 'ECDH',
|
||||
namedCurve: curve
|
||||
},
|
||||
false,
|
||||
[]
|
||||
),
|
||||
privateKey
|
||||
])
|
||||
|
||||
nodeify(keys.then((keys) => webcrypto.subtle.deriveBits(
|
||||
if (forcePrivate) {
|
||||
privateKey = await webcrypto.subtle.importKey(
|
||||
'jwk',
|
||||
unmarshalPrivateKey(curve, forcePrivate),
|
||||
{
|
||||
name: 'ECDH',
|
||||
namedCurve: curve,
|
||||
public: keys[0]
|
||||
namedCurve: curve
|
||||
},
|
||||
keys[1],
|
||||
bits[curve]
|
||||
)).then((bits) => Buffer.from(bits)), cb)
|
||||
false,
|
||||
['deriveBits']
|
||||
)
|
||||
} else {
|
||||
privateKey = pair.privateKey
|
||||
}
|
||||
|
||||
return webcrypto.subtle.exportKey('jwk', pair.publicKey)
|
||||
.then((publicKey) => {
|
||||
return {
|
||||
key: marshalPublicKey(publicKey),
|
||||
genSharedKey
|
||||
}
|
||||
})
|
||||
}), callback)
|
||||
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 = {
|
||||
|
@ -1,7 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const crypto = require('crypto')
|
||||
const nextTick = require('async/nextTick')
|
||||
|
||||
const curves = {
|
||||
'P-256': 'prime256v1',
|
||||
@ -9,33 +8,21 @@ const curves = {
|
||||
'P-521': 'secp521r1'
|
||||
}
|
||||
|
||||
exports.generateEphmeralKeyPair = function (curve, callback) {
|
||||
exports.generateEphmeralKeyPair = async function (curve) { // eslint-disable-line require-await
|
||||
if (!curves[curve]) {
|
||||
return callback(new Error(`Unkown curve: ${curve}`))
|
||||
throw new Error(`Unkown curve: ${curve}`)
|
||||
}
|
||||
const ecdh = crypto.createECDH(curves[curve])
|
||||
ecdh.generateKeys()
|
||||
|
||||
nextTick(() => callback(null, {
|
||||
return {
|
||||
key: ecdh.getPublicKey(),
|
||||
genSharedKey (theirPub, forcePrivate, cb) {
|
||||
if (typeof forcePrivate === 'function') {
|
||||
cb = forcePrivate
|
||||
forcePrivate = null
|
||||
}
|
||||
|
||||
async genSharedKey (theirPub, forcePrivate) { // eslint-disable-line require-await
|
||||
if (forcePrivate) {
|
||||
ecdh.setPrivateKey(forcePrivate.private)
|
||||
}
|
||||
|
||||
let secret
|
||||
try {
|
||||
secret = ecdh.computeSecret(theirPub)
|
||||
} catch (err) {
|
||||
return cb(err)
|
||||
}
|
||||
|
||||
nextTick(() => cb(null, secret))
|
||||
return ecdh.computeSecret(theirPub)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -12,9 +12,8 @@ class Ed25519PublicKey {
|
||||
this._key = ensureKey(key, crypto.publicKeyLength)
|
||||
}
|
||||
|
||||
verify (data, sig, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndVerify(this._key, sig, data, callback)
|
||||
async verify (data, sig) { // eslint-disable-line require-await
|
||||
return crypto.hashAndVerify(this._key, sig, data)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
@ -32,9 +31,8 @@ class Ed25519PublicKey {
|
||||
return this.bytes.equals(key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
async hash () { // eslint-disable-line require-await
|
||||
return multihashing(this.bytes, 'sha2-256')
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,9 +44,8 @@ class Ed25519PrivateKey {
|
||||
this._publicKey = ensureKey(publicKey, crypto.publicKeyLength)
|
||||
}
|
||||
|
||||
sign (message, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndSign(this._key, message, callback)
|
||||
async sign (message) { // eslint-disable-line require-await
|
||||
return crypto.hashAndSign(this._key, message)
|
||||
}
|
||||
|
||||
get public () {
|
||||
@ -74,9 +71,8 @@ class Ed25519PrivateKey {
|
||||
return this.bytes.equals(key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
async hash () { // eslint-disable-line require-await
|
||||
return multihashing(this.bytes, 'sha2-256')
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,28 +82,19 @@ class Ed25519PrivateKey {
|
||||
* The public key is a protobuf encoding containing a type and the DER encoding
|
||||
* of the PKCS SubjectPublicKeyInfo.
|
||||
*
|
||||
* @param {function(Error, id)} callback
|
||||
* @returns {undefined}
|
||||
* @returns {Promise<String>}
|
||||
*/
|
||||
id (callback) {
|
||||
this.public.hash((err, hash) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
callback(null, bs58.encode(hash))
|
||||
})
|
||||
async id () {
|
||||
const hash = await this.public.hash()
|
||||
return bs58.encode(hash)
|
||||
}
|
||||
}
|
||||
|
||||
function unmarshalEd25519PrivateKey (bytes, callback) {
|
||||
try {
|
||||
bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength)
|
||||
} catch (err) {
|
||||
return callback(err)
|
||||
}
|
||||
function unmarshalEd25519PrivateKey (bytes) {
|
||||
bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength)
|
||||
const privateKeyBytes = bytes.slice(0, crypto.privateKeyLength)
|
||||
const publicKeyBytes = bytes.slice(crypto.privateKeyLength, bytes.length)
|
||||
callback(null, new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes))
|
||||
return new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes)
|
||||
}
|
||||
|
||||
function unmarshalEd25519PublicKey (bytes) {
|
||||
@ -115,52 +102,14 @@ function unmarshalEd25519PublicKey (bytes) {
|
||||
return new Ed25519PublicKey(bytes)
|
||||
}
|
||||
|
||||
function generateKeyPair (_bits, cb) {
|
||||
if (cb === undefined && typeof _bits === 'function') {
|
||||
cb = _bits
|
||||
}
|
||||
|
||||
crypto.generateKey((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)
|
||||
})
|
||||
async function generateKeyPair () {
|
||||
const { secretKey, publicKey } = await crypto.generateKey()
|
||||
return new Ed25519PrivateKey(secretKey, publicKey)
|
||||
}
|
||||
|
||||
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) {
|
||||
if (typeof cb !== 'function') {
|
||||
throw new Error('callback is required')
|
||||
}
|
||||
async function generateKeyPairFromSeed (seed) {
|
||||
const { secretKey, publicKey } = await crypto.generateKeyFromSeed(seed)
|
||||
return new Ed25519PrivateKey(secretKey, publicKey)
|
||||
}
|
||||
|
||||
function ensureKey (key, length) {
|
||||
|
@ -1,51 +1,23 @@
|
||||
'use strict'
|
||||
|
||||
const nacl = require('tweetnacl')
|
||||
const nextTick = require('async/nextTick')
|
||||
|
||||
exports.publicKeyLength = nacl.sign.publicKeyLength
|
||||
exports.privateKeyLength = nacl.sign.secretKeyLength
|
||||
|
||||
exports.generateKey = function (callback) {
|
||||
nextTick(() => {
|
||||
let result
|
||||
try {
|
||||
result = nacl.sign.keyPair()
|
||||
} catch (err) {
|
||||
return callback(err)
|
||||
}
|
||||
callback(null, result)
|
||||
})
|
||||
exports.generateKey = async function () { // eslint-disable-line require-await
|
||||
return nacl.sign.keyPair()
|
||||
}
|
||||
|
||||
// seed should be a 32 byte uint8array
|
||||
exports.generateKeyFromSeed = function (seed, callback) {
|
||||
nextTick(() => {
|
||||
let result
|
||||
try {
|
||||
result = nacl.sign.keyPair.fromSeed(seed)
|
||||
} catch (err) {
|
||||
return callback(err)
|
||||
}
|
||||
callback(null, result)
|
||||
})
|
||||
exports.generateKeyFromSeed = async function (seed) { // eslint-disable-line require-await
|
||||
return nacl.sign.keyPair.fromSeed(seed)
|
||||
}
|
||||
|
||||
exports.hashAndSign = function (key, msg, callback) {
|
||||
nextTick(() => {
|
||||
callback(null, Buffer.from(nacl.sign.detached(msg, key)))
|
||||
})
|
||||
exports.hashAndSign = async function (key, msg) { // eslint-disable-line require-await
|
||||
return Buffer.from(nacl.sign.detached(msg, key))
|
||||
}
|
||||
|
||||
exports.hashAndVerify = function (key, sig, msg, callback) {
|
||||
nextTick(() => {
|
||||
let result
|
||||
try {
|
||||
result = nacl.sign.detached.verify(msg, sig, key)
|
||||
} catch (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
callback(null, result)
|
||||
})
|
||||
exports.hashAndVerify = async function (key, sig, msg) { // eslint-disable-line require-await
|
||||
return nacl.sign.detached.verify(msg, sig, key)
|
||||
}
|
||||
|
@ -6,6 +6,4 @@ const ecdh = require('./ecdh')
|
||||
// the shared secret key.
|
||||
//
|
||||
// Focuses only on ECDH now, but can be made more general in the future.
|
||||
module.exports = (curve, callback) => {
|
||||
ecdh.generateEphmeralKeyPair(curve, callback)
|
||||
}
|
||||
module.exports = async (curve) => ecdh.generateEphmeralKeyPair(curve) // eslint-disable-line require-await
|
||||
|
@ -27,27 +27,27 @@ exports.keyStretcher = require('./key-stretcher')
|
||||
exports.generateEphemeralKeyPair = require('./ephemeral-keys')
|
||||
|
||||
// Generates a keypair of the given type and bitsize
|
||||
exports.generateKeyPair = (type, bits, cb) => {
|
||||
exports.generateKeyPair = async (type, bits) => { // eslint-disable-line require-await
|
||||
let key = supportedKeys[type.toLowerCase()]
|
||||
|
||||
if (!key) {
|
||||
return cb(new Error('invalid or unsupported key type'))
|
||||
throw new Error('invalid or unsupported key type')
|
||||
}
|
||||
|
||||
key.generateKeyPair(bits, cb)
|
||||
return key.generateKeyPair(bits)
|
||||
}
|
||||
|
||||
// Generates a keypair of the given type and bitsize
|
||||
// seed is a 32 byte uint8array
|
||||
exports.generateKeyPairFromSeed = (type, seed, bits, cb) => {
|
||||
exports.generateKeyPairFromSeed = async (type, seed, bits) => { // eslint-disable-line require-await
|
||||
let key = supportedKeys[type.toLowerCase()]
|
||||
if (!key) {
|
||||
return cb(new Error('invalid or unsupported key type'))
|
||||
throw new Error('invalid or unsupported key type')
|
||||
}
|
||||
if (type.toLowerCase() !== 'ed25519') {
|
||||
return cb(new Error('Seed key derivation is unimplemented for RSA or secp256k1'))
|
||||
throw new Error('Seed key derivation is unimplemented for RSA or secp256k1')
|
||||
}
|
||||
key.generateKeyPairFromSeed(seed, bits, cb)
|
||||
return key.generateKeyPairFromSeed(seed, bits)
|
||||
}
|
||||
|
||||
// Converts a protobuf serialized public key into its
|
||||
@ -84,29 +84,23 @@ exports.marshalPublicKey = (key, type) => {
|
||||
|
||||
// Converts a protobuf serialized private key into its
|
||||
// representative object
|
||||
exports.unmarshalPrivateKey = (buf, callback) => {
|
||||
let decoded
|
||||
try {
|
||||
decoded = keysPBM.PrivateKey.decode(buf)
|
||||
} catch (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
exports.unmarshalPrivateKey = async (buf) => { // eslint-disable-line require-await
|
||||
const decoded = keysPBM.PrivateKey.decode(buf)
|
||||
const data = decoded.Data
|
||||
|
||||
switch (decoded.Type) {
|
||||
case keysPBM.KeyType.RSA:
|
||||
return supportedKeys.rsa.unmarshalRsaPrivateKey(data, callback)
|
||||
return supportedKeys.rsa.unmarshalRsaPrivateKey(data)
|
||||
case keysPBM.KeyType.Ed25519:
|
||||
return supportedKeys.ed25519.unmarshalEd25519PrivateKey(data, callback)
|
||||
return supportedKeys.ed25519.unmarshalEd25519PrivateKey(data)
|
||||
case keysPBM.KeyType.Secp256k1:
|
||||
if (supportedKeys.secp256k1) {
|
||||
return supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey(data, callback)
|
||||
return supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey(data)
|
||||
} else {
|
||||
return callback(new Error('secp256k1 support requires libp2p-crypto-secp256k1 package'))
|
||||
throw new Error('secp256k1 support requires libp2p-crypto-secp256k1 package')
|
||||
}
|
||||
default:
|
||||
callback(new Error('invalid or unsupported key type'))
|
||||
throw new Error('invalid or unsupported key type')
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,16 +114,12 @@ exports.marshalPrivateKey = (key, type) => {
|
||||
return key.bytes
|
||||
}
|
||||
|
||||
exports.import = (pem, password, callback) => {
|
||||
try {
|
||||
const key = forge.pki.decryptRsaPrivateKey(pem, password)
|
||||
if (key === null) {
|
||||
throw new Error('Cannot read the key, most likely the password is wrong or not a RSA key')
|
||||
}
|
||||
let der = forge.asn1.toDer(forge.pki.privateKeyToAsn1(key))
|
||||
der = Buffer.from(der.getBytes(), 'binary')
|
||||
return supportedKeys.rsa.unmarshalRsaPrivateKey(der, callback)
|
||||
} catch (err) {
|
||||
callback(err)
|
||||
exports.import = async (pem, password) => { // eslint-disable-line require-await
|
||||
const key = forge.pki.decryptRsaPrivateKey(pem, password)
|
||||
if (key === null) {
|
||||
throw new Error('Cannot read the key, most likely the password is wrong or not a RSA key')
|
||||
}
|
||||
let der = forge.asn1.toDer(forge.pki.privateKeyToAsn1(key))
|
||||
der = Buffer.from(der.getBytes(), 'binary')
|
||||
return supportedKeys.rsa.unmarshalRsaPrivateKey(der)
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const whilst = require('async/whilst')
|
||||
const hmac = require('../hmac')
|
||||
|
||||
const cipherMap = {
|
||||
@ -20,15 +19,15 @@ const cipherMap = {
|
||||
|
||||
// Generates a set of keys for each party by stretching the shared key.
|
||||
// (myIV, theirIV, myCipherKey, theirCipherKey, myMACKey, theirMACKey)
|
||||
module.exports = (cipherType, hash, secret, callback) => {
|
||||
module.exports = async (cipherType, hash, secret) => {
|
||||
const cipher = cipherMap[cipherType]
|
||||
|
||||
if (!cipher) {
|
||||
return callback(new Error('unkown cipherType passed'))
|
||||
throw new Error('unkown cipherType passed')
|
||||
}
|
||||
|
||||
if (!hash) {
|
||||
return callback(new Error('unkown hashType passed'))
|
||||
throw new Error('unkown hashType passed')
|
||||
}
|
||||
|
||||
const cipherKeySize = cipher.keySize
|
||||
@ -37,72 +36,38 @@ module.exports = (cipherType, hash, secret, callback) => {
|
||||
const seed = Buffer.from('key expansion')
|
||||
const resultLength = 2 * (ivSize + cipherKeySize + hmacKeySize)
|
||||
|
||||
hmac.create(hash, secret, (err, m) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
const m = await hmac.create(hash, secret)
|
||||
let a = await m.digest(seed)
|
||||
|
||||
let result = []
|
||||
let j = 0
|
||||
|
||||
while (j < resultLength) {
|
||||
const b = await m.digest(Buffer.concat([a, seed]))
|
||||
let todo = b.length
|
||||
|
||||
if (j + todo > resultLength) {
|
||||
todo = resultLength - j
|
||||
}
|
||||
|
||||
m.digest(seed, (err, a) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
result.push(b)
|
||||
j += todo
|
||||
a = await m.digest(a)
|
||||
}
|
||||
|
||||
let result = []
|
||||
let j = 0
|
||||
const half = resultLength / 2
|
||||
const resultBuffer = Buffer.concat(result)
|
||||
const r1 = resultBuffer.slice(0, half)
|
||||
const r2 = resultBuffer.slice(half, resultLength)
|
||||
|
||||
whilst(
|
||||
() => j < resultLength,
|
||||
stretch,
|
||||
finish
|
||||
)
|
||||
|
||||
function stretch (cb) {
|
||||
m.digest(Buffer.concat([a, seed]), (err, b) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
|
||||
let todo = b.length
|
||||
|
||||
if (j + todo > resultLength) {
|
||||
todo = resultLength - j
|
||||
}
|
||||
|
||||
result.push(b)
|
||||
|
||||
j += todo
|
||||
|
||||
m.digest(a, (err, _a) => {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
a = _a
|
||||
cb()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function finish (err) {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
const half = resultLength / 2
|
||||
const resultBuffer = Buffer.concat(result)
|
||||
const r1 = resultBuffer.slice(0, half)
|
||||
const r2 = resultBuffer.slice(half, resultLength)
|
||||
|
||||
const createKey = (res) => ({
|
||||
iv: res.slice(0, ivSize),
|
||||
cipherKey: res.slice(ivSize, ivSize + cipherKeySize),
|
||||
macKey: res.slice(ivSize + cipherKeySize)
|
||||
})
|
||||
|
||||
callback(null, {
|
||||
k1: createKey(r1),
|
||||
k2: createKey(r2)
|
||||
})
|
||||
}
|
||||
})
|
||||
const createKey = (res) => ({
|
||||
iv: res.slice(0, ivSize),
|
||||
cipherKey: res.slice(ivSize, ivSize + cipherKeySize),
|
||||
macKey: res.slice(ivSize + cipherKeySize)
|
||||
})
|
||||
|
||||
return {
|
||||
k1: createKey(r1),
|
||||
k2: createKey(r2)
|
||||
}
|
||||
}
|
||||
|
@ -12,4 +12,4 @@ message PublicKey {
|
||||
message PrivateKey {
|
||||
required KeyType Type = 1;
|
||||
required bytes Data = 2;
|
||||
}`
|
||||
}`
|
||||
|
@ -1,13 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
const nodeify = require('../nodeify')
|
||||
const webcrypto = require('../webcrypto')
|
||||
const webcrypto = require('../webcrypto.js')
|
||||
const randomBytes = require('../random-bytes')
|
||||
|
||||
exports.utils = require('./rsa-utils')
|
||||
|
||||
exports.generateKey = function (bits, callback) {
|
||||
nodeify(webcrypto.subtle.generateKey(
|
||||
exports.generateKey = async function (bits) {
|
||||
const pair = await webcrypto.subtle.generateKey(
|
||||
{
|
||||
name: 'RSASSA-PKCS1-v1_5',
|
||||
modulusLength: bits,
|
||||
@ -17,16 +16,18 @@ exports.generateKey = function (bits, callback) {
|
||||
true,
|
||||
['sign', 'verify']
|
||||
)
|
||||
.then(exportKey)
|
||||
.then((keys) => ({
|
||||
privateKey: keys[0],
|
||||
publicKey: keys[1]
|
||||
})), callback)
|
||||
|
||||
const keys = await exportKey(pair)
|
||||
|
||||
return {
|
||||
privateKey: keys[0],
|
||||
publicKey: keys[1]
|
||||
}
|
||||
}
|
||||
|
||||
// Takes a jwk key
|
||||
exports.unmarshalPrivateKey = function (key, callback) {
|
||||
const privateKey = webcrypto.subtle.importKey(
|
||||
exports.unmarshalPrivateKey = async function (key) {
|
||||
const privateKey = await webcrypto.subtle.importKey(
|
||||
'jwk',
|
||||
key,
|
||||
{
|
||||
@ -37,22 +38,26 @@ exports.unmarshalPrivateKey = function (key, callback) {
|
||||
['sign']
|
||||
)
|
||||
|
||||
nodeify(Promise.all([
|
||||
const pair = [
|
||||
privateKey,
|
||||
derivePublicFromPrivate(key)
|
||||
]).then((keys) => exportKey({
|
||||
await derivePublicFromPrivate(key)
|
||||
]
|
||||
|
||||
const keys = await exportKey({
|
||||
privateKey: pair[0],
|
||||
publicKey: pair[1]
|
||||
})
|
||||
|
||||
return {
|
||||
privateKey: keys[0],
|
||||
publicKey: keys[1]
|
||||
})).then((keys) => ({
|
||||
privateKey: keys[0],
|
||||
publicKey: keys[1]
|
||||
})), callback)
|
||||
}
|
||||
}
|
||||
|
||||
exports.getRandomValues = randomBytes
|
||||
|
||||
exports.hashAndSign = function (key, msg, callback) {
|
||||
nodeify(webcrypto.subtle.importKey(
|
||||
exports.hashAndSign = async function (key, msg) {
|
||||
const privateKey = await webcrypto.subtle.importKey(
|
||||
'jwk',
|
||||
key,
|
||||
{
|
||||
@ -61,17 +66,19 @@ exports.hashAndSign = function (key, msg, callback) {
|
||||
},
|
||||
false,
|
||||
['sign']
|
||||
).then((privateKey) => {
|
||||
return webcrypto.subtle.sign(
|
||||
{ name: 'RSASSA-PKCS1-v1_5' },
|
||||
privateKey,
|
||||
Uint8Array.from(msg)
|
||||
)
|
||||
}).then((sig) => Buffer.from(sig)), callback)
|
||||
)
|
||||
|
||||
const sig = await webcrypto.subtle.sign(
|
||||
{ name: 'RSASSA-PKCS1-v1_5' },
|
||||
privateKey,
|
||||
Uint8Array.from(msg)
|
||||
)
|
||||
|
||||
return Buffer.from(sig)
|
||||
}
|
||||
|
||||
exports.hashAndVerify = function (key, sig, msg, callback) {
|
||||
nodeify(webcrypto.subtle.importKey(
|
||||
exports.hashAndVerify = async function (key, sig, msg) {
|
||||
const publicKey = await webcrypto.subtle.importKey(
|
||||
'jwk',
|
||||
key,
|
||||
{
|
||||
@ -80,14 +87,14 @@ exports.hashAndVerify = function (key, sig, msg, callback) {
|
||||
},
|
||||
false,
|
||||
['verify']
|
||||
).then((publicKey) => {
|
||||
return webcrypto.subtle.verify(
|
||||
{ name: 'RSASSA-PKCS1-v1_5' },
|
||||
publicKey,
|
||||
sig,
|
||||
msg
|
||||
)
|
||||
}), callback)
|
||||
)
|
||||
|
||||
return webcrypto.subtle.verify(
|
||||
{ name: 'RSASSA-PKCS1-v1_5' },
|
||||
publicKey,
|
||||
sig,
|
||||
msg
|
||||
)
|
||||
}
|
||||
|
||||
function exportKey (pair) {
|
||||
|
@ -3,7 +3,6 @@
|
||||
const multihashing = require('multihashing-async')
|
||||
const protobuf = require('protons')
|
||||
const bs58 = require('bs58')
|
||||
const nextTick = require('async/nextTick')
|
||||
|
||||
const crypto = require('./rsa')
|
||||
const pbm = protobuf(require('./keys.proto'))
|
||||
@ -16,9 +15,8 @@ class RsaPublicKey {
|
||||
this._key = key
|
||||
}
|
||||
|
||||
verify (data, sig, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndVerify(this._key, sig, data, callback)
|
||||
async verify (data, sig) { // eslint-disable-line require-await
|
||||
return crypto.hashAndVerify(this._key, sig, data)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
@ -40,9 +38,8 @@ class RsaPublicKey {
|
||||
return this.bytes.equals(key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
async hash () { // eslint-disable-line require-await
|
||||
return multihashing(this.bytes, 'sha2-256')
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,9 +55,8 @@ class RsaPrivateKey {
|
||||
return crypto.getRandomValues(16)
|
||||
}
|
||||
|
||||
sign (message, callback) {
|
||||
ensure(callback)
|
||||
crypto.hashAndSign(this._key, message, callback)
|
||||
async sign (message) { // eslint-disable-line require-await
|
||||
return crypto.hashAndSign(this._key, message)
|
||||
}
|
||||
|
||||
get public () {
|
||||
@ -71,10 +67,6 @@ class RsaPrivateKey {
|
||||
return new RsaPublicKey(this._publicKey)
|
||||
}
|
||||
|
||||
decrypt (msg, callback) {
|
||||
crypto.decrypt(this._key, msg, callback)
|
||||
}
|
||||
|
||||
marshal () {
|
||||
return crypto.utils.jwkToPkcs1(this._key)
|
||||
}
|
||||
@ -90,9 +82,8 @@ class RsaPrivateKey {
|
||||
return this.bytes.equals(key.bytes)
|
||||
}
|
||||
|
||||
hash (callback) {
|
||||
ensure(callback)
|
||||
multihashing(this.bytes, 'sha2-256', callback)
|
||||
async hash () { // eslint-disable-line require-await
|
||||
return multihashing(this.bytes, 'sha2-256')
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,16 +93,11 @@ class RsaPrivateKey {
|
||||
* The public key is a protobuf encoding containing a type and the DER encoding
|
||||
* of the PKCS SubjectPublicKeyInfo.
|
||||
*
|
||||
* @param {function(Error, id)} callback
|
||||
* @returns {undefined}
|
||||
* @returns {Promise<String>}
|
||||
*/
|
||||
id (callback) {
|
||||
this.public.hash((err, hash) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
callback(null, bs58.encode(hash))
|
||||
})
|
||||
async id () {
|
||||
const hash = await this.public.hash()
|
||||
return bs58.encode(hash)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,87 +105,55 @@ class RsaPrivateKey {
|
||||
*
|
||||
* @param {string} [format] - Defaults to 'pkcs-8'.
|
||||
* @param {string} password - The password to read the encrypted PEM
|
||||
* @param {function(Error, KeyInfo)} callback
|
||||
* @returns {undefined}
|
||||
* @returns {KeyInfo}
|
||||
*/
|
||||
export (format, password, callback) {
|
||||
if (typeof password === 'function') {
|
||||
callback = password
|
||||
async export (format, password) { // eslint-disable-line require-await
|
||||
if (password == null) {
|
||||
password = format
|
||||
format = 'pkcs-8'
|
||||
}
|
||||
|
||||
ensure(callback)
|
||||
let pem = null
|
||||
|
||||
nextTick(() => {
|
||||
let err = null
|
||||
let pem = null
|
||||
try {
|
||||
const buffer = new forge.util.ByteBuffer(this.marshal())
|
||||
const asn1 = forge.asn1.fromDer(buffer)
|
||||
const privateKey = forge.pki.privateKeyFromAsn1(asn1)
|
||||
if (format === 'pkcs-8') {
|
||||
const options = {
|
||||
algorithm: 'aes256',
|
||||
count: 10000,
|
||||
saltSize: 128 / 8,
|
||||
prfAlgorithm: 'sha512'
|
||||
}
|
||||
pem = forge.pki.encryptRsaPrivateKey(privateKey, password, options)
|
||||
} else {
|
||||
err = new Error(`Unknown export format '${format}'`)
|
||||
}
|
||||
} catch (_err) {
|
||||
err = _err
|
||||
const buffer = new forge.util.ByteBuffer(this.marshal())
|
||||
const asn1 = forge.asn1.fromDer(buffer)
|
||||
const privateKey = forge.pki.privateKeyFromAsn1(asn1)
|
||||
|
||||
if (format === 'pkcs-8') {
|
||||
const options = {
|
||||
algorithm: 'aes256',
|
||||
count: 10000,
|
||||
saltSize: 128 / 8,
|
||||
prfAlgorithm: 'sha512'
|
||||
}
|
||||
pem = forge.pki.encryptRsaPrivateKey(privateKey, password, options)
|
||||
} else {
|
||||
throw new Error(`Unknown export format '${format}'`)
|
||||
}
|
||||
|
||||
callback(err, pem)
|
||||
})
|
||||
return pem
|
||||
}
|
||||
}
|
||||
|
||||
function unmarshalRsaPrivateKey (bytes, callback) {
|
||||
async function unmarshalRsaPrivateKey (bytes) {
|
||||
const jwk = crypto.utils.pkcs1ToJwk(bytes)
|
||||
|
||||
crypto.unmarshalPrivateKey(jwk, (err, keys) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
callback(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
|
||||
})
|
||||
const keys = await crypto.unmarshalPrivateKey(jwk)
|
||||
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
|
||||
}
|
||||
|
||||
function unmarshalRsaPublicKey (bytes) {
|
||||
const jwk = crypto.utils.pkixToJwk(bytes)
|
||||
|
||||
return new RsaPublicKey(jwk)
|
||||
}
|
||||
|
||||
function fromJwk (jwk, callback) {
|
||||
crypto.unmarshalPrivateKey(jwk, (err, keys) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
callback(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
|
||||
})
|
||||
async function fromJwk (jwk) {
|
||||
const keys = await crypto.unmarshalPrivateKey(jwk)
|
||||
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
|
||||
}
|
||||
|
||||
function generateKeyPair (bits, callback) {
|
||||
crypto.generateKey(bits, (err, keys) => {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
callback(null, new RsaPrivateKey(keys.privateKey, keys.publicKey))
|
||||
})
|
||||
}
|
||||
|
||||
function ensure (callback) {
|
||||
if (typeof callback !== 'function') {
|
||||
throw new Error('callback is required')
|
||||
}
|
||||
async function generateKeyPair (bits) {
|
||||
const keys = await crypto.generateKey(bits)
|
||||
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
const crypto = require('crypto')
|
||||
const randomBytes = require('../random-bytes')
|
||||
const nextTick = require('async/nextTick')
|
||||
|
||||
let keypair
|
||||
try {
|
||||
@ -30,70 +29,41 @@ const jwkToPem = require('pem-jwk').jwk2pem
|
||||
|
||||
exports.utils = require('./rsa-utils')
|
||||
|
||||
exports.generateKey = function (bits, callback) {
|
||||
nextTick(() => {
|
||||
let result
|
||||
try {
|
||||
const key = keypair({ bits: bits })
|
||||
result = {
|
||||
privateKey: pemToJwk(key.private),
|
||||
publicKey: pemToJwk(key.public)
|
||||
}
|
||||
} catch (err) {
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
callback(null, result)
|
||||
})
|
||||
exports.generateKey = async function (bits) { // eslint-disable-line require-await
|
||||
const key = keypair({ bits })
|
||||
return {
|
||||
privateKey: pemToJwk(key.private),
|
||||
publicKey: pemToJwk(key.public)
|
||||
}
|
||||
}
|
||||
|
||||
// Takes a jwk key
|
||||
exports.unmarshalPrivateKey = function (key, callback) {
|
||||
nextTick(() => {
|
||||
if (!key) {
|
||||
return callback(new Error('Key is invalid'))
|
||||
exports.unmarshalPrivateKey = async function (key) { // eslint-disable-line require-await
|
||||
if (!key) {
|
||||
throw new Error('Key is invalid')
|
||||
}
|
||||
return {
|
||||
privateKey: key,
|
||||
publicKey: {
|
||||
kty: key.kty,
|
||||
n: key.n,
|
||||
e: key.e
|
||||
}
|
||||
callback(null, {
|
||||
privateKey: key,
|
||||
publicKey: {
|
||||
kty: key.kty,
|
||||
n: key.n,
|
||||
e: key.e
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
exports.getRandomValues = randomBytes
|
||||
|
||||
exports.hashAndSign = function (key, msg, callback) {
|
||||
nextTick(() => {
|
||||
let result
|
||||
try {
|
||||
const sign = crypto.createSign('RSA-SHA256')
|
||||
sign.update(msg)
|
||||
const pem = jwkToPem(key)
|
||||
result = sign.sign(pem)
|
||||
} catch (err) {
|
||||
return callback(new Error('Key or message is invalid!: ' + err.message))
|
||||
}
|
||||
|
||||
callback(null, result)
|
||||
})
|
||||
exports.hashAndSign = async function (key, msg) { // eslint-disable-line require-await
|
||||
const sign = crypto.createSign('RSA-SHA256')
|
||||
sign.update(msg)
|
||||
const pem = jwkToPem(key)
|
||||
return sign.sign(pem)
|
||||
}
|
||||
|
||||
exports.hashAndVerify = function (key, sig, msg, callback) {
|
||||
nextTick(() => {
|
||||
let result
|
||||
try {
|
||||
const verify = crypto.createVerify('RSA-SHA256')
|
||||
verify.update(msg)
|
||||
const pem = jwkToPem(key)
|
||||
result = verify.verify(pem, sig)
|
||||
} catch (err) {
|
||||
return callback(new Error('Key or message is invalid!:' + err.message))
|
||||
}
|
||||
|
||||
callback(null, result)
|
||||
})
|
||||
exports.hashAndVerify = async function (key, sig, msg) { // eslint-disable-line require-await
|
||||
const verify = crypto.createVerify('RSA-SHA256')
|
||||
verify.update(msg)
|
||||
const pem = jwkToPem(key)
|
||||
return verify.verify(pem, sig)
|
||||
}
|
||||
|
Reference in New Issue
Block a user