Skip to content
This repository was archived by the owner on Jul 21, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .aegir.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
bundlesize: { maxSize: '155kB' }
}
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
include:
- stage: check
script:
- npx aegir commitlint --travis
- npx aegir build --bundlesize
- npx aegir dep-check
- npm run lint

Expand Down
15 changes: 7 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"release-minor": "aegir release --type minor",
"release-major": "aegir release --type major",
"coverage": "aegir coverage --ignore src/keys/keys.proto.js",
"size": "bundlesize -f dist/index.min.js -s 139kB"
"size": "aegir build --bundlesize"
},
"keywords": [
"IPFS",
Expand All @@ -36,30 +36,29 @@
"license": "MIT",
"dependencies": {
"asmcrypto.js": "^2.3.2",
"asn1.js": "^5.0.1",
"asn1.js": "^5.2.0",
"bn.js": "^5.0.0",
"browserify-aes": "^1.2.0",
"bs58": "^4.0.1",
"err-code": "^1.1.2",
"iso-random-stream": "^1.1.0",
"keypair": "^1.0.1",
"libp2p-crypto-secp256k1": "~0.4.0",
"multihashing-async": "~0.7.0",
"node-forge": "~0.8.5",
"multihashing-async": "~0.8.0",
"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.0"
"ursa-optional": "~0.10.1"
},
"devDependencies": {
"aegir": "^19.0.5",
"aegir": "^20.4.1",
"benchmark": "^2.1.4",
"bundlesize": "~0.18.0",
"chai": "^4.2.0",
"chai-string": "^1.5.0",
"dirty-chai": "^2.0.1",
"sinon": "^7.3.2"
"sinon": "^7.5.0"
},
"engines": {
"node": ">=10.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/keys/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const ErrMissingSecp256K1 = {
}

function typeToKey (type) {
let key = supportedKeys[type.toLowerCase()]
const key = supportedKeys[type.toLowerCase()]
if (!key) {
const supported = Object.keys(supportedKeys).join(' / ')
throw errcode(new Error(`invalid or unsupported key type ${type}. Must be ${supported}`), 'ERR_UNSUPPORTED_KEY_TYPE')
Expand Down
42 changes: 42 additions & 0 deletions src/keys/jwk2pem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'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)
}

function convert (key, types) {
return types.map(t => base64urlToBigInteger(key[t]))
}

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
}
4 changes: 2 additions & 2 deletions src/keys/key-stretcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ module.exports = async (cipherType, hash, secret) => {
}

if (!hash) {
throw errcode(new Error(`missing hash type`), 'ERR_MISSING_HASH_TYPE')
throw errcode(new Error('missing hash type'), 'ERR_MISSING_HASH_TYPE')
}

const cipherKeySize = cipher.keySize
Expand All @@ -41,7 +41,7 @@ module.exports = async (cipherType, hash, secret) => {
const m = await hmac.create(hash, secret)
let a = await m.digest(seed)

let result = []
const result = []
let j = 0

while (j < resultLength) {
Expand Down
29 changes: 29 additions & 0 deletions src/keys/rsa-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,32 @@ function derivePublicFromPrivate (jwKey) {
['verify']
)
}

/*

RSA encryption/decryption for the browser with webcrypto workarround
"bloody dark magic. webcrypto's why."

Explanation:
- Convert JWK to nodeForge
- Convert msg buffer to nodeForge buffer: ByteBuffer is a "binary-string backed buffer", so let's make our buffer a binary string
- Convert resulting nodeForge buffer to buffer: it returns a binary string, turn that into a uint8array(buffer)

*/

const { jwk2pub, jwk2priv } = require('./jwk2pem')

function convertKey (key, pub, msg, handle) {
const fkey = pub ? jwk2pub(key) : jwk2priv(key)
const fmsg = Buffer.from(msg).toString('binary')
const fomsg = handle(fmsg, fkey)
return Buffer.from(fomsg, 'binary')
}

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))
}
6 changes: 5 additions & 1 deletion src/keys/rsa-class.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class RsaPublicKey {
}

encrypt (bytes) {
return this._key.encrypt(bytes, 'RSAES-PKCS1-V1_5')
return crypto.encrypt(this._key, bytes)
}

