2017-07-22 10:57:27 -07:00
|
|
|
'use strict'
|
|
|
|
|
|
|
|
const multihashing = require('multihashing-async')
|
2017-09-07 11:37:56 +02:00
|
|
|
const protobuf = require('protons')
|
2017-12-20 21:11:47 +13:00
|
|
|
const bs58 = require('bs58')
|
2019-07-22 06:16:02 -04:00
|
|
|
const errcode = require('err-code')
|
2017-07-22 10:57:27 -07:00
|
|
|
|
|
|
|
const crypto = require('./rsa')
|
2017-09-06 08:29:45 +01:00
|
|
|
const pbm = protobuf(require('./keys.proto'))
|
2019-01-08 18:37:03 +00:00
|
|
|
require('node-forge/lib/sha512')
|
|
|
|
require('node-forge/lib/pbe')
|
|
|
|
const forge = require('node-forge/lib/forge')
|
2017-07-22 10:57:27 -07:00
|
|
|
|
|
|
|
class RsaPublicKey {
|
|
|
|
constructor (key) {
|
|
|
|
this._key = key
|
|
|
|
}
|
|
|
|
|
2019-07-10 17:15:26 +01:00
|
|
|
async verify (data, sig) { // eslint-disable-line require-await
|
|
|
|
return crypto.hashAndVerify(this._key, sig, data)
|
2017-07-22 10:57:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
marshal () {
|
|
|
|
return crypto.utils.jwkToPkix(this._key)
|
|
|
|
}
|
|
|
|
|
|
|
|
get bytes () {
|
2017-09-06 08:29:45 +01:00
|
|
|
return pbm.PublicKey.encode({
|
2017-07-22 10:57:27 -07:00
|
|
|
Type: pbm.KeyType.RSA,
|
|
|
|
Data: this.marshal()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-10-23 13:04:55 +02:00
|
|
|
encrypt (bytes) {
|
2019-07-12 18:53:46 +02:00
|
|
|
return crypto.encrypt(this._key, bytes)
|
2017-07-22 10:57:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
equals (key) {
|
|
|
|
return this.bytes.equals(key.bytes)
|
|
|
|
}
|
|
|
|
|
2019-07-10 17:15:26 +01:00
|
|
|
async hash () { // eslint-disable-line require-await
|
|
|
|
return multihashing(this.bytes, 'sha2-256')
|
2017-07-22 10:57:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class RsaPrivateKey {
|
|
|
|
// key - Object of the jwk format
|
|
|
|
// publicKey - Buffer of the spki format
|
|
|
|
constructor (key, publicKey) {
|
|
|
|
this._key = key
|
|
|
|
this._publicKey = publicKey
|
|
|
|
}
|
|
|
|
|
|
|
|
genSecret () {
|
2019-01-08 18:37:03 +00:00
|
|
|
return crypto.getRandomValues(16)
|
2017-07-22 10:57:27 -07:00
|
|
|
}
|
|
|
|
|
2019-07-10 17:15:26 +01:00
|
|
|
async sign (message) { // eslint-disable-line require-await
|
|
|
|
return crypto.hashAndSign(this._key, message)
|
2017-07-22 10:57:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
get public () {
|
|
|
|
if (!this._publicKey) {
|
2019-07-22 06:16:02 -04:00
|
|
|
throw errcode(new Error('public key not provided'), 'ERR_PUBKEY_NOT_PROVIDED')
|
2017-07-22 10:57:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return new RsaPublicKey(this._publicKey)
|
|
|
|
}
|
|
|
|
|
2019-10-23 13:04:55 +02:00
|
|
|
decrypt (bytes) {
|
2019-07-12 18:53:46 +02:00
|
|
|
return crypto.decrypt(this._key, bytes)
|
|
|
|
}
|
|
|
|
|
2017-07-22 10:57:27 -07:00
|
|
|
marshal () {
|
|
|
|
return crypto.utils.jwkToPkcs1(this._key)
|
|
|
|
}
|
|
|
|
|
|
|
|
get bytes () {
|
2017-09-06 08:29:45 +01:00
|
|
|
return pbm.PrivateKey.encode({
|
2017-07-22 10:57:27 -07:00
|
|
|
Type: pbm.KeyType.RSA,
|
|
|
|
Data: this.marshal()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
equals (key) {
|
|
|
|
return this.bytes.equals(key.bytes)
|
|
|
|
}
|
|
|
|
|
2019-07-10 17:15:26 +01:00
|
|
|
async hash () { // eslint-disable-line require-await
|
|
|
|
return multihashing(this.bytes, 'sha2-256')
|
2017-07-22 10:57:27 -07:00
|
|
|
}
|
2017-12-20 21:11:47 +13:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the ID of the key.
|
|
|
|
*
|
|
|
|
* The key id is the base58 encoding of the SHA-256 multihash of its public key.
|
|
|
|
* The public key is a protobuf encoding containing a type and the DER encoding
|
|
|
|
* of the PKCS SubjectPublicKeyInfo.
|
|
|
|
*
|
2019-07-10 17:15:26 +01:00
|
|
|
* @returns {Promise<String>}
|
2017-12-20 21:11:47 +13:00
|
|
|
*/
|
2019-07-10 17:15:26 +01:00
|
|
|
async id () {
|
|
|
|
const hash = await this.public.hash()
|
|
|
|
return bs58.encode(hash)
|
2017-12-20 21:11:47 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Exports the key into a password protected PEM format
|
|
|
|
*
|
|
|
|
* @param {string} password - The password to read the encrypted PEM
|
2019-07-10 13:03:34 -04:00
|
|
|
* @param {string} [format] - Defaults to 'pkcs-8'.
|
2017-12-20 21:11:47 +13:00
|
|
|
*/
|
2019-07-10 13:03:34 -04:00
|
|
|
async export (password, format = 'pkcs-8') { // eslint-disable-line require-await
|
2019-07-10 17:15:26 +01:00
|
|
|
let pem = null
|
|
|
|
|
|
|
|
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'
|
2017-12-20 21:11:47 +13:00
|
|
|
}
|
2019-07-10 17:15:26 +01:00
|
|
|
pem = forge.pki.encryptRsaPrivateKey(privateKey, password, options)
|
|
|
|
} else {
|
2019-07-22 06:16:02 -04:00
|
|
|
throw errcode(new Error(`Unknown export format '${format}'. Must be pkcs-8`), 'ERR_INVALID_EXPORT_FORMAT')
|
2019-07-10 17:15:26 +01:00
|
|
|
}
|
2017-12-20 21:11:47 +13:00
|
|
|
|
2019-07-10 17:15:26 +01:00
|
|
|
return pem
|
2017-12-20 21:11:47 +13:00
|
|
|
}
|
2017-07-22 10:57:27 -07:00
|
|
|
}
|
|
|
|
|
2019-07-10 17:15:26 +01:00
|
|
|
async function unmarshalRsaPrivateKey (bytes) {
|
2017-07-22 10:57:27 -07:00
|
|
|
const jwk = crypto.utils.pkcs1ToJwk(bytes)
|
2019-07-10 17:15:26 +01:00
|
|
|
const keys = await crypto.unmarshalPrivateKey(jwk)
|
|
|
|
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
|
2017-07-22 10:57:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
function unmarshalRsaPublicKey (bytes) {
|
|
|
|
const jwk = crypto.utils.pkixToJwk(bytes)
|
|
|
|
return new RsaPublicKey(jwk)
|
|
|
|
}
|
|
|
|
|
2019-07-10 17:15:26 +01:00
|
|
|
async function fromJwk (jwk) {
|
|
|
|
const keys = await crypto.unmarshalPrivateKey(jwk)
|
|
|
|
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
|
2017-12-20 21:11:47 +13:00
|
|
|
}
|
|
|
|
|
2019-07-10 17:15:26 +01:00
|
|
|
async function generateKeyPair (bits) {
|
|
|
|
const keys = await crypto.generateKey(bits)
|
|
|
|
return new RsaPrivateKey(keys.privateKey, keys.publicKey)
|
2017-07-22 10:57:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
RsaPublicKey,
|
|
|
|
RsaPrivateKey,
|
|
|
|
unmarshalRsaPublicKey,
|
|
|
|
unmarshalRsaPrivateKey,
|
2017-12-20 21:11:47 +13:00
|
|
|
generateKeyPair,
|
|
|
|
fromJwk
|
2017-07-22 10:57:27 -07:00
|
|
|
}
|