libp2p-crypto
Version:
Crypto primitives for libp2p
154 lines (128 loc) • 3.41 kB
JavaScript
const webcrypto = require('../webcrypto')
const randomBytes = require('../random-bytes')
const { toString: uint8ArrayToString } = require('uint8arrays/to-string')
const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string')
exports.utils = require('./rsa-utils')
exports.generateKey = async function (bits) {
const pair = await webcrypto.get().subtle.generateKey(
{
name: 'RSASSA-PKCS1-v1_5',
modulusLength: bits,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: { name: 'SHA-256' }
},
true,
['sign', 'verify']
)
const keys = await exportKey(pair)
return {
privateKey: keys[0],
publicKey: keys[1]
}
}
// Takes a jwk key
exports.unmarshalPrivateKey = async function (key) {
const privateKey = await webcrypto.get().subtle.importKey(
'jwk',
key,
{
name: 'RSASSA-PKCS1-v1_5',
hash: { name: 'SHA-256' }
},
true,
['sign']
)
const pair = [
privateKey,
await derivePublicFromPrivate(key)
]
const keys = await exportKey({
privateKey: pair[0],
publicKey: pair[1]
})
return {
privateKey: keys[0],
publicKey: keys[1]
}
}
exports.getRandomValues = randomBytes
exports.hashAndSign = async function (key, msg) {
const privateKey = await webcrypto.get().subtle.importKey(
'jwk',
key,
{
name: 'RSASSA-PKCS1-v1_5',
hash: { name: 'SHA-256' }
},
false,
['sign']
)
const sig = await webcrypto.get().subtle.sign(
{ name: 'RSASSA-PKCS1-v1_5' },
privateKey,
Uint8Array.from(msg)
)
return new Uint8Array(sig, sig.byteOffset, sig.byteLength)
}
exports.hashAndVerify = async function (key, sig, msg) {
const publicKey = await webcrypto.get().subtle.importKey(
'jwk',
key,
{
name: 'RSASSA-PKCS1-v1_5',
hash: { name: 'SHA-256' }
},
false,
['verify']
)
return webcrypto.get().subtle.verify(
{ name: 'RSASSA-PKCS1-v1_5' },
publicKey,
sig,
msg
)
}
function exportKey (pair) {
return Promise.all([
webcrypto.get().subtle.exportKey('jwk', pair.privateKey),
webcrypto.get().subtle.exportKey('jwk', pair.publicKey)
])
}
function derivePublicFromPrivate (jwKey) {
return webcrypto.get().subtle.importKey(
'jwk',
{
kty: jwKey.kty,
n: jwKey.n,
e: jwKey.e
},
{
name: 'RSASSA-PKCS1-v1_5',
hash: { name: 'SHA-256' }
},
true,
['verify']
)
}
/*
RSA encryption/decryption for the browser with webcrypto workarround
"bloody dark magic. webcrypto's why."
Explanation:
- Convert JWK to nodeForge
- Convert msg Uint8Array to nodeForge buffer: ByteBuffer is a "binary-string backed buffer", so let's make our Uint8Array a binary string
- Convert resulting nodeForge buffer to Uint8Array: it returns a binary string, turn that into a Uint8Array
*/
const { jwk2pub, jwk2priv } = require('./jwk2pem')
function convertKey (key, pub, msg, handle) {
const fkey = pub ? jwk2pub(key) : jwk2priv(key)
const fmsg = uint8ArrayToString(Uint8Array.from(msg), 'ascii')
const fomsg = handle(fmsg, fkey)
return uint8ArrayFromString(fomsg, 'ascii')
}
exports.encrypt = function (key, msg) {
return convertKey(key, true, msg, (msg, key) => key.encrypt(msg))
}
exports.decrypt = function (key, msg) {
return convertKey(key, false, msg, (msg, key) => key.decrypt(msg))
}