mirror of
https://github.com/fluencelabs/js-libp2p-crypto
synced 2025-07-24 00:32:12 +00:00
Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1b0fac84a8 | ||
|
efaafa9c06 | ||
|
88b3018c9c | ||
|
9aacb478c4 | ||
|
269d169f7c | ||
|
c956d1ad2a | ||
|
d3601fa936 | ||
|
f01e3812e9 | ||
|
00477e3bcb | ||
|
0f4c533dfa | ||
|
d566e7ef3b | ||
|
78e2ddd2bd | ||
|
0ad513887a | ||
|
e7468d830d | ||
|
cc2094975b |
@@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
bundlesize: { maxSize: '155kB' }
|
||||
bundlesize: { maxSize: '124kB' }
|
||||
}
|
||||
|
@@ -1 +0,0 @@
|
||||
src/keys/keys.proto.js
|
31
CHANGELOG.md
31
CHANGELOG.md
@@ -1,3 +1,34 @@
|
||||
<a name="0.17.5"></a>
|
||||
## [0.17.5](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.4...v0.17.5) (2020-03-24)
|
||||
|
||||
|
||||
|
||||
<a name="0.17.4"></a>
|
||||
## [0.17.4](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.3...v0.17.4) (2020-03-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add buffer, cleanup, reduce size ([#170](https://github.com/libp2p/js-libp2p-crypto/issues/170)) ([c956d1a](https://github.com/libp2p/js-libp2p-crypto/commit/c956d1a))
|
||||
|
||||
|
||||
|
||||
<a name="0.17.3"></a>
|
||||
## [0.17.3](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.2...v0.17.3) (2020-02-26)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* remove asn1.js and use node-forge ([#166](https://github.com/libp2p/js-libp2p-crypto/issues/166)) ([00477e3](https://github.com/libp2p/js-libp2p-crypto/commit/00477e3))
|
||||
* remove jwk2privPem and jwk2pubPem ([#162](https://github.com/libp2p/js-libp2p-crypto/issues/162)) ([cc20949](https://github.com/libp2p/js-libp2p-crypto/commit/cc20949))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* removes unused jwk2pem methods `jwk2pubPem` and `jwk2privPem`. These methods are not being used in any js libp2p modules, so only users referencing these directly will be impacted.
|
||||
|
||||
|
||||
|
||||
<a name="0.17.2"></a>
|
||||
## [0.17.2](https://github.com/libp2p/js-libp2p-crypto/compare/v0.17.1...v0.17.2) (2020-01-17)
|
||||
|
||||
|
56
package.json
56
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "libp2p-crypto",
|
||||
"version": "0.17.2",
|
||||
"version": "0.17.5",
|
||||
"description": "Crypto primitives for libp2p",
|
||||
"main": "src/index.js",
|
||||
"types": "src/index.d.ts",
|
||||
@@ -37,36 +37,30 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asmcrypto.js": "^2.3.2",
|
||||
"asn1.js": "^5.2.0",
|
||||
"bn.js": "^5.0.0",
|
||||
"browserify-aes": "^1.2.0",
|
||||
"bs58": "^4.0.1",
|
||||
"err-code": "^1.1.2",
|
||||
"buffer": "^5.5.0",
|
||||
"err-code": "^2.0.0",
|
||||
"iso-random-stream": "^1.1.0",
|
||||
"keypair": "^1.0.1",
|
||||
"libp2p-crypto-secp256k1": "~0.4.0",
|
||||
"multihashing-async": "~0.8.0",
|
||||
"libp2p-crypto-secp256k1": "^0.4.2",
|
||||
"multibase": "^0.7.0",
|
||||
"multihashing-async": "^0.8.1",
|
||||
"node-forge": "~0.9.1",
|
||||
"pem-jwk": "^2.0.0",
|
||||
"protons": "^1.0.1",
|
||||
"rsa-pem-to-jwk": "^1.1.3",
|
||||
"tweetnacl": "^1.0.1",
|
||||
"ursa-optional": "~0.10.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bn.js": "^4.11.6",
|
||||
"@types/chai": "^4.2.7",
|
||||
"@types/chai": "^4.2.11",
|
||||
"@types/chai-string": "^1.4.2",
|
||||
"@types/dirty-chai": "^2.0.2",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/mocha": "^7.0.1",
|
||||
"@types/sinon": "^7.5.1",
|
||||
"aegir": "^20.4.1",
|
||||
"aegir": "^21.0.2",
|
||||
"benchmark": "^2.1.4",
|
||||
"chai": "^4.2.0",
|
||||
"chai-string": "^1.5.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"sinon": "^7.5.0"
|
||||
"sinon": "^9.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0",
|
||||
@@ -81,29 +75,25 @@
|
||||
},
|
||||
"homepage": "https://github.com/libp2p/js-libp2p-crypto",
|
||||
"contributors": [
|
||||
"Alan Shaw <alan.shaw@protocol.ai>",
|
||||
"Alan Shaw <alan@tableflip.io>",
|
||||
"Alberto Elias <hi@albertoelias.me>",
|
||||
"Arve Knudsen <arve.knudsen@gmail.com>",
|
||||
"Carson Farmer <carson.farmer@gmail.com>",
|
||||
"David Dias <daviddias.p@gmail.com>",
|
||||
"Dmitriy Ryajov <dryajov@gmail.com>",
|
||||
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
|
||||
"Greenkeeper <support@greenkeeper.io>",
|
||||
"Maciej Krüger <mkg20001@gmail.com>",
|
||||
"Jacob Heun <jacobheun@gmail.com>",
|
||||
"dryajov <dryajov@gmail.com>",
|
||||
"Alan Shaw <alan.shaw@protocol.ai>",
|
||||
"Yusef Napora <yusef@napora.org>",
|
||||
"Richard Littauer <richard.littauer@gmail.com>",
|
||||
"Arve Knudsen <arve.knudsen@gmail.com>",
|
||||
"Hugo Dias <hugomrdias@gmail.com>",
|
||||
"Jack Kleeman <jackkleeman@gmail.com>",
|
||||
"Jacob Heun <jacobheun@gmail.com>",
|
||||
"Joao Santos <jrmsantos15@gmail.com>",
|
||||
"Maciej Krüger <mkg20001@gmail.com>",
|
||||
"Richard Littauer <richard.littauer@gmail.com>",
|
||||
"Richard Schneider <makaretu@gmail.com>",
|
||||
"Tom Swindell <t.swindell@rubyx.co.uk>",
|
||||
"Vasco Santos <vasco.santos@ua.pt>",
|
||||
"Victor Bjelkholm <victorbjelkholm@gmail.com>",
|
||||
"Yusef Napora <yusef@napora.org>",
|
||||
"dignifiedquire <dignifiedquire@users.noreply.github.com>",
|
||||
"dirkmc <dirkmdev@gmail.com>",
|
||||
"greenkeeper[bot] <greenkeeper[bot]@users.noreply.github.com>",
|
||||
"nikuda <nikuda@gmail.com>"
|
||||
"nikuda <nikuda@gmail.com>",
|
||||
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>",
|
||||
"Tom Swindell <t.swindell@rubyx.co.uk>",
|
||||
"Carson Farmer <carson.farmer@gmail.com>",
|
||||
"Alberto Elias <hi@albertoelias.me>",
|
||||
"Joao Santos <jrmsantos15@gmail.com>"
|
||||
]
|
||||
}
|
||||
|
@@ -1,8 +1,27 @@
|
||||
'use strict'
|
||||
|
||||
const crypto = require('browserify-aes')
|
||||
const { Buffer } = require('buffer')
|
||||
require('node-forge/lib/aes')
|
||||
const forge = require('node-forge/lib/forge')
|
||||
|
||||
module.exports = {
|
||||
createCipheriv: crypto.createCipheriv,
|
||||
createDecipheriv: crypto.createDecipheriv
|
||||
createCipheriv: (mode, key, iv) => {
|
||||
const cipher2 = forge.cipher.createCipher('AES-CTR', key.toString('binary'))
|
||||
cipher2.start({ iv: iv.toString('binary') })
|
||||
return {
|
||||
update: (data) => {
|
||||
cipher2.update(forge.util.createBuffer(data.toString('binary')))
|
||||
return Buffer.from(cipher2.output.getBytes(), 'binary')
|
||||
}
|
||||
}
|
||||
},
|
||||
createDecipheriv: (mode, key, iv) => {
|
||||
const cipher2 = forge.cipher.createDecipher('AES-CTR', key.toString('binary'))
|
||||
cipher2.start({ iv: iv.toString('binary') })
|
||||
return {
|
||||
update: (data) => {
|
||||
cipher2.update(forge.util.createBuffer(data.toString('binary')))
|
||||
return Buffer.from(cipher2.output.getBytes(), 'binary')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,34 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const asm = require('asmcrypto.js')
|
||||
const validateCipherMode = require('./cipher-mode')
|
||||
|
||||
exports.create = async function (key, iv) { // eslint-disable-line require-await
|
||||
// Throws an error if mode is invalid
|
||||
validateCipherMode(key)
|
||||
|
||||
const enc = new asm.AES_CTR.Encrypt({
|
||||
key: key,
|
||||
nonce: iv
|
||||
})
|
||||
const dec = new asm.AES_CTR.Decrypt({
|
||||
key: key,
|
||||
nonce: iv
|
||||
})
|
||||
|
||||
const res = {
|
||||
async encrypt (data) { // eslint-disable-line require-await
|
||||
return Buffer.from(
|
||||
enc.process(data).result
|
||||
)
|
||||
},
|
||||
|
||||
async decrypt (data) { // eslint-disable-line require-await
|
||||
return Buffer.from(
|
||||
dec.process(data).result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const webcrypto = require('../webcrypto')
|
||||
const lengths = require('./lengths')
|
||||
|
||||
|
@@ -1,9 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
const errcode = require('err-code')
|
||||
const { Buffer } = require('buffer')
|
||||
const webcrypto = require('../webcrypto')
|
||||
const BN = require('asn1.js').bignum
|
||||
const { toBase64, toBn } = require('../util')
|
||||
const { bufferToBase64url, base64urlToBuffer } = require('../util')
|
||||
const validateCurveType = require('./validate-curve-type')
|
||||
|
||||
const bits = {
|
||||
@@ -89,8 +89,8 @@ function marshalPublicKey (jwk) {
|
||||
|
||||
return Buffer.concat([
|
||||
Buffer.from([4]), // uncompressed point
|
||||
toBn(jwk.x).toArrayLike(Buffer, 'be', byteLen),
|
||||
toBn(jwk.y).toArrayLike(Buffer, 'be', byteLen)
|
||||
base64urlToBuffer(jwk.x, byteLen),
|
||||
base64urlToBuffer(jwk.y, byteLen)
|
||||
], 1 + byteLen * 2)
|
||||
}
|
||||
|
||||
@@ -101,20 +101,17 @@ function unmarshalPublicKey (curve, key) {
|
||||
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),
|
||||
x: bufferToBase64url(key.slice(1, byteLen + 1), byteLen),
|
||||
y: bufferToBase64url(key.slice(1 + byteLen), byteLen),
|
||||
ext: true
|
||||
}
|
||||
}
|
||||
|
||||
function unmarshalPrivateKey (curve, key) {
|
||||
const result = unmarshalPublicKey(curve, key.public)
|
||||
result.d = toBase64(new BN(key.private))
|
||||
return result
|
||||
}
|
||||
const unmarshalPrivateKey = (curve, key) => ({
|
||||
...unmarshalPublicKey(curve, key.public),
|
||||
d: bufferToBase64url(key.private)
|
||||
})
|
||||
|
@@ -1,8 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
const multihashing = require('multihashing-async')
|
||||
const { Buffer } = require('buffer')
|
||||
const sha = require('multihashing-async/src/sha')
|
||||
const protobuf = require('protons')
|
||||
const bs58 = require('bs58')
|
||||
const multibase = require('multibase')
|
||||
const errcode = require('err-code')
|
||||
|
||||
const crypto = require('./ed25519')
|
||||
@@ -33,7 +34,7 @@ class Ed25519PublicKey {
|
||||
}
|
||||
|
||||
async hash () { // eslint-disable-line require-await
|
||||
return multihashing(this.bytes, 'sha2-256')
|
||||
return sha.multihashing(this.bytes, 'sha2-256')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +70,7 @@ class Ed25519PrivateKey {
|
||||
}
|
||||
|
||||
async hash () { // eslint-disable-line require-await
|
||||
return multihashing(this.bytes, 'sha2-256')
|
||||
return sha.multihashing(this.bytes, 'sha2-256')
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,7 +84,7 @@ class Ed25519PrivateKey {
|
||||
*/
|
||||
async id () {
|
||||
const hash = await this.public.hash()
|
||||
return bs58.encode(hash)
|
||||
return multibase.encode('base58btc', hash).toString().slice(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,13 +101,13 @@ function unmarshalEd25519PublicKey (bytes) {
|
||||
}
|
||||
|
||||
async function generateKeyPair () {
|
||||
const { secretKey, publicKey } = await crypto.generateKey()
|
||||
return new Ed25519PrivateKey(secretKey, publicKey)
|
||||
const { privateKey, publicKey } = await crypto.generateKey()
|
||||
return new Ed25519PrivateKey(privateKey, publicKey)
|
||||
}
|
||||
|
||||
async function generateKeyPairFromSeed (seed) {
|
||||
const { secretKey, publicKey } = await crypto.generateKeyFromSeed(seed)
|
||||
return new Ed25519PrivateKey(secretKey, publicKey)
|
||||
const { privateKey, publicKey } = await crypto.generateKeyFromSeed(seed)
|
||||
return new Ed25519PrivateKey(privateKey, publicKey)
|
||||
}
|
||||
|
||||
function ensureKey (key, length) {
|
||||
|
@@ -1,23 +1,24 @@
|
||||
'use strict'
|
||||
|
||||
const nacl = require('tweetnacl')
|
||||
|
||||
exports.publicKeyLength = nacl.sign.publicKeyLength
|
||||
exports.privateKeyLength = nacl.sign.secretKeyLength
|
||||
require('node-forge/lib/ed25519')
|
||||
const forge = require('node-forge/lib/forge')
|
||||
exports.publicKeyLength = forge.pki.ed25519.constants.PUBLIC_KEY_BYTE_LENGTH
|
||||
exports.privateKeyLength = forge.pki.ed25519.constants.PRIVATE_KEY_BYTE_LENGTH
|
||||
|
||||
exports.generateKey = async function () { // eslint-disable-line require-await
|
||||
return nacl.sign.keyPair()
|
||||
return forge.pki.ed25519.generateKeyPair()
|
||||
}
|
||||
|
||||
// seed should be a 32 byte uint8array
|
||||
exports.generateKeyFromSeed = async function (seed) { // eslint-disable-line require-await
|
||||
return nacl.sign.keyPair.fromSeed(seed)
|
||||
return forge.pki.ed25519.generateKeyPair({ seed })
|
||||
}
|
||||
|
||||
exports.hashAndSign = async function (key, msg) { // eslint-disable-line require-await
|
||||
return Buffer.from(nacl.sign.detached(msg, key))
|
||||
return forge.pki.ed25519.sign({ message: msg, privateKey: key })
|
||||
// return Buffer.from(nacl.sign.detached(msg, key))
|
||||
}
|
||||
|
||||
exports.hashAndVerify = async function (key, sig, msg) { // eslint-disable-line require-await
|
||||
return nacl.sign.detached.verify(msg, sig, key)
|
||||
return forge.pki.ed25519.verify({ signature: sig, message: msg, publicKey: key })
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const protobuf = require('protons')
|
||||
const keysPBM = protobuf(require('./keys.proto'))
|
||||
require('node-forge/lib/asn1')
|
||||
require('node-forge/lib/rsa')
|
||||
require('node-forge/lib/pbe')
|
||||
const forge = require('node-forge/lib/forge')
|
||||
const errcode = require('err-code')
|
||||
|
@@ -1,18 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
const forge = {
|
||||
util: require('node-forge/lib/util'),
|
||||
pki: require('node-forge/lib/pki'),
|
||||
jsbn: require('node-forge/lib/jsbn')
|
||||
}
|
||||
|
||||
function base64urlToBigInteger (str) {
|
||||
var bytes = forge.util.decode64(
|
||||
(str + '==='.slice((str.length + 3) % 4))
|
||||
.replace(/-/g, '+')
|
||||
.replace(/_/g, '/'))
|
||||
return new forge.jsbn.BigInteger(forge.util.bytesToHex(bytes), 16)
|
||||
}
|
||||
require('node-forge/lib/rsa')
|
||||
const forge = require('node-forge/lib/forge')
|
||||
const { base64urlToBigInteger } = require('../util')
|
||||
|
||||
function convert (key, types) {
|
||||
return types.map(t => base64urlToBigInteger(key[t]))
|
||||
@@ -22,21 +12,11 @@ function jwk2priv (key) {
|
||||
return forge.pki.setRsaPrivateKey(...convert(key, ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi']))
|
||||
}
|
||||
|
||||
function jwk2privPem (key) {
|
||||
return forge.pki.privateKeyToPem(jwk2priv(key))
|
||||
}
|
||||
|
||||
function jwk2pub (key) {
|
||||
return forge.pki.setRsaPublicKey(...convert(key, ['n', 'e']))
|
||||
}
|
||||
|
||||
function jwk2pubPem (key) {
|
||||
return forge.pki.publicKeyToPem(jwk2pub(key))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
jwk2pub,
|
||||
jwk2pubPem,
|
||||
jwk2priv,
|
||||
jwk2privPem
|
||||
jwk2priv
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const errcode = require('err-code')
|
||||
const hmac = require('../hmac')
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const webcrypto = require('../webcrypto')
|
||||
const randomBytes = require('../random-bytes')
|
||||
|
||||
|
@@ -1,14 +1,14 @@
|
||||
'use strict'
|
||||
|
||||
const multihashing = require('multihashing-async')
|
||||
const sha = require('multihashing-async/src/sha')
|
||||
const protobuf = require('protons')
|
||||
const bs58 = require('bs58')
|
||||
const multibase = require('multibase')
|
||||
const errcode = require('err-code')
|
||||
|
||||
const crypto = require('./rsa')
|
||||
const pbm = protobuf(require('./keys.proto'))
|
||||
require('node-forge/lib/sha512')
|
||||
require('node-forge/lib/pbe')
|
||||
require('node-forge/lib/ed25519')
|
||||
const forge = require('node-forge/lib/forge')
|
||||
|
||||
class RsaPublicKey {
|
||||
@@ -40,7 +40,7 @@ class RsaPublicKey {
|
||||
}
|
||||
|
||||
async hash () { // eslint-disable-line require-await
|
||||
return multihashing(this.bytes, 'sha2-256')
|
||||
return sha.multihashing(this.bytes, 'sha2-256')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ class RsaPrivateKey {
|
||||
}
|
||||
|
||||
async hash () { // eslint-disable-line require-await
|
||||
return multihashing(this.bytes, 'sha2-256')
|
||||
return sha.multihashing(this.bytes, 'sha2-256')
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,7 +102,7 @@ class RsaPrivateKey {
|
||||
*/
|
||||
async id () {
|
||||
const hash = await this.public.hash()
|
||||
return bs58.encode(hash)
|
||||
return multibase.encode('base58btc', hash).toString().slice(1)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,68 +1,27 @@
|
||||
'use strict'
|
||||
|
||||
const asn1 = require('asn1.js')
|
||||
|
||||
const util = require('./../util')
|
||||
const toBase64 = util.toBase64
|
||||
const toBn = util.toBn
|
||||
|
||||
const RSAPrivateKey = asn1.define('RSAPrivateKey', function () {
|
||||
this.seq().obj(
|
||||
this.key('version').int(),
|
||||
this.key('modulus').int(),
|
||||
this.key('publicExponent').int(),
|
||||
this.key('privateExponent').int(),
|
||||
this.key('prime1').int(),
|
||||
this.key('prime2').int(),
|
||||
this.key('exponent1').int(),
|
||||
this.key('exponent2').int(),
|
||||
this.key('coefficient').int()
|
||||
)
|
||||
})
|
||||
|
||||
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()
|
||||
)
|
||||
})
|
||||
const { Buffer } = require('buffer')
|
||||
require('node-forge/lib/asn1')
|
||||
require('node-forge/lib/rsa')
|
||||
const forge = require('node-forge/lib/forge')
|
||||
const { bigIntegerToUintBase64url, base64urlToBigInteger } = require('./../util')
|
||||
|
||||
// Convert a PKCS#1 in ASN1 DER format to a JWK key
|
||||
exports.pkcs1ToJwk = function (bytes) {
|
||||
const asn1 = RSAPrivateKey.decode(bytes, 'der')
|
||||
const asn1 = forge.asn1.fromDer(bytes.toString('binary'))
|
||||
const privateKey = forge.pki.privateKeyFromAsn1(asn1)
|
||||
|
||||
// https://tools.ietf.org/html/rfc7518#section-6.3.1
|
||||
return {
|
||||
kty: 'RSA',
|
||||
n: toBase64(asn1.modulus),
|
||||
e: toBase64(asn1.publicExponent),
|
||||
d: toBase64(asn1.privateExponent),
|
||||
p: toBase64(asn1.prime1),
|
||||
q: toBase64(asn1.prime2),
|
||||
dp: toBase64(asn1.exponent1),
|
||||
dq: toBase64(asn1.exponent2),
|
||||
qi: toBase64(asn1.coefficient),
|
||||
n: bigIntegerToUintBase64url(privateKey.n),
|
||||
e: bigIntegerToUintBase64url(privateKey.e),
|
||||
d: bigIntegerToUintBase64url(privateKey.d),
|
||||
p: bigIntegerToUintBase64url(privateKey.p),
|
||||
q: bigIntegerToUintBase64url(privateKey.q),
|
||||
dp: bigIntegerToUintBase64url(privateKey.dP),
|
||||
dq: bigIntegerToUintBase64url(privateKey.dQ),
|
||||
qi: bigIntegerToUintBase64url(privateKey.qInv),
|
||||
alg: 'RS256',
|
||||
kid: '2011-04-29'
|
||||
}
|
||||
@@ -70,28 +29,29 @@ exports.pkcs1ToJwk = function (bytes) {
|
||||
|
||||
// Convert a JWK key into PKCS#1 in ASN1 DER format
|
||||
exports.jwkToPkcs1 = function (jwk) {
|
||||
return RSAPrivateKey.encode({
|
||||
version: 0,
|
||||
modulus: toBn(jwk.n),
|
||||
publicExponent: toBn(jwk.e),
|
||||
privateExponent: toBn(jwk.d),
|
||||
prime1: toBn(jwk.p),
|
||||
prime2: toBn(jwk.q),
|
||||
exponent1: toBn(jwk.dp),
|
||||
exponent2: toBn(jwk.dq),
|
||||
coefficient: toBn(jwk.qi)
|
||||
}, 'der')
|
||||
const asn1 = forge.pki.privateKeyToAsn1({
|
||||
n: base64urlToBigInteger(jwk.n),
|
||||
e: base64urlToBigInteger(jwk.e),
|
||||
d: base64urlToBigInteger(jwk.d),
|
||||
p: base64urlToBigInteger(jwk.p),
|
||||
q: base64urlToBigInteger(jwk.q),
|
||||
dP: base64urlToBigInteger(jwk.dp),
|
||||
dQ: base64urlToBigInteger(jwk.dq),
|
||||
qInv: base64urlToBigInteger(jwk.qi)
|
||||
})
|
||||
|
||||
return Buffer.from(forge.asn1.toDer(asn1).getBytes(), 'binary')
|
||||
}
|
||||
|
||||
// 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')
|
||||
const asn1 = forge.asn1.fromDer(bytes.toString('binary'))
|
||||
const publicKey = forge.pki.publicKeyFromAsn1(asn1)
|
||||
|
||||
return {
|
||||
kty: 'RSA',
|
||||
n: toBase64(asn1.modulus),
|
||||
e: toBase64(asn1.publicExponent),
|
||||
n: bigIntegerToUintBase64url(publicKey.n),
|
||||
e: bigIntegerToUintBase64url(publicKey.e),
|
||||
alg: 'RS256',
|
||||
kid: '2011-04-29'
|
||||
}
|
||||
@@ -99,16 +59,10 @@ exports.pkixToJwk = function (bytes) {
|
||||
|
||||
// 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')
|
||||
const asn1 = forge.pki.publicKeyToAsn1({
|
||||
n: base64urlToBigInteger(jwk.n),
|
||||
e: base64urlToBigInteger(jwk.e)
|
||||
})
|
||||
|
||||
return Buffer.from(forge.asn1.toDer(asn1).getBytes(), 'binary')
|
||||
}
|
||||
|
57
src/util.js
57
src/util.js
@@ -1,20 +1,55 @@
|
||||
'use strict'
|
||||
|
||||
const BN = require('asn1.js').bignum
|
||||
const { Buffer } = require('buffer')
|
||||
require('node-forge/lib/util')
|
||||
require('node-forge/lib/jsbn')
|
||||
const forge = require('node-forge/lib/forge')
|
||||
|
||||
// Convert a BN.js instance to a base64 encoded string without padding
|
||||
exports.bigIntegerToUintBase64url = (num, len) => {
|
||||
// Call `.abs()` to convert to unsigned
|
||||
let buf = Buffer.from(num.abs().toByteArray()) // toByteArray converts to big endian
|
||||
|
||||
// toByteArray() gives us back a signed array, which will include a leading 0
|
||||
// byte if the most significant bit of the number is 1:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/seccertenroll/about-integer
|
||||
// Our number will always be positive so we should remove the leading padding.
|
||||
buf = buf[0] === 0 ? buf.slice(1) : buf
|
||||
|
||||
if (len != null) {
|
||||
if (buf.length > len) throw new Error('byte array longer than desired length')
|
||||
buf = Buffer.concat([Buffer.alloc(len - buf.length), buf])
|
||||
}
|
||||
|
||||
return exports.bufferToBase64url(buf)
|
||||
}
|
||||
|
||||
// Convert a Buffer 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, len) {
|
||||
// if len is defined then the bytes are leading-0 padded to the length
|
||||
const s = bn.toArrayLike(Buffer, 'be', len).toString('base64')
|
||||
|
||||
return s
|
||||
.replace(/(=*)$/, '') // Remove any trailing '='s
|
||||
exports.bufferToBase64url = buf => {
|
||||
return buf
|
||||
.toString('base64')
|
||||
.split('=')[0] // 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'))
|
||||
// Convert a base64url encoded string to a BigInteger
|
||||
exports.base64urlToBigInteger = str => {
|
||||
const buf = exports.base64urlToBuffer(str)
|
||||
return new forge.jsbn.BigInteger(buf.toString('hex'), 16)
|
||||
}
|
||||
|
||||
exports.base64urlToBuffer = (str, len) => {
|
||||
str = (str + '==='.slice((str.length + 3) % 4))
|
||||
.replace(/-/g, '+')
|
||||
.replace(/_/g, '/')
|
||||
|
||||
let buf = Buffer.from(str, 'base64')
|
||||
|
||||
if (len != null) {
|
||||
if (buf.length > len) throw new Error('byte array longer than desired length')
|
||||
buf = Buffer.concat([Buffer.alloc(len - buf.length), buf])
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
|
1
test/fixtures/go-elliptic-key.js
vendored
1
test/fixtures/go-elliptic-key.js
vendored
@@ -1,4 +1,5 @@
|
||||
'use strict'
|
||||
const { Buffer } = require('buffer')
|
||||
|
||||
module.exports = {
|
||||
curve: 'P-256',
|
||||
|
1
test/fixtures/go-key-ed25519.js
vendored
1
test/fixtures/go-key-ed25519.js
vendored
@@ -1,4 +1,5 @@
|
||||
'use strict'
|
||||
const { Buffer } = require('buffer')
|
||||
|
||||
module.exports = {
|
||||
// These were generated in a gore (https://github.com/motemen/gore) repl session:
|
||||
|
2
test/fixtures/go-key-rsa.js
vendored
2
test/fixtures/go-key-rsa.js
vendored
@@ -1,5 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
module.exports = {
|
||||
private: {
|
||||
hash: Buffer.from([
|
||||
|
2
test/fixtures/go-stretch-key.js
vendored
2
test/fixtures/go-stretch-key.js
vendored
@@ -1,5 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
module.exports = [{
|
||||
cipher: 'AES-256',
|
||||
hash: 'SHA256',
|
||||
|
1
test/fixtures/secp256k1.js
vendored
1
test/fixtures/secp256k1.js
vendored
@@ -1,5 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
module.exports = {
|
||||
// protobuf marshaled key pair generated with libp2p-crypto-secp256k1
|
||||
// and marshaled with libp2p-crypto.marshalPublicKey / marshalPrivateKey
|
||||
|
@@ -1,6 +1,7 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const util = require('util')
|
||||
const garbage = [Buffer.from('00010203040506070809', 'hex'), {}, null, false, undefined, true, 1, 0, Buffer.from(''), 'aGVsbG93b3JsZA==', 'helloworld', '']
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/* eslint max-nested-callbacks: ["error", 8] */
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
|
@@ -1,6 +1,7 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
|
@@ -2,6 +2,7 @@
|
||||
/* eslint-env mocha */
|
||||
'use strict'
|
||||
|
||||
const { Buffer } = require('buffer')
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
|
@@ -3,29 +3,37 @@
|
||||
'use strict'
|
||||
|
||||
const chai = require('chai')
|
||||
const { Buffer } = require('buffer')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const expect = chai.expect
|
||||
chai.use(dirtyChai)
|
||||
|
||||
require('node-forge/lib/jsbn')
|
||||
const forge = require('node-forge/lib/forge')
|
||||
const util = require('../src/util')
|
||||
const BN = require('bn.js')
|
||||
|
||||
describe('Util', () => {
|
||||
let bn
|
||||
|
||||
before((done) => {
|
||||
bn = new BN('dead', 16)
|
||||
done()
|
||||
before(() => {
|
||||
bn = new forge.jsbn.BigInteger('dead', 16)
|
||||
})
|
||||
|
||||
it('toBase64', (done) => {
|
||||
expect(util.toBase64(bn)).to.eql('3q0')
|
||||
done()
|
||||
it('should convert BigInteger to a uint base64url encoded string', () => {
|
||||
expect(util.bigIntegerToUintBase64url(bn)).to.eql('3q0')
|
||||
})
|
||||
|
||||
it('toBase64 zero padding', (done) => {
|
||||
const bnpad = new BN('ff', 16)
|
||||
expect(util.toBase64(bnpad, 2)).to.eql('AP8')
|
||||
done()
|
||||
it('should convert BigInteger to a uint base64url encoded string with padding', () => {
|
||||
const bnpad = new forge.jsbn.BigInteger('ff', 16)
|
||||
expect(util.bigIntegerToUintBase64url(bnpad, 2)).to.eql('AP8')
|
||||
})
|
||||
|
||||
it('should convert base64url encoded string to BigInteger', () => {
|
||||
const num = util.base64urlToBigInteger('3q0')
|
||||
expect(num.equals(bn)).to.be.true()
|
||||
})
|
||||
|
||||
it('should convert base64url encoded string to Buffer with padding', () => {
|
||||
const buf = util.base64urlToBuffer('AP8', 2)
|
||||
expect(Buffer.from([0, 255])).to.eql(buf)
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user