This repository has been archived by the owner on Jul 21, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add exporting/importing of non rsa keys in libp2p-key format (#179
) * feat: add exporting/importing of ed25519 keys in libp2p-key format * feat: add libp2p-key export/import support for rsa and secp keys * chore: dep bumps * chore: update aegir * refactor: import and export base64 strings * refactor: simplify api for now * chore: fix lint * refactor: remove extraneous param * refactor: clean up * fix: review patches
- Loading branch information
Showing
16 changed files
with
415 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,9 +6,10 @@ | |
"types": "src/index.d.ts", | ||
"leadMaintainer": "Jacob Heun <[email protected]>", | ||
"browser": { | ||
"./src/aes/ciphers.js": "./src/aes/ciphers-browser.js", | ||
"./src/ciphers/aes-gcm.js": "./src/ciphers/aes-gcm.browser.js", | ||
"./src/hmac/index.js": "./src/hmac/index-browser.js", | ||
"./src/keys/ecdh.js": "./src/keys/ecdh-browser.js", | ||
"./src/aes/ciphers.js": "./src/aes/ciphers-browser.js", | ||
"./src/keys/rsa.js": "./src/keys/rsa-browser.js" | ||
}, | ||
"files": [ | ||
|
@@ -43,21 +44,22 @@ | |
"is-typedarray": "^1.0.0", | ||
"iso-random-stream": "^1.1.0", | ||
"keypair": "^1.0.1", | ||
"multibase": "^0.7.0", | ||
"multibase": "^1.0.1", | ||
"multicodec": "^1.0.4", | ||
"multihashing-async": "^0.8.1", | ||
"node-forge": "^0.9.1", | ||
"pem-jwk": "^2.0.0", | ||
"protons": "^1.0.1", | ||
"protons": "^1.2.1", | ||
"secp256k1": "^4.0.0", | ||
"ursa-optional": "~0.10.1" | ||
"uint8arrays": "^1.0.0", | ||
"ursa-optional": "^0.10.1" | ||
}, | ||
"devDependencies": { | ||
"@types/chai": "^4.2.11", | ||
"@types/chai": "^4.2.12", | ||
"@types/chai-string": "^1.4.2", | ||
"@types/dirty-chai": "^2.0.2", | ||
"@types/mocha": "^7.0.1", | ||
"@types/sinon": "^9.0.0", | ||
"aegir": "^22.0.0", | ||
"@types/mocha": "^8.0.1", | ||
"aegir": "^25.0.0", | ||
"benchmark": "^2.1.4", | ||
"chai": "^4.2.0", | ||
"chai-string": "^1.5.0", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
'use strict' | ||
|
||
const concat = require('uint8arrays/concat') | ||
const fromString = require('uint8arrays/from-string') | ||
|
||
const webcrypto = require('../webcrypto') | ||
|
||
// Based off of code from https://github.com/luke-park/SecureCompatibleEncryptionExamples | ||
|
||
/** | ||
* | ||
* @param {object} [options] | ||
* @param {string} [options.algorithm=AES-GCM] | ||
* @param {Number} [options.nonceLength=12] | ||
* @param {Number} [options.keyLength=16] | ||
* @param {string} [options.digest=sha256] | ||
* @param {Number} [options.saltLength=16] | ||
* @param {Number} [options.iterations=32767] | ||
* @returns {*} | ||
*/ | ||
function create ({ | ||
algorithm = 'AES-GCM', | ||
nonceLength = 12, | ||
keyLength = 16, | ||
digest = 'SHA-256', | ||
saltLength = 16, | ||
iterations = 32767 | ||
} = {}) { | ||
const crypto = webcrypto.get() | ||
keyLength *= 8 // Browser crypto uses bits instead of bytes | ||
|
||
/** | ||
* Uses the provided password to derive a pbkdf2 key. The key | ||
* will then be used to encrypt the data. | ||
* | ||
* @param {Uint8Array} data The data to decrypt | ||
* @param {string} password A plain password | ||
* @returns {Promise<Uint8Array>} | ||
*/ | ||
async function encrypt (data, password) { // eslint-disable-line require-await | ||
const salt = crypto.getRandomValues(new Uint8Array(saltLength)) | ||
const nonce = crypto.getRandomValues(new Uint8Array(nonceLength)) | ||
const aesGcm = { name: algorithm, iv: nonce } | ||
|
||
// Derive a key using PBKDF2. | ||
const deriveParams = { name: 'PBKDF2', salt, iterations, hash: { name: digest } } | ||
const rawKey = await crypto.subtle.importKey('raw', fromString(password), { name: 'PBKDF2' }, false, ['deriveKey', 'deriveBits']) | ||
const cryptoKey = await crypto.subtle.deriveKey(deriveParams, rawKey, { name: algorithm, length: keyLength }, true, ['encrypt']) | ||
|
||
// Encrypt the string. | ||
const ciphertext = await crypto.subtle.encrypt(aesGcm, cryptoKey, data) | ||
return concat([salt, aesGcm.iv, new Uint8Array(ciphertext)]) | ||
} | ||
|
||
/** | ||
* Uses the provided password to derive a pbkdf2 key. The key | ||
* will then be used to decrypt the data. The options used to create | ||
* this decryption cipher must be the same as those used to create | ||
* the encryption cipher. | ||
* | ||
* @param {Uint8Array} data The data to decrypt | ||
* @param {string} password A plain password | ||
* @returns {Promise<Uint8Array>} | ||
*/ | ||
async function decrypt (data, password) { | ||
const salt = data.slice(0, saltLength) | ||
const nonce = data.slice(saltLength, saltLength + nonceLength) | ||
const ciphertext = data.slice(saltLength + nonceLength) | ||
const aesGcm = { name: algorithm, iv: nonce } | ||
|
||
// Derive the key using PBKDF2. | ||
const deriveParams = { name: 'PBKDF2', salt, iterations, hash: { name: digest } } | ||
const rawKey = await crypto.subtle.importKey('raw', fromString(password), { name: 'PBKDF2' }, false, ['deriveKey', 'deriveBits']) | ||
const cryptoKey = await crypto.subtle.deriveKey(deriveParams, rawKey, { name: algorithm, length: keyLength }, true, ['decrypt']) | ||
|
||
// Decrypt the string. | ||
const plaintext = await crypto.subtle.decrypt(aesGcm, cryptoKey, ciphertext) | ||
return new Uint8Array(plaintext) | ||
} | ||
|
||
return { | ||
encrypt, | ||
decrypt | ||
} | ||
} | ||
|
||
module.exports = { | ||
create | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
'use strict' | ||
|
||
const crypto = require('crypto') | ||
|
||
// Based off of code from https://github.com/luke-park/SecureCompatibleEncryptionExamples | ||
|
||
/** | ||
* | ||
* @param {object} [options] | ||
* @param {Number} [options.algorithmTagLength=16] | ||
* @param {Number} [options.nonceLength=12] | ||
* @param {Number} [options.keyLength=16] | ||
* @param {string} [options.digest=sha256] | ||
* @param {Number} [options.saltLength=16] | ||
* @param {Number} [options.iterations=32767] | ||
* @returns {*} | ||
*/ | ||
function create ({ | ||
algorithmTagLength = 16, | ||
nonceLength = 12, | ||
keyLength = 16, | ||
digest = 'sha256', | ||
saltLength = 16, | ||
iterations = 32767 | ||
} = {}) { | ||
const algorithm = 'aes-128-gcm' | ||
/** | ||
* | ||
* @private | ||
* @param {Buffer} data | ||
* @param {Buffer} key | ||
* @returns {Promise<Buffer>} | ||
*/ | ||
async function encryptWithKey (data, key) { // eslint-disable-line require-await | ||
const nonce = crypto.randomBytes(nonceLength) | ||
|
||
// Create the cipher instance. | ||
const cipher = crypto.createCipheriv(algorithm, key, nonce) | ||
|
||
// Encrypt and prepend nonce. | ||
const ciphertext = Buffer.concat([cipher.update(data), cipher.final()]) | ||
|
||
return Buffer.concat([nonce, ciphertext, cipher.getAuthTag()]) | ||
} | ||
|
||
/** | ||
* Uses the provided password to derive a pbkdf2 key. The key | ||
* will then be used to encrypt the data. | ||
* | ||
* @param {Buffer} data The data to decrypt | ||
* @param {string|Buffer} password A plain password | ||
* @returns {Promise<Buffer>} | ||
*/ | ||
async function encrypt (data, password) { // eslint-disable-line require-await | ||
// Generate a 128-bit salt using a CSPRNG. | ||
const salt = crypto.randomBytes(saltLength) | ||
|
||
// Derive a key using PBKDF2. | ||
const key = crypto.pbkdf2Sync(Buffer.from(password), salt, iterations, keyLength, digest) | ||
|
||
// Encrypt and prepend salt. | ||
return Buffer.concat([salt, await encryptWithKey(Buffer.from(data), key)]) | ||
} | ||
|
||
/** | ||
* Decrypts the given cipher text with the provided key. The `key` should | ||
* be a cryptographically safe key and not a plaintext password. To use | ||
* a plaintext password, use `decrypt`. The options used to create | ||
* this decryption cipher must be the same as those used to create | ||
* the encryption cipher. | ||
* | ||
* @private | ||
* @param {Buffer} ciphertextAndNonce The data to decrypt | ||
* @param {Buffer} key | ||
* @returns {Promise<Buffer>} | ||
*/ | ||
async function decryptWithKey (ciphertextAndNonce, key) { // eslint-disable-line require-await | ||
// Create buffers of nonce, ciphertext and tag. | ||
const nonce = ciphertextAndNonce.slice(0, nonceLength) | ||
const ciphertext = ciphertextAndNonce.slice(nonceLength, ciphertextAndNonce.length - algorithmTagLength) | ||
const tag = ciphertextAndNonce.slice(ciphertext.length + nonceLength) | ||
|
||
// Create the cipher instance. | ||
const cipher = crypto.createDecipheriv(algorithm, key, nonce) | ||
|
||
// Decrypt and return result. | ||
cipher.setAuthTag(tag) | ||
return Buffer.concat([cipher.update(ciphertext), cipher.final()]) | ||
} | ||
|
||
/** | ||
* Uses the provided password to derive a pbkdf2 key. The key | ||
* will then be used to decrypt the data. The options used to create | ||
* this decryption cipher must be the same as those used to create | ||
* the encryption cipher. | ||
* | ||
* @param {Buffer} data The data to decrypt | ||
* @param {string|Buffer} password A plain password | ||
*/ | ||
async function decrypt (data, password) { // eslint-disable-line require-await | ||
// Create buffers of salt and ciphertextAndNonce. | ||
const salt = data.slice(0, saltLength) | ||
const ciphertextAndNonce = data.slice(saltLength) | ||
|
||
// Derive the key using PBKDF2. | ||
const key = crypto.pbkdf2Sync(Buffer.from(password), salt, iterations, keyLength, digest) | ||
|
||
// Decrypt and return result. | ||
return decryptWithKey(ciphertextAndNonce, key) | ||
} | ||
|
||
return { | ||
encrypt, | ||
decrypt | ||
} | ||
} | ||
|
||
module.exports = { | ||
create | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.