equals (key) {
Expand Down Expand Up @@ -68,6 +68,10 @@ class RsaPrivateKey {
return new RsaPublicKey(this._publicKey)
}

decrypt (bytes) {
return crypto.decrypt(this._key, bytes)
}

marshal () {
return crypto.utils.jwkToPkcs1(this._key)
}
Expand Down
10 changes: 10 additions & 0 deletions src/keys/rsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,13 @@ exports.hashAndVerify = async function (key, sig, msg) { // eslint-disable-line
const pem = jwkToPem(key)
return verify.verify(pem, sig)
}

const padding = crypto.constants.RSA_PKCS1_PADDING

exports.encrypt = function (key, bytes) {
return crypto.publicEncrypt({ key: jwkToPem(key), padding }, bytes)
}

exports.decrypt = function (key, bytes) {
return crypto.privateDecrypt({ key: jwkToPem(key), padding }, bytes)
}
2 changes: 1 addition & 1 deletion src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const BN = require('asn1.js').bignum
// 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
let s = bn.toArrayLike(Buffer, 'be', len).toString('base64')
const s = bn.toArrayLike(Buffer, 'be', len).toString('base64')

return s
.replace(/(=*)$/, '') // Remove any trailing '='s
Expand Down
2 changes: 1 addition & 1 deletion test/helpers/test-garbage-error-handling.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function doTests (fncName, fnc, num, skipBuffersAndStrings) {
// skip this garbage because it's a buffer or a string and we were told do do that
return
}
let args = []
const args = []
for (let i = 0; i < num; i++) {
args.push(garbage)
}
Expand Down
2 changes: 1 addition & 1 deletion test/keys/ephemeral-keys.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ describe('generateEphemeralKeyPair', () => {
})
})

