Import legacy ciphers (CAST5, TwoFish, BlowFish, DES) only on demand

This primarily affects the lightweight build, which will not include these
(fairly large) modules in the main bundle file.
This commit is contained in:
larabr 2024-02-02 17:22:31 +01:00
parent 3320eaccb2
commit 057f5fe33b
15 changed files with 146 additions and 149 deletions

View File

@ -21,7 +21,7 @@
* @module crypto/aes_kw * @module crypto/aes_kw
*/ */
import * as cipher from './cipher'; import { getCipher } from './cipher';
import util from '../util'; import util from '../util';
/** /**
@ -31,8 +31,9 @@ import util from '../util';
* @param {Uint8Array} data * @param {Uint8Array} data
* @returns {Uint8Array} * @returns {Uint8Array}
*/ */
export function wrap(key, data) { export async function wrap(algo, key, data) {
const aes = new cipher['aes' + (key.length * 8)](key); const Cipher = await getCipher(algo);
const aes = new Cipher(key);
const IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]); const IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]);
const P = unpack(data); const P = unpack(data);
let A = IV; let A = IV;
@ -71,8 +72,9 @@ export function wrap(key, data) {
* @returns {Uint8Array} * @returns {Uint8Array}
* @throws {Error} * @throws {Error}
*/ */
export function unwrap(key, data) { export async function unwrap(algo, key, data) {
const aes = new cipher['aes' + (key.length * 8)](key); const Cipher = await getCipher(algo);
const aes = new Cipher(key);
const IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]); const IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]);
const C = unpack(data); const C = unpack(data);
let A = C.subarray(0, 2); let A = C.subarray(0, 2);

View File

@ -1,13 +0,0 @@
import * as cipher from '.';
import enums from '../../enums';
/**
* Get implementation of the given cipher
* @param {enums.symmetric} algo
* @returns {Object}
* @throws {Error} on invalid algo
*/
export default function getCipher(algo) {
const algoName = enums.read(enums.symmetric, algo);
return cipher[algoName];
}

View File

@ -1,80 +1,78 @@
/** import aes from './aes'; // can be imported dynamically once Web Crypto is used for AES-KW too
* @fileoverview Symmetric cryptography functions import enums from '../../enums';
* @module crypto/cipher
*/
import aes from './aes'; export async function getCipher(algo) {
import { DES, TripleDES } from './des'; switch (algo) {
import CAST5 from './cast5'; case enums.symmetric.aes128:
import TF from './twofish'; case enums.symmetric.aes192:
import BF from './blowfish'; case enums.symmetric.aes256:
return aes(getCipherKeySize(algo));
case enums.symmetric.tripledes: {
const { TripleDES } = await import('./des');
return TripleDES;
}
case enums.symmetric.cast5: {
const { default: CAST5 } = await import('./cast5');
return CAST5;
}
case enums.symmetric.twofish: {
const { default: TwoFish } = await import('./twofish');
return TwoFish;
}
case enums.symmetric.blowfish: {
const { default: BlowFish } = await import('./blowfish');
return BlowFish;
}
default:
throw new Error('Unsupported symmetric-key algorithm');
}
}
/** /**
* AES-128 encryption and decryption (ID 7) * Get block size for given cipher algo
* @function * @param {module:enums.symmetric} algo - alrogithm identifier
* @param {String} key - 128-bit key
* @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
* @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
* @returns {Object}
*/ */
export const aes128 = aes(128); function getCipherBlockSize(algo) {
switch (algo) {
case enums.symmetric.aes128:
case enums.symmetric.aes192:
case enums.symmetric.aes256:
case enums.symmetric.twofish:
return 16;
case enums.symmetric.blowfish:
case enums.symmetric.cast5:
case enums.symmetric.tripledes:
return 8;
default:
throw new Error('Unsupported cipher');
}
}
/** /**
* AES-128 Block Cipher (ID 8) * Get key size for given cipher algo
* @function * @param {module:enums.symmetric} algo - alrogithm identifier
* @param {String} key - 192-bit key
* @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
* @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
* @returns {Object}
*/ */
export const aes192 = aes(192); function getCipherKeySize(algo) {
switch (algo) {
case enums.symmetric.aes128:
case enums.symmetric.blowfish:
case enums.symmetric.cast5:
return 16;
case enums.symmetric.aes192:
case enums.symmetric.tripledes:
return 24;
case enums.symmetric.aes256:
case enums.symmetric.twofish:
return 32;
default:
throw new Error('Unsupported cipher');
}
}
/** /**
* AES-128 Block Cipher (ID 9) * Get block and key size for given cipher algo
* @function * @param {module:enums.symmetric} algo - alrogithm identifier
* @param {String} key - 256-bit key
* @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
* @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
* @returns {Object}
*/ */
export const aes256 = aes(256); export function getCipherParams(algo) {
// Not in OpenPGP specifications return { keySize: getCipherKeySize(algo), blockSize: getCipherBlockSize(algo) };
export const des = DES; }
/**
* Triple DES Block Cipher (ID 2)
* @function
* @param {String} key - 192-bit key
* @see {@link https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-67r2.pdf|NIST SP 800-67}
* @returns {Object}
*/
export const tripledes = TripleDES;
/**
* CAST-128 Block Cipher (ID 3)
* @function
* @param {String} key - 128-bit key
* @see {@link https://tools.ietf.org/html/rfc2144|The CAST-128 Encryption Algorithm}
* @returns {Object}
*/
export const cast5 = CAST5;
/**
* Twofish Block Cipher (ID 10)
* @function
* @param {String} key - 256-bit key
* @see {@link https://tools.ietf.org/html/rfc4880#ref-TWOFISH|TWOFISH}
* @returns {Object}
*/
export const twofish = TF;
/**
* Blowfish Block Cipher (ID 4)
* @function
* @param {String} key - 128-bit key
* @see {@link https://tools.ietf.org/html/rfc4880#ref-BLOWFISH|BLOWFISH}
* @returns {Object}
*/
export const blowfish = BF;
/**
* Not implemented
* @function
* @throws {Error}
*/
export const idea = function() {
throw new Error('IDEA symmetric-key algorithm not implemented');
};

