Add config parameter to top-level functions (#1241)

Refactor functions to take the configuration as a parameter.

This allows setting a config option for a single function call, whereas
setting `openpgp.config` could lead to concurrency-related issues when
multiple async function calls are made at the same time.

`openpgp.config` is used as default for unset config values in top-level
functions.
`openpgp.config` is used as default config object in low-level functions
(i.e., when calling a low-level function, it may be required to pass
`{ ...openpgp.config, modifiedConfig: modifiedValue }`).

Also,

- remove `config.rsaBlinding`: blinding is now always applied to RSA decryption
- remove `config.debug`: debugging mode can be enabled by setting
  `process.env.NODE_ENV = 'development'`
- remove `config.useNative`: native crypto is always used when available
This commit is contained in:
larabr
2021-02-26 20:04:54 +01:00
committed by GitHub
parent 15ee659c9c
commit 7f37a8aaca
51 changed files with 1361 additions and 1038 deletions

View File

@@ -25,7 +25,6 @@
*/
import stream from 'web-stream-tools';
import config from '../config';
import crypto from '../crypto';
import enums from '../enums';
import util from '../util';
@@ -35,6 +34,7 @@ import {
OnePassSignaturePacket,
SignaturePacket
} from '../packet';
import defaultConfig from '../config';
const VERSION = 1; // A one-octet version number of the data packet.
@@ -107,10 +107,11 @@ class AEADEncryptedDataPacket {
* @param {String} sessionKeyAlgorithm The session key's cipher algorithm e.g. 'aes128'
* @param {Uint8Array} key The session key used to encrypt the payload
* @param {Boolean} streaming Whether the top-level function will return a stream
* @param {Object} config (optional) full configuration, defaults to openpgp.config
* @throws {Error} if encryption was not successful
* @async
*/
async encrypt(sessionKeyAlgorithm, key, streaming) {
async encrypt(sessionKeyAlgorithm, key, streaming, config = defaultConfig) {
this.cipherAlgo = enums.write(enums.symmetric, sessionKeyAlgorithm);
this.aeadAlgo = enums.write(enums.aead, this.aeadAlgorithm);
const mode = crypto[enums.read(enums.aead, this.aeadAlgo)];

View File

@@ -30,9 +30,9 @@ import { Inflate } from 'pako/lib/inflate';
import { Z_SYNC_FLUSH, Z_FINISH } from 'pako/lib/zlib/constants';
import { decode as BunzipDecode } from 'seek-bzip';
import stream from 'web-stream-tools';
import config from '../config';
import enums from '../enums';
import util from '../util';
import defaultConfig from '../config';
import {
LiteralDataPacket,
OnePassSignaturePacket,
@@ -49,7 +49,10 @@ import {
* @memberof module:packet
*/
class CompressedDataPacket {
constructor() {
/**
* @param {Object} config (optional) full configuration, defaults to openpgp.config
*/
constructor(config = defaultConfig) {
/**
* Packet type
* @type {module:enums.packet}
@@ -64,20 +67,25 @@ class CompressedDataPacket {
* Compression algorithm
* @type {compression}
*/
this.algorithm = 'zip';
this.algorithm = enums.read(enums.compression, config.compression);
/**
* Compressed packet data
* @type {Uint8Array | ReadableStream<Uint8Array>}
*/
this.compressed = null;
/**
* zip/zlib compression level, between 1 and 9
*/
this.deflateLevel = config.deflateLevel;
}
/**
* Parsing function for the packet.
* @param {Uint8Array | ReadableStream<Uint8Array>} bytes Payload of a tag 8 packet
*/
async read(bytes, streaming) {
async read(bytes, config, streaming) {
await stream.parse(bytes, async reader => {
// One octet that gives the algorithm used to compress the packet.
@@ -125,12 +133,11 @@ class CompressedDataPacket {
* Compress the packet data (member decompressedData)
*/
compress() {
if (!compress_fns[this.algorithm]) {
throw new Error(this.algorithm + ' compression not supported');
}
this.compressed = compress_fns[this.algorithm](this.packets.write());
this.compressed = compress_fns[this.algorithm](this.packets.write(), this.deflateLevel);
}
}
@@ -179,11 +186,11 @@ function bzip2(func) {
}
const compress_fns = nodeZlib ? {
zip: /*#__PURE__*/ node_zlib(nodeZlib.createDeflateRaw, { level: config.deflateLevel }),
zlib: /*#__PURE__*/ node_zlib(nodeZlib.createDeflate, { level: config.deflateLevel })
zip: /*#__PURE__*/ (compressed, level) => node_zlib(nodeZlib.createDeflateRaw, { level })(compressed),
zlib: /*#__PURE__*/ (compressed, level) => node_zlib(nodeZlib.createDeflate, { level })(compressed)
} : {
zip: /*#__PURE__*/ pako_zlib(Deflate, { raw: true, level: config.deflateLevel }),
zlib: /*#__PURE__*/ pako_zlib(Deflate, { level: config.deflateLevel })
zip: /*#__PURE__*/ (compressed, level) => pako_zlib(Deflate, { raw: true, level })(compressed),
zlib: /*#__PURE__*/ (compressed, level) => pako_zlib(Deflate, { level })(compressed)
};
const decompress_fns = nodeZlib ? {

View File

@@ -14,9 +14,9 @@ import {
writeTag, writeHeader,
writePartialLength, writeSimpleLength
} from './packet';
import config from '../config';
import enums from '../enums';
import util from '../util';
import defaultConfig from '../config';
/**
* This class represents a list of openpgp packets.
@@ -30,7 +30,7 @@ class PacketList extends Array {
* Reads a stream of binary data and interprets it as a list of packets.
* @param {Uint8Array | ReadableStream<Uint8Array>} bytes A Uint8Array of bytes.
*/
async read(bytes, allowedPackets, streaming) {
async read(bytes, allowedPackets, streaming, config = defaultConfig) {
this.stream = stream.transformPair(bytes, async (readable, writable) => {
const writer = stream.getWriter(writable);
try {
@@ -42,7 +42,7 @@ class PacketList extends Array {
const packet = packets.newPacketFromTag(tag, allowedPackets);
packet.packets = new PacketList();
packet.fromStream = util.isStream(parsed.packet);
await packet.read(parsed.packet, streaming);
await packet.read(parsed.packet, config, streaming);
await writer.write(packet);
} catch (e) {
if (!config.tolerant || supportsStreaming(parsed.tag)) {

View File

@@ -28,7 +28,7 @@
import { Sha1 } from 'asmcrypto.js/dist_es8/hash/sha1/sha1';
import { Sha256 } from 'asmcrypto.js/dist_es8/hash/sha256/sha256';
import type_keyid from '../type/keyid';
import config from '../config';
import defaultConfig from '../config';
import crypto from '../crypto';
import enums from '../enums';
import util from '../util';
@@ -46,7 +46,11 @@ import util from '../util';
* @memberof module:packet
*/
class PublicKeyPacket {
constructor(date = new Date()) {
/**
* @param {Date} date (optional) creation date
* @param {Object} config (optional) full configuration, defaults to openpgp.config
*/
constructor(date = new Date(), config = defaultConfig) {
/**
* Packet type
* @type {module:enums.packet}

View File

@@ -33,8 +33,12 @@ import enums from '../enums';
* @extends PublicKeyPacket
*/
class PublicSubkeyPacket extends PublicKeyPacket {
constructor() {
super();
/**
* @param {Date} date (optional) creation date
* @param {Object} config (optional) full configuration, defaults to openpgp.config
*/
constructor(date, config) {
super(date, config);
this.tag = enums.packet.publicSubkey;
}
}

View File

@@ -29,6 +29,7 @@ import type_s2k from '../type/s2k';
import crypto from '../crypto';
import enums from '../enums';
import util from '../util';
import defaultConfig from '../config';
/**
* A Secret-Key packet contains all the information that is found in a
@@ -38,8 +39,12 @@ import util from '../util';
* @extends PublicKeyPacket
*/
class SecretKeyPacket extends PublicKeyPacket {
constructor(date = new Date()) {
super(date);
/**
* @param {Date} date (optional) creation date
* @param {Object} config (optional) full configuration, defaults to openpgp.config
*/
constructor(date = new Date(), config = defaultConfig) {
super(date, config);
/**
* Packet type
* @type {module:enums.packet}
@@ -247,8 +252,9 @@ class SecretKeyPacket extends PublicKeyPacket {
/**
* Remove private key material, converting the key to a dummy one.
* The resulting key cannot be used for signing/decrypting but can still verify signatures.
* @param {Object} config (optional) full configuration, defaults to openpgp.config
*/
makeDummy() {
makeDummy(config = defaultConfig) {
if (this.isDummy()) {
return;
}
@@ -257,7 +263,7 @@ class SecretKeyPacket extends PublicKeyPacket {
}
this.isEncrypted = null;
this.keyMaterial = null;
this.s2k = new type_s2k();
this.s2k = new type_s2k(config);
this.s2k.algorithm = 0;
this.s2k.c = 0;
this.s2k.type = 'gnu-dummy';
@@ -271,10 +277,11 @@ class SecretKeyPacket extends PublicKeyPacket {
* and the passphrase is empty or undefined, the key will be set as not encrypted.
* This can be used to remove passphrase protection after calling decrypt().
* @param {String} passphrase
* @param {Object} config (optional) full configuration, defaults to openpgp.config
* @throws {Error} if encryption was not successful
* @async
*/
async encrypt(passphrase) {
async encrypt(passphrase, config = defaultConfig) {
if (this.isDummy()) {
return;
}
@@ -290,7 +297,7 @@ class SecretKeyPacket extends PublicKeyPacket {
throw new Error('The key must be decrypted before removing passphrase protection.');
}
this.s2k = new type_s2k();
this.s2k = new type_s2k(config);
this.s2k.salt = await crypto.random.getRandomBytes(8);
const algo = enums.write(enums.publicKey, this.algorithm);
const cleartext = crypto.serializeParams(algo, this.privateParams);
@@ -309,8 +316,8 @@ class SecretKeyPacket extends PublicKeyPacket {
this.s2k_usage = 254;
this.keyMaterial = await crypto.cfb.encrypt(this.symmetric, key, util.concatUint8Array([
cleartext,
await crypto.hash.sha1(cleartext)
]), this.iv);
await crypto.hash.sha1(cleartext, config)
]), this.iv, config);
}
}

View File

@@ -22,6 +22,7 @@
import SecretKeyPacket from './secret_key';
import enums from '../enums';
import defaultConfig from '../config';
/**
* A Secret-Subkey packet (tag 7) is the subkey analog of the Secret
@@ -30,8 +31,12 @@ import enums from '../enums';
* @extends SecretKeyPacket
*/
class SecretSubkeyPacket extends SecretKeyPacket {
constructor(date = new Date()) {
super(date);
/**
* @param {Date} date (optional) creation date
* @param {Object} config (optional) full configuration, defaults to openpgp.config
*/
constructor(date = new Date(), config = defaultConfig) {
super(date, config);
this.tag = enums.packet.secretSubkey;
}
}

View File

@@ -30,7 +30,7 @@ import type_keyid from '../type/keyid.js';
import crypto from '../crypto';
import enums from '../enums';
import util from '../util';
import config from '../config';
import defaultConfig from '../config';
/**
* Implementation of the Signature Packet (Tag 2)
@@ -99,9 +99,10 @@ class SignaturePacket {
/**
* parsing function for a signature packet (tag 2).
* @param {String} bytes payload of a tag 2 packet
* @param {Object} config (optional) full configuration, defaults to openpgp.config
* @returns {SignaturePacket} object representation
*/
read(bytes) {
read(bytes, config = defaultConfig) {
let i = 0;
this.version = bytes[i++];
@@ -114,7 +115,7 @@ class SignaturePacket {
this.hashAlgorithm = bytes[i++];
// hashed subpackets
i += this.read_sub_packets(bytes.subarray(i, bytes.length), true);
i += this.read_sub_packets(bytes.subarray(i, bytes.length), true, config);
// A V4 signature hashes the packet body
// starting from its first field, the version number, through the end
@@ -125,7 +126,7 @@ class SignaturePacket {
this.signatureData = bytes.subarray(0, i);
// unhashed subpackets
i += this.read_sub_packets(bytes.subarray(i, bytes.length), false);
i += this.read_sub_packets(bytes.subarray(i, bytes.length), false, config);
// Two-octet field holding left 16 bits of signed hash value.
this.signedHashValue = bytes.subarray(i, i + 2);
@@ -340,7 +341,7 @@ class SignaturePacket {
// V4 signature sub packets
read_sub_packet(bytes, trusted = true) {
read_sub_packet(bytes, trusted = true, config) {
let mypos = 0;
const read_array = (prop, bytes) => {
@@ -536,7 +537,7 @@ class SignaturePacket {
}
}
read_sub_packets(bytes, trusted = true) {
read_sub_packets(bytes, trusted = true, config) {
// Two-octet scalar octet count for following subpacket data.
const subpacket_length = util.readNumber(bytes.subarray(0, 2));
@@ -547,7 +548,7 @@ class SignaturePacket {
const len = readSimpleLength(bytes.subarray(i, bytes.length));
i += len.offset;
this.read_sub_packet(bytes.subarray(i, i + len.len), trusted);
this.read_sub_packet(bytes.subarray(i, i + len.len), trusted, config);
i += len.len;
}
@@ -671,10 +672,11 @@ class SignaturePacket {
* @param {String|Object} data data which on the signature applies
* @param {Boolean} detached (optional) whether to verify a detached signature
* @param {Boolean} streaming (optional) whether to process data as a stream
* @param {Object} config (optional) full configuration, defaults to openpgp.config
* @throws {Error} if signature validation failed
* @async
*/
async verify(key, signatureType, data, detached = false, streaming = false) {
async verify(key, signatureType, data, detached = false, streaming = false, config = defaultConfig) {
const publicKeyAlgorithm = enums.write(enums.publicKey, this.publicKeyAlgorithm);
const hashAlgorithm = enums.write(enums.hash, this.hashAlgorithm);

View File

@@ -26,7 +26,6 @@
*/
import stream from 'web-stream-tools';
import config from '../config';
import crypto from '../crypto';
import enums from '../enums';
import util from '../util';
@@ -36,6 +35,7 @@ import {
OnePassSignaturePacket,
SignaturePacket
} from '../packet';
import defaultConfig from '../config';
const VERSION = 1; // A one-octet version number of the data packet.
@@ -90,10 +90,11 @@ class SymEncryptedIntegrityProtectedDataPacket {
* @param {String} sessionKeyAlgorithm The selected symmetric encryption algorithm to be used e.g. 'aes128'
* @param {Uint8Array} key The key of cipher blocksize length to be used
* @param {Boolean} streaming Whether to set this.encrypted to a stream
* @param {Object} config (optional) full configuration, defaults to openpgp.config
* @returns {Promise<Boolean>}
* @async
*/
async encrypt(sessionKeyAlgorithm, key, streaming) {
async encrypt(sessionKeyAlgorithm, key, streaming, config = defaultConfig) {
let bytes = this.packets.write();
if (!streaming) bytes = await stream.readToEnd(bytes);
const prefix = await crypto.getPrefixRandom(sessionKeyAlgorithm);
@@ -103,7 +104,7 @@ class SymEncryptedIntegrityProtectedDataPacket {
const hash = await crypto.hash.sha1(stream.passiveClone(tohash));
const plaintext = util.concat([tohash, hash]);
this.encrypted = await crypto.cfb.encrypt(sessionKeyAlgorithm, key, plaintext, new Uint8Array(crypto.cipher[sessionKeyAlgorithm].blockSize));
this.encrypted = await crypto.cfb.encrypt(sessionKeyAlgorithm, key, plaintext, new Uint8Array(crypto.cipher[sessionKeyAlgorithm].blockSize), config);
return true;
}
@@ -112,10 +113,11 @@ class SymEncryptedIntegrityProtectedDataPacket {
* @param {String} sessionKeyAlgorithm The selected symmetric encryption algorithm to be used e.g. 'aes128'
* @param {Uint8Array} key The key of cipher blocksize length to be used
* @param {Boolean} streaming Whether to read this.encrypted as a stream
* @param {Object} config (optional) full configuration, defaults to openpgp.config
* @returns {Promise<Boolean>}
* @async
*/
async decrypt(sessionKeyAlgorithm, key, streaming) {
async decrypt(sessionKeyAlgorithm, key, streaming, config = defaultConfig) {
let encrypted = stream.clone(this.encrypted);
if (!streaming) encrypted = await stream.readToEnd(encrypted);
const decrypted = await crypto.cfb.decrypt(sessionKeyAlgorithm, key, encrypted, new Uint8Array(crypto.cipher[sessionKeyAlgorithm].blockSize));

View File

@@ -24,7 +24,7 @@
*/
import type_s2k from '../type/s2k';
import config from '../config';
import defaultConfig from '../config';
import crypto from '../crypto';
import enums from '../enums';
import util from '../util';
@@ -44,7 +44,10 @@ import util from '../util';
* @memberof module:packet
*/
class SymEncryptedSessionKeyPacket {
constructor() {
/**
* @param {Object} config (optional) full configuration, defaults to openpgp.config
*/
constructor(config = defaultConfig) {
this.tag = enums.packet.symEncryptedSessionKey;
this.version = config.aeadProtect ? 5 : 4;
this.sessionKey = null;
@@ -154,17 +157,18 @@ class SymEncryptedSessionKeyPacket {
/**
* Encrypts the session key
* @param {String} passphrase The passphrase in string form
* @param {Object} config (optional) full configuration, defaults to openpgp.config
* @throws {Error} if encryption was not successful
* @async
*/
async encrypt(passphrase) {
async encrypt(passphrase, config = defaultConfig) {
const algo = this.sessionKeyEncryptionAlgorithm !== null ?
this.sessionKeyEncryptionAlgorithm :
this.sessionKeyAlgorithm;
this.sessionKeyEncryptionAlgorithm = algo;
this.s2k = new type_s2k();
this.s2k = new type_s2k(config);
this.s2k.salt = await crypto.random.getRandomBytes(8);
const length = crypto.cipher[algo].keySize;
@@ -183,7 +187,7 @@ class SymEncryptedSessionKeyPacket {
} else {
const algo_enum = new Uint8Array([enums.write(enums.symmetric, this.sessionKeyAlgorithm)]);
const private_key = util.concatUint8Array([algo_enum, this.sessionKey]);
this.encrypted = await crypto.cfb.encrypt(algo, key, private_key, new Uint8Array(crypto.cipher[algo].blockSize));
this.encrypted = await crypto.cfb.encrypt(algo, key, private_key, new Uint8Array(crypto.cipher[algo].blockSize), config);
}
}
}

View File

@@ -25,7 +25,6 @@
*/
import stream from 'web-stream-tools';
import config from '../config';
import crypto from '../crypto';
import enums from '../enums';
import util from '../util';
@@ -35,6 +34,7 @@ import {
OnePassSignaturePacket,
SignaturePacket
} from '../packet';
import defaultConfig from '../config';
/**
* Implementation of the Symmetrically Encrypted Data Packet (Tag 9)
@@ -63,11 +63,6 @@ class SymmetricallyEncryptedDataPacket {
* @type {PacketList}
*/
this.packets = null;
/**
* When true, decrypt fails if message is not integrity protected
* @see module:config.ignoreMdcError
*/
this.ignoreMdcError = config.ignoreMdcError;
}
read(bytes) {
@@ -83,12 +78,14 @@ class SymmetricallyEncryptedDataPacket {
* See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms.
* @param {module:enums.symmetric} sessionKeyAlgorithm Symmetric key algorithm to use
* @param {Uint8Array} key The key of cipher blocksize length to be used
* @param {Object} config (optional) full configuration, defaults to openpgp.config
* @throws {Error} if decryption was not successful
* @async
*/
async decrypt(sessionKeyAlgorithm, key, streaming) {
async decrypt(sessionKeyAlgorithm, key, streaming, config = defaultConfig) {
// If MDC errors are not being ignored, all missing MDC packets in symmetrically encrypted data should throw an error
if (!this.ignoreMdcError) {
if (!config.ignoreMdcError) {
throw new Error('Decryption failed due to missing MDC.');
}
@@ -111,15 +108,16 @@ class SymmetricallyEncryptedDataPacket {
* See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms.
* @param {module:enums.symmetric} sessionKeyAlgorithm Symmetric key algorithm to use
* @param {Uint8Array} key The key of cipher blocksize length to be used
* @param {Object} config (optional) full configuration, defaults to openpgp.config
* @throws {Error} if encryption was not successful
* @async
*/
async encrypt(algo, key) {
async encrypt(algo, key, streaming, config = defaultConfig) {
const data = this.packets.write();
const prefix = await crypto.getPrefixRandom(algo);
const FRE = await crypto.cfb.encrypt(algo, key, prefix, new Uint8Array(crypto.cipher[algo].blockSize));
const ciphertext = await crypto.cfb.encrypt(algo, key, data, FRE.subarray(2));
const FRE = await crypto.cfb.encrypt(algo, key, prefix, new Uint8Array(crypto.cipher[algo].blockSize), config);
const ciphertext = await crypto.cfb.encrypt(algo, key, data, FRE.subarray(2), config);
this.encrypted = util.concat([FRE, ciphertext]);
}
}

View File

@@ -23,7 +23,7 @@ import emailAddresses from 'email-addresses';
import enums from '../enums';
import util from '../util';
import config from '../config';
import defaultConfig from '../config';
/**
* Implementation of the User ID Packet (Tag 13)
@@ -76,7 +76,7 @@ class UserIDPacket {
* Parsing function for a user id packet (tag 13).
* @param {Uint8Array} input payload of a tag 13 packet
*/
read(bytes) {
read(bytes, config = defaultConfig) {
const userid = util.decodeUtf8(bytes);
if (userid.length > config.maxUseridLength) {
throw new Error('User ID string is too long');