it(`handles bad curve name`, async () => {
it('handles bad curve name', async () => {
try {
await crypto.keys.generateEphemeralKeyPair('bad name')
} catch (err) {
Expand Down
20 changes: 20 additions & 0 deletions test/keys/rsa.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,26 @@ describe('RSA', function () {
expect(valid).to.be.eql(true)
})

it('encrypt and decrypt', async () => {
const data = Buffer.from('hello world')
const enc = await key.public.encrypt(data)
const dec = await key.decrypt(enc)
expect(dec).to.be.eql(data)
})

it('encrypt decrypt browser/node interop', async () => {
const id = await crypto.keys.unmarshalPrivateKey(Buffer.from('CAASqAkwggSkAgEAAoIBAQCk0O+6oNRxhcdZe2GxEDrFBkDV4TZFZnp2ly/dL1cGMBql/8oXPZgei6h7+P5zzfDq2YCfwbjbf0IVY1AshRl6B5VGE1WS+9p1y1OZxJf5os6V1ENnTi6FTcyuBl4BN8dmIKOif0hqgqflaT5OhfYZDXfbJyVQj4vb2+Stu2Xpph3nwqAnTw/7GC/7jrt2Cq6Tu1PoZi36wSwEPYW3eQ1HAYxZjTYYDXl2iyHygnTcbkGRwAQ7vjk+mW7u60zyoolCm9f6Y7c/orJ33DDUocbaGJLlHcfd8bioBwaZy/2m7q43X8pQs0Q1/iwUt0HHZj1YARmHKbh0zR31ciFiV37dAgMBAAECggEADtJBNKnA4QKURj47r0YT2uLwkqtBi6UnDyISalQXAdXyl4n0nPlrhBewC5H9I+HZr+zmTbeIjaiYgz7el1pSy7AB4v7bG7AtWZlyx6mvtwHGjR+8/f3AXjl8Vgv5iSeAdXUq8fJ7SyS7v3wi38HZOzCEXj9bci6ud5ODMYJgLE4gZD0+i1+/V9cpuYfGpS/gLTLEMQLiw/9o8NSZ7sAnxg0UlYhotqaQY23hvXPBOe+0oa95zl2n6XTxCafa3dQl/B6CD1tUq9dhbQew4bxqMq/mhRO9pREEqZ083Uh+u4PTc1BeHgIQaS864pHPb+AY1F7KDvPtHhdojnghp8d70QKBgQDeRYFxo6sd04ohY86Z/i9icVYIyCvfXAKnaMKeGUjK7ou6sDJwFX8W97+CzXpZ/vffsk/l5GGhC50KqrITxHAy/h5IjyDODfps7NMIp0Dm9sO4PWibbw3OOVBRc8w3b3i7I8MrUUA1nLHE1T1HA1rKOTz5jYhE0fi9XKiT1ciKOQKBgQC903w+n9y7M7eaMW7Z5/13kZ7PS3HlM681eaPrk8J4J+c6miFF40/8HOsmarS38v0fgTeKkriPz5A7aLzRHhSiOnp350JNM6c3sLwPEs2qx/CRuWWx1rMERatfDdUH6mvlK6QHu0QgSfQR27EO6a6XvVSJXbvFmimjmtIaz/IpxQKBgQDWJ9HYVAGC81abZTaiWK3/A4QJYhQjWNuVwPICsgnYvI4Uib+PDqcs0ffLZ38DRw48kek5bxpBuJbOuDhro1EXUJCNCJpq7jzixituovd9kTRyR3iKii2bDM2+LPwOTXDdnk9lZRugjCEbrPkleq33Ob7uEtfAty4aBTTHe6uEwQKBgQCB+2q8RyMSXNuADhFlzOFXGrOwJm0bEUUMTPrduRQUyt4e1qOqA3klnXe3mqGcxBpnlEe/76/JacvNom6Ikxx16a0qpYRU8OWz0KU1fR6vrrEgV98241k5t6sdL4+MGA1Bo5xyXtzLb1hdUh3vpDwVU2OrnC+To3iXus/b5EBiMQKBgEI1OaBcFiyjgLGEyFKoZbtzH1mdatTExfrAQqCjOVjQByoMpGhHTXwEaosvyYu63Pa8AJPT7juSGaiKYEJFcXO9BiNyVfmQiqSHJcYeuh+fmO9IlHRHgy5xaIIC00AHS2vC/gXwmXAdPis6BZqDJeiCuOLWJ94QXn8JBT8IgGAI', 'base64'))

const msg = Buffer.from('hello')

// browser
const dec1 = await id.decrypt(Buffer.from('YRFUDx8UjbWSfDS84cDA4WowaaOmd1qFNAv5QutodCKYb9uPtU/tDiAvJzOGu5DCJRo2J0l/35P2weiB4/C2Cb1aZgXKMx/QQC+2jSJiymhqcZaYerjTvkCFwkjCaqthoVo/YXxsaFZ1q7bdTZUDH1TaJR7hWfSyzyPcA8c0w43MIsw16pY8ZaPSclvnCwhoTg1JGjMk6te3we7+wR8QU7VrPhs54mZWxrpu3NQ8xZ6xQqIedsEiNhBUccrCSzYghgsP0Ae/8iKyGyl3U6IegsJNn8jcocvzOJrmU03rgIFPjvuBdaqB38xDSTjbA123KadB28jNoSZh18q/yH3ZIg==', 'base64'))
expect(dec1).to.be.eql(msg)
// node
const dec2 = await id.decrypt(Buffer.from('e6yxssqXsWc27ozDy0PGKtMkCS28KwFyES2Ijz89yiz+w6bSFkNOhHPKplpPzgQEuNoUGdbseKlJFyRYHjIT8FQFBHZM8UgSkgoimbY5on4xSxXs7E5/+twjqKdB7oNveTaTf7JCwaeUYnKSjbiYFEawtMiQE91F8sTT7TmSzOZ48tUhnddAAZ3Ac/O3Z9MSAKOCDipi+JdZtXRT8KimGt36/7hjjosYmPuHR1Xy/yMTL6SMbXtBM3yAuEgbQgP+q/7kHMHji3/JvTpYdIUU+LVtkMusXNasRA+UWG2zAht18vqjFMsm9JTiihZw9jRHD4vxAhf75M992tnC+0ZuQg==', 'base64'))
expect(dec2).to.be.eql(msg)
})

it('fails to verify for different data', async () => {
const data = Buffer.from('hello world')
const sig = await key.sign(data)
Expand Down
2 changes: 1 addition & 1 deletion test/util.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('Util', () => {
})

it('toBase64 zero padding', (done) => {
let bnpad = new BN('ff', 16)
const bnpad = new BN('ff', 16)
expect(util.toBase64(bnpad, 2)).to.eql('AP8')
done()
})
Expand Down