View File

@ -26,7 +26,7 @@
import publicKey from './public_key'; import publicKey from './public_key';
import mode from './mode'; import mode from './mode';
import { getRandomBytes } from './random'; import { getRandomBytes } from './random';
import getCipher from './cipher/getCipher'; import { getCipher, getCipherParams } from './cipher';
import ECDHSymkey from '../type/ecdh_symkey'; import ECDHSymkey from '../type/ecdh_symkey';
import KDFParams from '../type/kdf_params'; import KDFParams from '../type/kdf_params';
import enums from '../enums'; import enums from '../enums';
@ -442,7 +442,7 @@ export async function validateParams(algo, publicParams, privateParams) {
* @async * @async
*/ */
export async function getPrefixRandom(algo) { export async function getPrefixRandom(algo) {
const { blockSize } = getCipher(algo); const { blockSize } = getCipherParams(algo);
const prefixrandom = await getRandomBytes(blockSize); const prefixrandom = await getRandomBytes(blockSize);
const repeat = new Uint8Array([prefixrandom[prefixrandom.length - 2], prefixrandom[prefixrandom.length - 1]]); const repeat = new Uint8Array([prefixrandom[prefixrandom.length - 2], prefixrandom[prefixrandom.length - 1]]);
return util.concat([prefixrandom, repeat]); return util.concat([prefixrandom, repeat]);
@ -455,7 +455,7 @@ export async function getPrefixRandom(algo) {
* @returns {Uint8Array} Random bytes as a string to be used as a key. * @returns {Uint8Array} Random bytes as a string to be used as a key.
*/ */
export function generateSessionKey(algo) { export function generateSessionKey(algo) {
const { keySize } = getCipher(algo); const { keySize } = getCipherParams(algo);
return getRandomBytes(keySize); return getRandomBytes(keySize);
} }
@ -470,8 +470,6 @@ export function getAEADMode(algo) {
return mode[algoName]; return mode[algoName];
} }
export { getCipher };
/** /**
* Check whether the given curve OID is supported * Check whether the given curve OID is supported
* @param {module:type/oid} oid - EC object identifier * @param {module:type/oid} oid - EC object identifier
@ -524,3 +522,6 @@ export function getPreferredCurveHashAlgo(algo, oid) {
throw new Error('Unknown elliptic signing algo'); throw new Error('Unknown elliptic signing algo');
} }
} }
export { getCipher, getCipherParams };

View File

@ -23,9 +23,9 @@
import { AES_CFB } from '@openpgp/asmcrypto.js/aes/cfb.js'; import { AES_CFB } from '@openpgp/asmcrypto.js/aes/cfb.js';
import * as stream from '@openpgp/web-stream-tools'; import * as stream from '@openpgp/web-stream-tools';
import getCipher from '../cipher/getCipher';
import util from '../../util'; import util from '../../util';
import enums from '../../enums'; import enums from '../../enums';
import { getCipher, getCipherParams } from '../cipher';
const webCrypto = util.getWebCrypto(); const webCrypto = util.getWebCrypto();
const nodeCrypto = util.getNodeCrypto(); const nodeCrypto = util.getNodeCrypto();
@ -60,7 +60,7 @@ export async function encrypt(algo, key, plaintext, iv, config) {
return aesEncrypt(algo, key, plaintext, iv, config); return aesEncrypt(algo, key, plaintext, iv, config);
} }
const Cipher = getCipher(algo); const Cipher = await getCipher(algo);
const cipherfn = new Cipher(key); const cipherfn = new Cipher(key);
const block_size = cipherfn.blockSize; const block_size = cipherfn.blockSize;
@ -103,7 +103,7 @@ export async function decrypt(algo, key, ciphertext, iv) {
return aesDecrypt(algo, key, ciphertext, iv); return aesDecrypt(algo, key, ciphertext, iv);
} }
const Cipher = getCipher(algo); const Cipher = await getCipher(algo);
const cipherfn = new Cipher(key); const cipherfn = new Cipher(key);
const block_size = cipherfn.blockSize; const block_size = cipherfn.blockSize;
@ -131,7 +131,7 @@ export async function decrypt(algo, key, ciphertext, iv) {
class WebCryptoEncryptor { class WebCryptoEncryptor {
constructor(algo, key, iv) { constructor(algo, key, iv) {
const { blockSize } = getCipher(algo); const { blockSize } = getCipherParams(algo);
this.key = key; this.key = key;
this.prevBlock = iv; this.prevBlock = iv;
this.nextBlock = new Uint8Array(blockSize); this.nextBlock = new Uint8Array(blockSize);
@ -141,7 +141,7 @@ class WebCryptoEncryptor {
} }
static async isSupported(algo) { static async isSupported(algo) {
const { keySize } = getCipher(algo); const { keySize } = getCipherParams(algo);
return webCrypto.importKey('raw', new Uint8Array(keySize), 'aes-cbc', false, ['encrypt']) return webCrypto.importKey('raw', new Uint8Array(keySize), 'aes-cbc', false, ['encrypt'])
.then(() => true, () => false); .then(() => true, () => false);
} }

View File

@ -20,9 +20,8 @@
* @module crypto/mode/ocb * @module crypto/mode/ocb
*/ */
import * as ciphers from '../cipher'; import { getCipher } from '../cipher';
import util from '../../util'; import util from '../../util';
import enums from '../../enums';
const blockLength = 16; const blockLength = 16;
const ivLength = 15; const ivLength = 15;
@ -68,11 +67,11 @@ async function OCB(cipher, key) {
let decipher; let decipher;
let mask; let mask;
constructKeyVariables(cipher, key); await constructKeyVariables(cipher, key);
function constructKeyVariables(cipher, key) { async function constructKeyVariables(cipher, key) {
const cipherName = enums.read(enums.symmetric, cipher); const Cipher = await getCipher(cipher);
const aes = new ciphers[cipherName](key); const aes = new Cipher(key);
encipher = aes.encrypt.bind(aes); encipher = aes.encrypt.bind(aes);
decipher = aes.decrypt.bind(aes); decipher = aes.decrypt.bind(aes);

View File

@ -29,7 +29,7 @@ import enums from '../../../enums';
import util from '../../../util'; import util from '../../../util';
import { b64ToUint8Array } from '../../../encoding/base64'; import { b64ToUint8Array } from '../../../encoding/base64';
import * as pkcs5 from '../../pkcs5'; import * as pkcs5 from '../../pkcs5';
import getCipher from '../../cipher/getCipher'; import { getCipherParams } from '../../cipher';
const webCrypto = util.getWebCrypto(); const webCrypto = util.getWebCrypto();
const nodeCrypto = util.getNodeCrypto(); const nodeCrypto = util.getNodeCrypto();
@ -133,9 +133,9 @@ export async function encrypt(oid, kdfParams, data, Q, fingerprint) {
const curve = new CurveWithOID(oid); const curve = new CurveWithOID(oid);
const { publicKey, sharedKey } = await genPublicEphemeralKey(curve, Q); const { publicKey, sharedKey } = await genPublicEphemeralKey(curve, Q);
const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint); const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint);
const { keySize } = getCipher(kdfParams.cipher); const { keySize } = getCipherParams(kdfParams.cipher);
const Z = await kdf(kdfParams.hash, sharedKey, keySize, param); const Z = await kdf(kdfParams.hash, sharedKey, keySize, param);
const wrappedKey = aesKW.wrap(Z, m); const wrappedKey = await aesKW.wrap(kdfParams.cipher, Z, m);
return { publicKey, wrappedKey }; return { publicKey, wrappedKey };
} }
@ -195,13 +195,13 @@ export async function decrypt(oid, kdfParams, V, C, Q, d, fingerprint) {
const curve = new CurveWithOID(oid); const curve = new CurveWithOID(oid);
const { sharedKey } = await genPrivateEphemeralKey(curve, V, Q, d); const { sharedKey } = await genPrivateEphemeralKey(curve, V, Q, d);
const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint); const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint);
const { keySize } = getCipher(kdfParams.cipher); const { keySize } = getCipherParams(kdfParams.cipher);
let err; let err;
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
try { try {
// Work around old go crypto bug and old OpenPGP.js bug, respectively. // Work around old go crypto bug and old OpenPGP.js bug, respectively.
const Z = await kdf(kdfParams.hash, sharedKey, keySize, param, i === 1, i === 2); const Z = await kdf(kdfParams.hash, sharedKey, keySize, param, i === 1, i === 2);
return pkcs5.decode(aesKW.unwrap(Z, C)); return pkcs5.decode(await aesKW.unwrap(kdfParams.cipher, Z, C));
} catch (e) { } catch (e) {
err = e; err = e;
} }

View File

@ -9,8 +9,8 @@ import { getRandomBytes } from '../../random';
import enums from '../../../enums'; import enums from '../../../enums';
import util from '../../../util'; import util from '../../../util';
import getCipher from '../../cipher/getCipher';
import computeHKDF from '../../hkdf'; import computeHKDF from '../../hkdf';
import { getCipherParams } from '../../cipher';
const HKDF_INFO = { const HKDF_INFO = {
x25519: util.encodeUTF8('OpenPGP X25519'), x25519: util.encodeUTF8('OpenPGP X25519'),
@ -97,9 +97,10 @@ export async function encrypt(algo, data, recipientA) {
recipientA, recipientA,
sharedSecret sharedSecret
]); ]);
const { keySize } = getCipher(enums.symmetric.aes128); const cipherAlgo = enums.symmetric.aes128;
const { keySize } = getCipherParams(cipherAlgo);
const encryptionKey = await computeHKDF(enums.hash.sha256, hkdfInput, new Uint8Array(), HKDF_INFO.x25519, keySize); const encryptionKey = await computeHKDF(enums.hash.sha256, hkdfInput, new Uint8Array(), HKDF_INFO.x25519, keySize);
const wrappedKey = aesKW.wrap(encryptionKey, data); const wrappedKey = await aesKW.wrap(cipherAlgo, encryptionKey, data);
return { ephemeralPublicKey, wrappedKey }; return { ephemeralPublicKey, wrappedKey };
} }
case enums.publicKey.x448: { case enums.publicKey.x448: {
@ -112,9 +113,10 @@ export async function encrypt(algo, data, recipientA) {
recipientA, recipientA,
sharedSecret sharedSecret
]); ]);
const { keySize } = getCipher(enums.symmetric.aes256); const cipherAlgo = enums.symmetric.aes256;
const { keySize } = getCipherParams(enums.symmetric.aes256);
const encryptionKey = await computeHKDF(enums.hash.sha512, hkdfInput, new Uint8Array(), HKDF_INFO.x448, keySize); const encryptionKey = await computeHKDF(enums.hash.sha512, hkdfInput, new Uint8Array(), HKDF_INFO.x448, keySize);
const wrappedKey = aesKW.wrap(encryptionKey, data); const wrappedKey = await aesKW.wrap(cipherAlgo, encryptionKey, data);
return { ephemeralPublicKey, wrappedKey }; return { ephemeralPublicKey, wrappedKey };
} }
@ -143,9 +145,10 @@ export async function decrypt(algo, ephemeralPublicKey, wrappedKey, A, k) {
A, A,
sharedSecret sharedSecret
]); ]);
const { keySize } = getCipher(enums.symmetric.aes128); const cipherAlgo = enums.symmetric.aes128;
const { keySize } = getCipherParams(cipherAlgo);
const encryptionKey = await computeHKDF(enums.hash.sha256, hkdfInput, new Uint8Array(), HKDF_INFO.x25519, keySize); const encryptionKey = await computeHKDF(enums.hash.sha256, hkdfInput, new Uint8Array(), HKDF_INFO.x25519, keySize);
return aesKW.unwrap(encryptionKey, wrappedKey); return aesKW.unwrap(cipherAlgo, encryptionKey, wrappedKey);
} }
case enums.publicKey.x448: { case enums.publicKey.x448: {
const x448 = await util.getNobleCurve(enums.publicKey.x448); const x448 = await util.getNobleCurve(enums.publicKey.x448);
@ -155,9 +158,10 @@ export async function decrypt(algo, ephemeralPublicKey, wrappedKey, A, k) {
A, A,
sharedSecret sharedSecret
]); ]);
const { keySize } = getCipher(enums.symmetric.aes256); const cipherAlgo = enums.symmetric.aes256;
const { keySize } = getCipherParams(enums.symmetric.aes256);
const encryptionKey = await computeHKDF(enums.hash.sha512, hkdfInput, new Uint8Array(), HKDF_INFO.x448, keySize); const encryptionKey = await computeHKDF(enums.hash.sha512, hkdfInput, new Uint8Array(), HKDF_INFO.x448, keySize);
return aesKW.unwrap(encryptionKey, wrappedKey); return aesKW.unwrap(cipherAlgo, encryptionKey, wrappedKey);
} }
default: default:
throw new Error('Unsupported ECDH algorithm'); throw new Error('Unsupported ECDH algorithm');

