@digitalbazaar/minimal-cipher
Advanced tools
Comparing version
/*! | ||
* Copyright (c) 2019-2020 Digital Bazaar, Inc. All rights reserved. | ||
* Copyright (c) 2019-2023 Digital Bazaar, Inc. All rights reserved. | ||
*/ | ||
import * as base64url from 'base64url-universal'; | ||
import crypto from '../crypto.js'; | ||
import * as base64url from 'base64url-universal'; | ||
@@ -7,0 +7,0 @@ class Kek { |
/*! | ||
* Copyright (c) 2019-2022 Digital Bazaar, Inc. All rights reserved. | ||
* Copyright (c) 2019-2023 Digital Bazaar, Inc. All rights reserved. | ||
*/ | ||
import {ChaCha20Poly1305, KEY_LENGTH} from '@stablelib/chacha20poly1305'; | ||
import crypto from '../crypto.js'; | ||
import {ChaCha20Poly1305, KEY_LENGTH} from '@stablelib/chacha20poly1305'; | ||
import {streamXOR} from '@stablelib/chacha'; | ||
@@ -7,0 +7,0 @@ |
/*! | ||
* Copyright (c) 2019-2020 Digital Bazaar, Inc. All rights reserved. | ||
* Copyright (c) 2019-2023 Digital Bazaar, Inc. All rights reserved. | ||
*/ | ||
import * as cipher from './a256gcm.js'; | ||
// FIXME: not FIPs compliant yet; replace! | ||
import * as keyAgreement from './x25519.js'; | ||
import * as keyAgreement from './p256.js'; | ||
export {cipher}; | ||
export {keyAgreement}; |
/*! | ||
* Copyright (c) 2019-2022 Digital Bazaar, Inc. All rights reserved. | ||
* Copyright (c) 2019-2023 Digital Bazaar, Inc. All rights reserved. | ||
*/ | ||
import * as base64url from 'base64url-universal'; | ||
import nacl from 'tweetnacl'; | ||
import crypto from '../crypto.js'; | ||
import {x25519} from '@noble/curves/ed25519'; | ||
export async function deriveEphemeralKeyPair() { | ||
export async function generateEphemeralKeyPair() { | ||
// generate X25519 ephemeral public key | ||
const keyPair = nacl.box.keyPair(); | ||
const {secretKey: privateKey, publicKey} = keyPair; | ||
const privateKey = await crypto.getRandomValues(new Uint8Array(32)); | ||
const publicKey = x25519.scalarMultBase(privateKey); | ||
return { | ||
@@ -24,3 +25,3 @@ privateKey, | ||
// `scalarMult` takes secret key as param 1, public key as param 2 | ||
return nacl.scalarMult(privateKey, remotePublicKey); | ||
return x25519.scalarMult(privateKey, remotePublicKey); | ||
} |
/*! | ||
* Copyright (c) 2019-2020 Digital Bazaar, Inc. All rights reserved. | ||
* Copyright (c) 2019-2022 Digital Bazaar, Inc. All rights reserved. | ||
*/ | ||
import * as base64url from 'base64url-universal'; | ||
import nacl from 'tweetnacl'; | ||
import * as crypto from 'node:crypto'; | ||
@@ -20,19 +19,12 @@ import * as util from 'node:util'; | ||
export async function deriveEphemeralKeyPair() { | ||
export async function generateEphemeralKeyPair() { | ||
// generate X25519 ephemeral public key | ||
let keyPair; | ||
if(await _hasNodeDiffieHellman()) { | ||
const publicKeyEncoding = {format: 'der', type: 'spki'}; | ||
const privateKeyEncoding = {format: 'der', type: 'pkcs8'}; | ||
const {publicKey: publicDerBytes, privateKey: privateDerBytes} = | ||
await generateKeyPairAsync('x25519', { | ||
publicKeyEncoding, privateKeyEncoding | ||
}); | ||
const publicKey = publicDerBytes.slice(12, 12 + 32); | ||
const secretKey = privateDerBytes.slice(16, 16 + 32); | ||
keyPair = {secretKey, publicKey}; | ||
} else { | ||
keyPair = nacl.box.keyPair(); | ||
} | ||
const {secretKey: privateKey, publicKey} = keyPair; | ||
const publicKeyEncoding = {format: 'der', type: 'spki'}; | ||
const privateKeyEncoding = {format: 'der', type: 'pkcs8'}; | ||
const {publicKey: publicDerBytes, privateKey: privateDerBytes} = | ||
await generateKeyPairAsync('x25519', { | ||
publicKeyEncoding, privateKeyEncoding | ||
}); | ||
const publicKey = publicDerBytes.slice(12, 12 + 32); | ||
const privateKey = privateDerBytes.slice(16, 16 + 32); | ||
return { | ||
@@ -50,26 +42,16 @@ privateKey, | ||
export async function deriveSecret({privateKey, remotePublicKey}) { | ||
if(await _hasNodeDiffieHellman()) { | ||
const nodePrivateKey = crypto.createPrivateKey({ | ||
key: Buffer.concat([PRIVATE_KEY_DER_PREFIX, privateKey]), | ||
format: 'der', | ||
type: 'pkcs8' | ||
}); | ||
const nodePublicKey = crypto.createPublicKey({ | ||
key: Buffer.concat([PUBLIC_KEY_DER_PREFIX, remotePublicKey]), | ||
format: 'der', | ||
type: 'spki' | ||
}); | ||
return crypto.diffieHellman({ | ||
privateKey: nodePrivateKey, | ||
publicKey: nodePublicKey, | ||
}); | ||
} | ||
// `scalarMult` takes secret key as param 1, public key as param 2 | ||
return nacl.scalarMult(privateKey, remotePublicKey); | ||
const nodePrivateKey = crypto.createPrivateKey({ | ||
key: Buffer.concat([PRIVATE_KEY_DER_PREFIX, privateKey]), | ||
format: 'der', | ||
type: 'pkcs8' | ||
}); | ||
const nodePublicKey = crypto.createPublicKey({ | ||
key: Buffer.concat([PUBLIC_KEY_DER_PREFIX, remotePublicKey]), | ||
format: 'der', | ||
type: 'spki' | ||
}); | ||
return crypto.diffieHellman({ | ||
privateKey: nodePrivateKey, | ||
publicKey: nodePublicKey, | ||
}); | ||
} | ||
async function _hasNodeDiffieHellman() { | ||
// crypto.diffieHellman was added in Node.js v13.9.0 | ||
return !!crypto.diffieHellman; | ||
} |
/*! | ||
* Copyright (c) 2019-2022 Digital Bazaar, Inc. All rights reserved. | ||
* Copyright (c) 2019-2023 Digital Bazaar, Inc. All rights reserved. | ||
*/ | ||
import * as base58btc from 'base58-universal'; | ||
import * as base64url from 'base64url-universal'; | ||
import {deriveSecret, generateEphemeralKeyPair} from './x25519-helper.js'; | ||
import {createKek} from './aeskw.js'; | ||
import * as base58btc from 'base58-universal'; | ||
import {deriveKey} from './ecdhkdf.js'; | ||
import {deriveSecret, deriveEphemeralKeyPair} from './x25519-helper.js'; | ||
@@ -16,3 +16,3 @@ const KEY_TYPE = 'X25519KeyAgreementKey2020'; | ||
export const JWE_ALG = 'ECDH-ES+A256KW'; | ||
export {deriveEphemeralKeyPair, deriveSecret}; | ||
export {generateEphemeralKeyPair, deriveSecret}; | ||
@@ -66,2 +66,3 @@ // Decryption case: get Kek from a private key agreement key and a | ||
* }} kekObject | ||
* | ||
* @returns {Promise<kekObject>} - Resolves with kek object derived from static | ||
@@ -74,8 +75,3 @@ * peer. | ||
} | ||
const {privateKey} = ephemeralKeyPair; | ||
// TODO: consider accepting JWK format for `staticPublicKey` not just LD key | ||
if(staticPublicKey.type !== KEY_TYPE) { | ||
throw new Error( | ||
`"staticPublicKey.type" must be "${KEY_TYPE}".`); | ||
} | ||
// static key must be an X25519 multikey | ||
const remotePublicKey = multibaseDecode( | ||
@@ -89,2 +85,3 @@ MULTICODEC_X25519_PUB_HEADER, staticPublicKey.publicKeyMultibase); | ||
const consumerInfo = encoder.encode(staticPublicKey.id); | ||
const {privateKey} = ephemeralKeyPair; | ||
const secret = await deriveSecret({privateKey, remotePublicKey}); | ||
@@ -107,2 +104,3 @@ const keyData = await deriveKey({secret, producerInfo, consumerInfo}); | ||
* @param {Uint8Array} bytes - Byte array representing a public or private key. | ||
* | ||
* @returns {string} Base58-btc encoded key (with multicodec prefix). | ||
@@ -122,6 +120,7 @@ */ | ||
* @param {string} text - Multibase encoded string to decode. | ||
* | ||
* @returns {Uint8Array} Decoded bytes. | ||
*/ | ||
export function multibaseDecode(header, text) { | ||
const mcValue = base58btc.decode(text.substr(1)); | ||
const mcValue = base58btc.decode(text.slice(1)); | ||
@@ -128,0 +127,0 @@ if(!header.every((val, i) => mcValue[i] === val)) { |
/*! | ||
* Copyright (c) 2019-2022 Digital Bazaar, Inc. All rights reserved. | ||
* Copyright (c) 2019-2023 Digital Bazaar, Inc. All rights reserved. | ||
*/ | ||
import {_chacha20, decrypt as _decrypt, _encrypt} from './c20p.js'; | ||
import crypto from '../crypto.js'; | ||
import {_encrypt, decrypt as _decrypt, _chacha20} from './c20p.js'; | ||
@@ -7,0 +7,0 @@ // constants are based on the string: "expand 32-byte k" |
/*! | ||
* Copyright (c) 2019-2022 Digital Bazaar, Inc. All rights reserved. | ||
* Copyright (c) 2019-2023 Digital Bazaar, Inc. All rights reserved. | ||
*/ | ||
// TODO: Remove when TransformStream is recognized properly. | ||
/* eslint-disable no-undef, jsdoc/no-undefined-types */ | ||
import * as base64url from 'base64url-universal'; | ||
import {stringToUint8Array} from './util.js'; | ||
import * as fipsAlgorithm from './algorithms/fips.js'; | ||
import * as recAlgorithm from './algorithms/recommended.js'; | ||
import {DecryptTransformer} from './DecryptTransformer.js'; | ||
import {EncryptTransformer} from './EncryptTransformer.js'; | ||
import * as fipsAlgorithm from './algorithms/fips.js'; | ||
import * as recAlgorithm from './algorithms/recommended.js'; | ||
import {stringToUint8Array} from './util.js'; | ||
@@ -70,2 +67,3 @@ const VERSIONS = ['recommended', 'fips']; | ||
{recipients, keyResolver, chunkSize}); | ||
// eslint-disable-next-line no-undef | ||
return new TransformStream(transformer); | ||
@@ -96,2 +94,3 @@ } | ||
{keyAgreementKey}); | ||
// eslint-disable-next-line no-undef | ||
return new TransformStream(transformer); | ||
@@ -233,3 +232,3 @@ } | ||
// derive ephemeral ECDH key pair to use with all recipients | ||
const ephemeralKeyPair = await keyAgreement.deriveEphemeralKeyPair(); | ||
const ephemeralKeyPair = await keyAgreement.generateEphemeralKeyPair(); | ||
@@ -328,1 +327,7 @@ recipients = await Promise.all(recipients.map( | ||
} | ||
/** | ||
* See: https://streams.spec.whatwg.org/#ts-model . | ||
* | ||
* @typedef TransformStream | ||
*/ |
{ | ||
"name": "@digitalbazaar/minimal-cipher", | ||
"version": "5.1.1", | ||
"description": "Minimal encryption/decryption JWE/CWE library.", | ||
"version": "6.0.0", | ||
"description": "Minimal encryption/decryption JWE library.", | ||
"license": "BSD-3-Clause", | ||
@@ -26,31 +26,34 @@ "type": "module", | ||
"dependencies": { | ||
"@digitalbazaar/ecdsa-multikey": "^1.5.0", | ||
"@noble/curves": "^1.2.0", | ||
"@stablelib/chacha": "^1.0.1", | ||
"@stablelib/chacha20poly1305": "^1.0.1", | ||
"base58-universal": "^2.0.0", | ||
"base64url-universal": "^2.0.0", | ||
"tweetnacl": "^1.0.3" | ||
"base64url-universal": "^2.0.0" | ||
}, | ||
"devDependencies": { | ||
"@digitalbazaar/did-io": "^2.0.0", | ||
"@digitalbazaar/did-method-key": "^3.0.0", | ||
"@digitalbazaar/x25519-key-agreement-key-2020": "^3.0.0", | ||
"c8": "^7.11.3", | ||
"chai": "^4.3.6", | ||
"@digitalbazaar/did-method-key": "^5.1.0", | ||
"@digitalbazaar/ed25519-verification-key-2020": "^4.1.0", | ||
"@digitalbazaar/x25519-key-agreement-key-2020": "^3.0.1", | ||
"@es-joy/jsdoccomment": "^0.40.1", | ||
"c8": "^8.0.1", | ||
"chai": "^4.3.10", | ||
"cross-env": "^7.0.3", | ||
"eslint": "^8.17.0", | ||
"eslint-config-digitalbazaar": "^3.0.0", | ||
"eslint-plugin-jsdoc": "^39.3.2", | ||
"eslint-plugin-unicorn": "^42.0.0", | ||
"isomorphic-webcrypto": "^2.3.8", | ||
"karma": "^6.3.20", | ||
"eslint": "^8.52.0", | ||
"eslint-config-digitalbazaar": "^5.0.1", | ||
"eslint-plugin-jsdoc": "^46.8.2", | ||
"eslint-plugin-unicorn": "^49.0.0", | ||
"karma": "^6.4.2", | ||
"karma-chai": "^0.1.0", | ||
"karma-chrome-launcher": "^3.1.1", | ||
"karma-chrome-launcher": "^3.2.0", | ||
"karma-mocha": "^2.0.1", | ||
"karma-mocha-reporter": "^2.2.5", | ||
"karma-sourcemap-loader": "^0.3.8", | ||
"karma-sourcemap-loader": "^0.4.0", | ||
"karma-webpack": "^5.0.0", | ||
"mocha": "^10.0.0", | ||
"mocha": "^10.2.0", | ||
"mocha-lcov-reporter": "^1.3.0", | ||
"tweetnacl": "^1.0.3", | ||
"web-streams-polyfill": "^3.2.1", | ||
"webpack": "^5.73.0" | ||
"webpack": "^5.89.0" | ||
}, | ||
@@ -88,4 +91,4 @@ "repository": { | ||
"engines": { | ||
"node": ">=14" | ||
"node": ">=18" | ||
} | ||
} |
# Minimal Cipher _(@digitalbazaar/minimal-cipher)_ | ||
> Minimal encryption/decryption [JWE](https://tools.ietf.org/html/rfc7516)/[CWE](https://tools.ietf.org/html/rfc8152) library, secure algs only, browser-compatible | ||
Minimal encryption/decryption [JWE](https://tools.ietf.org/html/rfc7516) | ||
library, secure algs only, browser-compatible. | ||
@@ -5,0 +6,0 @@ ## Table of Contents |
65280
5.15%22
4.76%1436
5.9%195
0.52%6
20%24
9.09%+ Added
+ Added
+ Added
+ Added
- Removed
- Removed