View File

@ -174,7 +174,7 @@ class SecretKeyPacket extends PublicKeyPacket {
if (this.s2kUsage !== 253 || this.isLegacyAEAD) { if (this.s2kUsage !== 253 || this.isLegacyAEAD) {
this.iv = bytes.subarray( this.iv = bytes.subarray(
i, i,
i + crypto.getCipher(this.symmetric).blockSize i + crypto.getCipherParams(this.symmetric).blockSize
); );
this.usedModernAEAD = false; this.usedModernAEAD = false;
} else { } else {
@ -388,7 +388,7 @@ class SecretKeyPacket extends PublicKeyPacket {
const cleartext = crypto.serializeParams(this.algorithm, this.privateParams); const cleartext = crypto.serializeParams(this.algorithm, this.privateParams);
this.symmetric = enums.symmetric.aes256; this.symmetric = enums.symmetric.aes256;
const { blockSize } = crypto.getCipher(this.symmetric); const { blockSize } = crypto.getCipherParams(this.symmetric);
if (config.aeadProtect) { if (config.aeadProtect) {
this.s2kUsage = 253; this.s2kUsage = 253;
@ -568,7 +568,7 @@ class SecretKeyPacket extends PublicKeyPacket {
* @returns encryption key * @returns encryption key
*/ */
async function produceEncryptionKey(keyVersion, s2k, passphrase, cipherAlgo, aeadMode, serializedPacketTag, isLegacyAEAD) { async function produceEncryptionKey(keyVersion, s2k, passphrase, cipherAlgo, aeadMode, serializedPacketTag, isLegacyAEAD) {
const { keySize } = crypto.getCipher(cipherAlgo); const { keySize } = crypto.getCipherParams(cipherAlgo);
const derivedKey = await s2k.produceKey(passphrase, keySize); const derivedKey = await s2k.produceKey(passphrase, keySize);
if (!aeadMode || keyVersion === 5 || isLegacyAEAD) { if (!aeadMode || keyVersion === 5 || isLegacyAEAD) {
return derivedKey; return derivedKey;

View File

@ -138,7 +138,7 @@ class SymEncryptedIntegrityProtectedDataPacket {
this.chunkSizeByte = config.aeadChunkSizeByte; this.chunkSizeByte = config.aeadChunkSizeByte;
this.encrypted = await runAEAD(this, 'encrypt', key, bytes); this.encrypted = await runAEAD(this, 'encrypt', key, bytes);
} else { } else {
const { blockSize } = crypto.getCipher(sessionKeyAlgorithm); const { blockSize } = crypto.getCipherParams(sessionKeyAlgorithm);
const prefix = await crypto.getPrefixRandom(sessionKeyAlgorithm); const prefix = await crypto.getPrefixRandom(sessionKeyAlgorithm);
const mdc = new Uint8Array([0xD3, 0x14]); // modification detection code packet const mdc = new Uint8Array([0xD3, 0x14]); // modification detection code packet
@ -169,7 +169,7 @@ class SymEncryptedIntegrityProtectedDataPacket {
if (this.version === 2) { if (this.version === 2) {
packetbytes = await runAEAD(this, 'decrypt', key, encrypted); packetbytes = await runAEAD(this, 'decrypt', key, encrypted);
} else { } else {
const { blockSize } = crypto.getCipher(sessionKeyAlgorithm); const { blockSize } = crypto.getCipherParams(sessionKeyAlgorithm);
const decrypted = await crypto.mode.cfb.decrypt(sessionKeyAlgorithm, key, encrypted, new Uint8Array(blockSize)); const decrypted = await crypto.mode.cfb.decrypt(sessionKeyAlgorithm, key, encrypted, new Uint8Array(blockSize));
// there must be a modification detection code packet as the // there must be a modification detection code packet as the
@ -231,7 +231,7 @@ export async function runAEAD(packet, fn, key, data) {
let iv; let iv;
let ivView; let ivView;
if (isSEIPDv2) { if (isSEIPDv2) {
const { keySize } = crypto.getCipher(packet.cipherAlgorithm); const { keySize } = crypto.getCipherParams(packet.cipherAlgorithm);
const { ivLength } = mode; const { ivLength } = mode;
const info = new Uint8Array(adataBuffer, 0, 5); const info = new Uint8Array(adataBuffer, 0, 5);
const derived = await computeHKDF(enums.hash.sha256, key, packet.salt, info, keySize + ivLength); const derived = await computeHKDF(enums.hash.sha256, key, packet.salt, info, keySize + ivLength);

View File

@ -163,7 +163,7 @@ class SymEncryptedSessionKeyPacket {
this.sessionKeyEncryptionAlgorithm : this.sessionKeyEncryptionAlgorithm :
this.sessionKeyAlgorithm; this.sessionKeyAlgorithm;
const { blockSize, keySize } = crypto.getCipher(algo); const { blockSize, keySize } = crypto.getCipherParams(algo);
const key = await this.s2k.produceKey(passphrase, keySize); const key = await this.s2k.produceKey(passphrase, keySize);
if (this.version >= 5) { if (this.version >= 5) {
@ -199,7 +199,7 @@ class SymEncryptedSessionKeyPacket {
this.s2k = newS2KFromConfig(config); this.s2k = newS2KFromConfig(config);
this.s2k.generateSalt(); this.s2k.generateSalt();
const { blockSize, keySize } = crypto.getCipher(algo); const { blockSize, keySize } = crypto.getCipherParams(algo);
const key = await this.s2k.produceKey(passphrase, keySize); const key = await this.s2k.produceKey(passphrase, keySize);
if (this.sessionKey === null) { if (this.sessionKey === null) {

View File

@ -86,7 +86,7 @@ class SymmetricallyEncryptedDataPacket {
throw new Error('Message is not authenticated.'); throw new Error('Message is not authenticated.');
} }
const { blockSize } = crypto.getCipher(sessionKeyAlgorithm); const { blockSize } = crypto.getCipherParams(sessionKeyAlgorithm);
const encrypted = await stream.readToEnd(stream.clone(this.encrypted)); const encrypted = await stream.readToEnd(stream.clone(this.encrypted));
const decrypted = await crypto.mode.cfb.decrypt(sessionKeyAlgorithm, key, const decrypted = await crypto.mode.cfb.decrypt(sessionKeyAlgorithm, key,
encrypted.subarray(blockSize + 2), encrypted.subarray(blockSize + 2),
@ -107,7 +107,7 @@ class SymmetricallyEncryptedDataPacket {
*/ */
async encrypt(sessionKeyAlgorithm, key, config = defaultConfig) { async encrypt(sessionKeyAlgorithm, key, config = defaultConfig) {
const data = this.packets.write(); const data = this.packets.write();
const { blockSize } = crypto.getCipher(sessionKeyAlgorithm); const { blockSize } = crypto.getCipherParams(sessionKeyAlgorithm);
const prefix = await crypto.getPrefixRandom(sessionKeyAlgorithm); const prefix = await crypto.getPrefixRandom(sessionKeyAlgorithm);
const FRE = await crypto.mode.cfb.encrypt(sessionKeyAlgorithm, key, prefix, new Uint8Array(blockSize), config); const FRE = await crypto.mode.cfb.encrypt(sessionKeyAlgorithm, key, prefix, new Uint8Array(blockSize), config);

View File

@ -2,41 +2,48 @@ import { expect } from 'chai';
import * as aesKW from '../../src/crypto/aes_kw.js'; import * as aesKW from '../../src/crypto/aes_kw.js';
import util from '../../src/util.js'; import util from '../../src/util.js';
import enums from '../../src/enums.js';
export default () => describe('AES Key Wrap and Unwrap', function () { export default () => describe('AES Key Wrap and Unwrap', function () {
const test_vectors = [ const test_vectors = [
[ [
'128 bits of Key Data with a 128-bit KEK', '128 bits of Key Data with a 128-bit KEK',
enums.symmetric.aes128,
'000102030405060708090A0B0C0D0E0F', '000102030405060708090A0B0C0D0E0F',
'00112233445566778899AABBCCDDEEFF', '00112233445566778899AABBCCDDEEFF',
'1FA68B0A8112B447 AEF34BD8FB5A7B82 9D3E862371D2CFE5' '1FA68B0A8112B447 AEF34BD8FB5A7B82 9D3E862371D2CFE5'
], ],
[ [
'128 bits of Key Data with a 192-bit KEK', '128 bits of Key Data with a 192-bit KEK',
enums.symmetric.aes192,
'000102030405060708090A0B0C0D0E0F1011121314151617', '000102030405060708090A0B0C0D0E0F1011121314151617',
'00112233445566778899AABBCCDDEEFF', '00112233445566778899AABBCCDDEEFF',
'96778B25AE6CA435 F92B5B97C050AED2 468AB8A17AD84E5D' '96778B25AE6CA435 F92B5B97C050AED2 468AB8A17AD84E5D'
], ],
[ [
'128 bits of Key Data with a 256-bit KEK', '128 bits of Key Data with a 256-bit KEK',
enums.symmetric.aes256,
'000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F', '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F',
'00112233445566778899AABBCCDDEEFF', '00112233445566778899AABBCCDDEEFF',
'64E8C3F9CE0F5BA2 63E9777905818A2A 93C8191E7D6E8AE7' '64E8C3F9CE0F5BA2 63E9777905818A2A 93C8191E7D6E8AE7'
], ],
[ [
'192 bits of Key Data with a 192-bit KEK', '192 bits of Key Data with a 192-bit KEK',
enums.symmetric.aes192,
'000102030405060708090A0B0C0D0E0F1011121314151617', '000102030405060708090A0B0C0D0E0F1011121314151617',
'00112233445566778899AABBCCDDEEFF0001020304050607', '00112233445566778899AABBCCDDEEFF0001020304050607',
'031D33264E15D332 68F24EC260743EDC E1C6C7DDEE725A93 6BA814915C6762D2' '031D33264E15D332 68F24EC260743EDC E1C6C7DDEE725A93 6BA814915C6762D2'
], ],
[ [
'192 bits of Key Data with a 256-bit KEK', '192 bits of Key Data with a 256-bit KEK',
enums.symmetric.aes256,
'000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F', '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F',
'00112233445566778899AABBCCDDEEFF0001020304050607', '00112233445566778899AABBCCDDEEFF0001020304050607',
'A8F9BC1612C68B3F F6E6F4FBE30E71E4 769C8B80A32CB895 8CD5D17D6B254DA1' 'A8F9BC1612C68B3F F6E6F4FBE30E71E4 769C8B80A32CB895 8CD5D17D6B254DA1'
], ],
[ [
'256 bits of Key Data with a 256-bit KEK', '256 bits of Key Data with a 256-bit KEK',
enums.symmetric.aes256,
'000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F', '000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F',
'00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F', '00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F',
'28C9F404C4B810F4 CBCCB35CFB87F826 3F5786E2D80ED326 CBC7F0E71A99F43B FB988B9B7A02DD21' '28C9F404C4B810F4 CBCCB35CFB87F826 3F5786E2D80ED326 CBC7F0E71A99F43B FB988B9B7A02DD21'
@ -44,15 +51,15 @@ export default () => describe('AES Key Wrap and Unwrap', function () {
]; ];
test_vectors.forEach(function(test) { test_vectors.forEach(function(test) {
it(test[0], function(done) { it(test[0], async function() {
const kek = util.hexToUint8Array(test[1]); const algo = test[1];
const input = test[2].replace(/\s/g, ''); const kek = util.hexToUint8Array(test[2]);
const input = test[3].replace(/\s/g, '');
const input_bin = util.uint8ArrayToString(util.hexToUint8Array(input)); const input_bin = util.uint8ArrayToString(util.hexToUint8Array(input));
const output = test[3].replace(/\s/g, ''); const output = test[4].replace(/\s/g, '');
const output_bin = util.uint8ArrayToString(util.hexToUint8Array(output)); const output_bin = util.uint8ArrayToString(util.hexToUint8Array(output));
expect(util.uint8ArrayToHex(aesKW.wrap(kek, input_bin)).toUpperCase()).to.equal(output); expect(util.uint8ArrayToHex(await aesKW.wrap(algo, kek, input_bin)).toUpperCase()).to.equal(output);
expect(util.uint8ArrayToHex(aesKW.unwrap(kek, output_bin)).toUpperCase()).to.equal(input); expect(util.uint8ArrayToHex(await aesKW.unwrap(algo, kek, output_bin)).toUpperCase()).to.equal(input);
done();
}); });
}); });
}); });

View File

@ -1,9 +1,11 @@
import { expect } from 'chai'; import { expect } from 'chai';
import { aes128 as AES128 } from '../../../src/crypto/cipher'; import enums from '../../../src/enums';
import { getCipher } from '../../../src/crypto/cipher';
export default () => describe('AES Rijndael cipher test with test vectors from ecb_tbl.txt', function() { export default () => describe('AES Rijndael cipher test with test vectors from ecb_tbl.txt', function() {
function test_aes(input, key, output) { async function test_aes(input, key, output) {
const AES128 = await getCipher(enums.symmetric.aes128);
const aes = new AES128(new Uint8Array(key)); const aes = new AES128(new Uint8Array(key));
const encrypted = aes.encrypt(new Uint8Array(input)); const encrypted = aes.encrypt(new Uint8Array(input));
@ -61,24 +63,21 @@ export default () => describe('AES Rijndael cipher test with test vectors from e
[[0x08,0x09,0x0A,0x0B,0x0D,0x0E,0x0F,0x10,0x12,0x13,0x14,0x15,0x17,0x18,0x19,0x1A,0x1C,0x1D,0x1E,0x1F,0x21,0x22,0x23,0x24,0x26,0x27,0x28,0x29,0x2B,0x2C,0x2D,0x2E],[0x06,0x9A,0x00,0x7F,0xC7,0x6A,0x45,0x9F,0x98,0xBA,0xF9,0x17,0xFE,0xDF,0x95,0x21],[0x08,0x0E,0x95,0x17,0xEB,0x16,0x77,0x71,0x9A,0xCF,0x72,0x80,0x86,0x04,0x0A,0xE3]], [[0x08,0x09,0x0A,0x0B,0x0D,0x0E,0x0F,0x10,0x12,0x13,0x14,0x15,0x17,0x18,0x19,0x1A,0x1C,0x1D,0x1E,0x1F,0x21,0x22,0x23,0x24,0x26,0x27,0x28,0x29,0x2B,0x2C,0x2D,0x2E],[0x06,0x9A,0x00,0x7F,0xC7,0x6A,0x45,0x9F,0x98,0xBA,0xF9,0x17,0xFE,0xDF,0x95,0x21],[0x08,0x0E,0x95,0x17,0xEB,0x16,0x77,0x71,0x9A,0xCF,0x72,0x80,0x86,0x04,0x0A,0xE3]],
[[0x30,0x31,0x32,0x33,0x35,0x36,0x37,0x38,0x3A,0x3B,0x3C,0x3D,0x3F,0x40,0x41,0x42,0x44,0x45,0x46,0x47,0x49,0x4A,0x4B,0x4C,0x4E,0x4F,0x50,0x51,0x53,0x54,0x55,0x56],[0x72,0x61,0x65,0xC1,0x72,0x3F,0xBC,0xF6,0xC0,0x26,0xD7,0xD0,0x0B,0x09,0x10,0x27],[0x7C,0x17,0x00,0x21,0x1A,0x39,0x91,0xFC,0x0E,0xCD,0xED,0x0A,0xB3,0xE5,0x76,0xB0]]]; [[0x30,0x31,0x32,0x33,0x35,0x36,0x37,0x38,0x3A,0x3B,0x3C,0x3D,0x3F,0x40,0x41,0x42,0x44,0x45,0x46,0x47,0x49,0x4A,0x4B,0x4C,0x4E,0x4F,0x50,0x51,0x53,0x54,0x55,0x56],[0x72,0x61,0x65,0xC1,0x72,0x3F,0xBC,0xF6,0xC0,0x26,0xD7,0xD0,0x0B,0x09,0x10,0x27],[0x7C,0x17,0x00,0x21,0x1A,0x39,0x91,0xFC,0x0E,0xCD,0xED,0x0A,0xB3,0xE5,0x76,0xB0]]];
it('128 bit key', function (done) { it('128 bit key', async function () {
for (let i = 0; i < testvectors128.length; i++) { for (let i = 0; i < testvectors128.length; i++) {
test_aes(testvectors128[i][1],testvectors128[i][0],testvectors128[i][2]); await test_aes(testvectors128[i][1],testvectors128[i][0],testvectors128[i][2]);
} }
done();
}); });
it('192 bit key', function (done) { it('192 bit key', async function () {
for (let i = 0; i < testvectors192.length; i++) { for (let i = 0; i < testvectors192.length; i++) {
test_aes(testvectors192[i][1],testvectors192[i][0],testvectors192[i][2]); await test_aes(testvectors192[i][1],testvectors192[i][0],testvectors192[i][2]);
} }
done();
}); });
it('256 bit key', function (done) { it('256 bit key', async function () {
for (let i = 0; i < testvectors256.length; i++) { for (let i = 0; i < testvectors256.length; i++) {
test_aes(testvectors256[i][1],testvectors256[i][0],testvectors256[i][2]); await test_aes(testvectors256[i][1],testvectors256[i][0],testvectors256[i][2]);
} }
done();
}); });
}); });

View File

@ -254,7 +254,7 @@ export default () => describe('API functional testing', function() {
async function testCFB(plaintext, config = openpgp.config) { async function testCFB(plaintext, config = openpgp.config) {
await Promise.all(symmAlgoNames.map(async function(algoName) { await Promise.all(symmAlgoNames.map(async function(algoName) {
const algo = openpgp.enums.write(openpgp.enums.symmetric, algoName); const algo = openpgp.enums.write(openpgp.enums.symmetric, algoName);
const { blockSize } = crypto.getCipher(algo); const { blockSize } = crypto.getCipherParams(algo);
const symmKey = crypto.generateSessionKey(algo); const symmKey = crypto.generateSessionKey(algo);
const IV = new Uint8Array(blockSize); const IV = new Uint8Array(blockSize);
const symmencData = await crypto.mode.cfb.encrypt(algo, symmKey, util.stringToUint8Array(plaintext), IV, config); const symmencData = await crypto.mode.cfb.encrypt(algo, symmKey, util.stringToUint8Array(plaintext), IV, config);