Replace armor option with format in openpgp.encrypt, sign and encryptSessionKey (#1354)

Breaking changes:
- a new `format` option has been added to `openpgp.encrypt`, `sign` and
`encryptSessionKey` to select the format of the output message. `format`
replaces the existing `armor` option, and accepts three values:
   * if `format: 'armor'` (default), an armored signed/encrypted message is
returned (same as `armor: true`).
   * if `format: 'binary'`,  a binary signed/encrypted message is returned (same
as `armor: false`).
   * if `format: 'object'`, a Message or Signature object is returned (this was
not supported before).
This change is to uniform the output format selection across all top-level
functions (following up to #1345).

- All top-level functions now throw if unrecognised options are passed, to make
library users aware that those options are not being applied.
This commit is contained in:
larabr 2021-07-19 18:12:42 +02:00 committed by GitHub
parent 53f54e1e19
commit ce70484738
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 443 additions and 120 deletions

View File

@ -177,7 +177,7 @@ Encryption will use the algorithm specified in config.preferredSymmetricAlgorith
const encrypted = await openpgp.encrypt({
message, // input as Message object
passwords: ['secret stuff'], // multiple passwords possible
armor: false // don't ASCII armor (for Uint8Array output)
format: 'binary' // don't ASCII armor (for Uint8Array output)
});
console.log(encrypted); // Uint8Array
@ -358,7 +358,7 @@ Where the value can be any of:
const encrypted = await openpgp.encrypt({
message, // input as Message object
passwords: ['secret stuff'], // multiple passwords possible
armor: false // don't ASCII armor (for Uint8Array output)
format: 'binary' // don't ASCII armor (for Uint8Array output)
});
console.log(encrypted); // raw encrypted packets as ReadableStream<Uint8Array>

40
openpgp.d.ts vendored
View File

@ -172,11 +172,14 @@ export class CleartextMessage {
/* ############## v5 MSG #################### */
export function generateSessionKey(options: { encryptionKeys: MaybeArray<PublicKey>, date?: Date, encryptionUserIDs?: MaybeArray<UserID>, config?: PartialConfig }): Promise<SessionKey>;
export function encryptSessionKey(options: SessionKey & {
encryptionKeys?: MaybeArray<PublicKey>, passwords?: MaybeArray<string>, armor?: true, wildcard?: boolean, encryptionKeyIDs?: MaybeArray<KeyID>, date?: Date, encryptionUserIDs?: MaybeArray<UserID>, config?: PartialConfig
encryptionKeys?: MaybeArray<PublicKey>, passwords?: MaybeArray<string>, format?: 'armor', wildcard?: boolean, encryptionKeyIDs?: MaybeArray<KeyID>, date?: Date, encryptionUserIDs?: MaybeArray<UserID>, config?: PartialConfig
}) : Promise<string>;
export function encryptSessionKey(options: SessionKey & {
encryptionKeys?: MaybeArray<PublicKey>, passwords?: MaybeArray<string>, armor: false, wildcard?: boolean, encryptionKeyIDs?: MaybeArray<KeyID>, date?: Date, encryptionUserIDs?: MaybeArray<UserID>, config?: PartialConfig
encryptionKeys?: MaybeArray<PublicKey>, passwords?: MaybeArray<string>, format: 'binary', wildcard?: boolean, encryptionKeyIDs?: MaybeArray<KeyID>, date?: Date, encryptionUserIDs?: MaybeArray<UserID>, config?: PartialConfig
}) : Promise<Uint8Array>;
export function encryptSessionKey(options: SessionKey & {
encryptionKeys?: MaybeArray<PublicKey>, passwords?: MaybeArray<string>, format: 'object', wildcard?: boolean, encryptionKeyIDs?: MaybeArray<KeyID>, date?: Date, encryptionUserIDs?: MaybeArray<UserID>, config?: PartialConfig
}) : Promise<Message<Data>>;
export function decryptSessionKeys<T extends MaybeStream<Data>>(options: { message: Message<T>, decryptionKeys?: MaybeArray<PrivateKey>, passwords?: MaybeArray<string>, date?: Date, config?: PartialConfig }): Promise<SessionKey[]>;
export function readMessage<T extends MaybeStream<string>>(options: { armoredMessage: T, config?: PartialConfig }): Promise<Message<T>>;
@ -185,28 +188,31 @@ export function readMessage<T extends MaybeStream<Uint8Array>>(options: { binary
export function createMessage<T extends MaybeStream<string>>(options: { text: T, filename?: string, date?: Date, type?: DataPacketType }): Promise<Message<T>>;
export function createMessage<T extends MaybeStream<Uint8Array>>(options: { binary: T, filename?: string, date?: Date, type?: DataPacketType }): Promise<Message<T>>;
export function encrypt<T extends MaybeStream<Data>>(options: EncryptOptions & { message: Message<T>, armor: false }): Promise<
T extends WebStream<infer X> ? WebStream<Uint8Array> :
T extends NodeStream<infer X> ? NodeStream<Uint8Array> :
Uint8Array
>;
export function encrypt<T extends MaybeStream<Data>>(options: EncryptOptions & { message: Message<T> }): Promise<
export function encrypt<T extends MaybeStream<Data>>(options: EncryptOptions & { message: Message<T>, format?: 'armor' }): Promise<
T extends WebStream<infer X> ? WebStream<string> :
T extends NodeStream<infer X> ? NodeStream<string> :
string
>;
export function encrypt<T extends MaybeStream<Data>>(options: EncryptOptions & { message: Message<T>, format: 'binary' }): Promise<
T extends WebStream<infer X> ? WebStream<Uint8Array> :
T extends NodeStream<infer X> ? NodeStream<Uint8Array> :
Uint8Array
>;
export function encrypt<T extends MaybeStream<Data>>(options: EncryptOptions & { message: Message<T>, format: 'object' }): Promise<Message<T>>;
export function sign<T extends MaybeStream<Data>>(options: SignOptions & { message: Message<T>, armor: false }): Promise<
T extends WebStream<infer X> ? WebStream<Uint8Array> :
T extends NodeStream<infer X> ? NodeStream<Uint8Array> :
Uint8Array
>;
export function sign<T extends MaybeStream<Data>>(options: SignOptions & { message: Message<T> }): Promise<
export function sign<T extends MaybeStream<Data>>(options: SignOptions & { message: Message<T>, format?: 'armor' }): Promise<
T extends WebStream<infer X> ? WebStream<string> :
T extends NodeStream<infer X> ? NodeStream<string> :
string
>;
export function sign(options: SignOptions & { message: CleartextMessage }): Promise<string>;
export function sign<T extends MaybeStream<Data>>(options: SignOptions & { message: Message<T>, format: 'binary' }): Promise<
T extends WebStream<infer X> ? WebStream<Uint8Array> :
T extends NodeStream<infer X> ? NodeStream<Uint8Array> :
Uint8Array
>;
export function sign<T extends MaybeStream<Data>>(options: SignOptions & { message: Message<T>, format: 'object' }): Promise<Message<T>>;
export function sign(options: SignOptions & { message: CleartextMessage, format?: 'armor' }): Promise<string>;
export function sign(options: SignOptions & { message: CleartextMessage, format: 'object' }): Promise<CleartextMessage>;
export function decrypt<T extends MaybeStream<Data>>(options: DecryptOptions & { message: Message<T>, format: 'binary' }): Promise<DecryptMessageResult & {
data:
@ -577,7 +583,7 @@ interface EncryptOptions {
/** (optional) session key */
sessionKey?: SessionKey;
/** if the return values should be ascii armored or the message/signature objects */
armor?: boolean;
format?: 'armor' | 'binary' | 'object';
/** (optional) if the signature should be detached (if true, signature will be added to returned object) */
signature?: Signature;
/** (optional) encrypt as of a certain date */
@ -620,7 +626,7 @@ interface DecryptOptions {
interface SignOptions {
message: CleartextMessage | Message<MaybeStream<Data>>;
signingKeys?: MaybeArray<PrivateKey>;
armor?: boolean;
format?: 'armor' | 'binary' | 'object';
dataType?: DataPacketType;
detached?: boolean;
signingKeyIDs?: MaybeArray<KeyID>;

View File

@ -132,7 +132,7 @@ export class CleartextMessage {
* @async
* @static
*/
export async function readCleartextMessage({ cleartextMessage, config }) {
export async function readCleartextMessage({ cleartextMessage, config, ...rest }) {
config = { ...defaultConfig, ...config };
if (!cleartextMessage) {
throw new Error('readCleartextMessage: must pass options object containing `cleartextMessage`');
@ -140,6 +140,8 @@ export async function readCleartextMessage({ cleartextMessage, config }) {
if (!util.isString(cleartextMessage)) {
throw new Error('readCleartextMessage: options.cleartextMessage must be a string');
}
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
const input = await unarmor(cleartextMessage);
if (input.type !== enums.armor.signed) {
throw new Error('No cleartext signed message.');
@ -203,12 +205,14 @@ function verifyHeaders(headers, packetlist) {
* @static
* @async
*/
export async function createCleartextMessage({ text }) {
export async function createCleartextMessage({ text, ...rest }) {
if (!text) {
throw new Error('createCleartextMessage: must pass options object containing `text`');
}
if (!util.isString(text)) {
throw new Error('createCleartextMessage: options.text must be a string');
}
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
return new CleartextMessage(text);
}

View File

@ -275,7 +275,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options, conf
* @async
* @static
*/
export async function readKey({ armoredKey, binaryKey, config }) {
export async function readKey({ armoredKey, binaryKey, config, ...rest }) {
config = { ...defaultConfig, ...config };
if (!armoredKey && !binaryKey) {
throw new Error('readKey: must pass options object containing `armoredKey` or `binaryKey`');
@ -286,6 +286,8 @@ export async function readKey({ armoredKey, binaryKey, config }) {
if (binaryKey && !util.isUint8Array(binaryKey)) {
throw new Error('readKey: options.binaryKey must be a Uint8Array');
}
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
let input;
if (armoredKey) {
const { type, data } = await unarmor(armoredKey, config);
@ -310,7 +312,7 @@ export async function readKey({ armoredKey, binaryKey, config }) {
* @async
* @static
*/
export async function readPrivateKey({ armoredKey, binaryKey, config }) {
export async function readPrivateKey({ armoredKey, binaryKey, config, ...rest }) {
config = { ...defaultConfig, ...config };
if (!armoredKey && !binaryKey) {
throw new Error('readPrivateKey: must pass options object containing `armoredKey` or `binaryKey`');
@ -321,6 +323,8 @@ export async function readPrivateKey({ armoredKey, binaryKey, config }) {
if (binaryKey && !util.isUint8Array(binaryKey)) {
throw new Error('readPrivateKey: options.binaryKey must be a Uint8Array');
}
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
let input;
if (armoredKey) {
const { type, data } = await unarmor(armoredKey, config);
@ -345,7 +349,7 @@ export async function readPrivateKey({ armoredKey, binaryKey, config }) {
* @async
* @static
*/
export async function readKeys({ armoredKeys, binaryKeys, config }) {
export async function readKeys({ armoredKeys, binaryKeys, config, ...rest }) {
config = { ...defaultConfig, ...config };
let input = armoredKeys || binaryKeys;
if (!input) {
@ -357,6 +361,8 @@ export async function readKeys({ armoredKeys, binaryKeys, config }) {
if (binaryKeys && !util.isUint8Array(binaryKeys)) {
throw new Error('readKeys: options.binaryKeys must be a Uint8Array');
}
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
if (armoredKeys) {
const { type, data } = await unarmor(armoredKeys, config);
if (type !== enums.armor.publicKey && type !== enums.armor.privateKey) {

View File

@ -790,7 +790,7 @@ export async function createVerificationObjects(signatureList, literalDataList,
* @async
* @static
*/
export async function readMessage({ armoredMessage, binaryMessage, config }) {
export async function readMessage({ armoredMessage, binaryMessage, config, ...rest }) {
config = { ...defaultConfig, ...config };
let input = armoredMessage || binaryMessage;
if (!input) {
@ -802,6 +802,8 @@ export async function readMessage({ armoredMessage, binaryMessage, config }) {
if (binaryMessage && !util.isUint8Array(binaryMessage) && !util.isStream(binaryMessage)) {
throw new Error('readMessage: options.binaryMessage must be a Uint8Array or stream');
}
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
const streamType = util.isStream(input);
if (streamType) {
await stream.loadStreamsPonyfill();
@ -832,7 +834,7 @@ export async function readMessage({ armoredMessage, binaryMessage, config }) {
* @async
* @static
*/
export async function createMessage({ text, binary, filename, date = new Date(), format = text !== undefined ? 'utf8' : 'binary' }) {
export async function createMessage({ text, binary, filename, date = new Date(), format = text !== undefined ? 'utf8' : 'binary', ...rest }) {
let input = text !== undefined ? text : binary;
if (input === undefined) {
throw new Error('createMessage: must pass options object containing `text` or `binary`');
@ -843,6 +845,8 @@ export async function createMessage({ text, binary, filename, date = new Date(),
if (binary && !util.isUint8Array(binary) && !util.isStream(binary)) {
throw new Error('createMessage: options.binary must be a Uint8Array or stream');
}
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
const streamType = util.isStream(input);
if (streamType) {
await stream.loadStreamsPonyfill();

View File

@ -32,6 +32,7 @@ import util from './util';
/**
* Generates a new OpenPGP key pair. Supports RSA and ECC keys. By default, primary and subkeys will be of same type.
* The generated primary key will have signing capabilities. By default, one subkey with encryption capabilities is also generated.
* @param {Object} options
* @param {Object|Array<Object>} options.userIDs - User IDs as objects: `{ name: 'Jo Doe', email: 'info@jo.com' }`
* @param {'ecc'|'rsa'} [options.type='ecc'] - The primary key algorithm type: ECC (default) or RSA
@ -51,9 +52,11 @@ import util from './util';
* @async
* @static
*/
export async function generateKey({ userIDs = [], passphrase = '', type = 'ecc', rsaBits = 4096, curve = 'curve25519', keyExpirationTime = 0, date = new Date(), subkeys = [{}], format = 'armor', config }) {
export async function generateKey({ userIDs = [], passphrase = '', type = 'ecc', rsaBits = 4096, curve = 'curve25519', keyExpirationTime = 0, date = new Date(), subkeys = [{}], format = 'armor', config, ...rest }) {
config = { ...defaultConfig, ...config };
userIDs = toArray(userIDs);
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
if (userIDs.length === 0) {
throw new Error('UserIDs are required for key generation');
}
@ -66,8 +69,8 @@ export async function generateKey({ userIDs = [], passphrase = '', type = 'ecc',
const { key, revocationCertificate } = await generate(options, config);
return {
privateKey: formatKey(key, format, config),
publicKey: formatKey(key.toPublic(), format, config),
privateKey: formatObject(key, format, config),
publicKey: formatObject(key.toPublic(), format, config),
revocationCertificate
};
} catch (err) {
@ -90,9 +93,11 @@ export async function generateKey({ userIDs = [], passphrase = '', type = 'ecc',
* @async
* @static
*/
export async function reformatKey({ privateKey, userIDs = [], passphrase = '', keyExpirationTime = 0, date, format = 'armor', config }) {
export async function reformatKey({ privateKey, userIDs = [], passphrase = '', keyExpirationTime = 0, date, format = 'armor', config, ...rest }) {
config = { ...defaultConfig, ...config };
userIDs = toArray(userIDs);
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
if (userIDs.length === 0) {
throw new Error('UserIDs are required for key reformat');
}
@ -102,8 +107,8 @@ export async function reformatKey({ privateKey, userIDs = [], passphrase = '', k
const { key: reformattedKey, revocationCertificate } = await reformat(options, config);
return {
privateKey: formatKey(reformattedKey, format, config),
publicKey: formatKey(reformattedKey.toPublic(), format, config),
privateKey: formatObject(reformattedKey, format, config),
publicKey: formatObject(reformattedKey.toPublic(), format, config),
revocationCertificate
};
} catch (err) {
@ -129,19 +134,21 @@ export async function reformatKey({ privateKey, userIDs = [], passphrase = '', k
* @async
* @static
*/
export async function revokeKey({ key, revocationCertificate, reasonForRevocation, date = new Date(), format = 'armor', config }) {
export async function revokeKey({ key, revocationCertificate, reasonForRevocation, date = new Date(), format = 'armor', config, ...rest }) {
config = { ...defaultConfig, ...config };
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
try {
const revokedKey = revocationCertificate ?
await key.applyRevocationCertificate(revocationCertificate, date, config) :
await key.revoke(reasonForRevocation, date, config);
return revokedKey.isPrivate() ? {
privateKey: formatKey(revokedKey, format, config),
publicKey: formatKey(revokedKey.toPublic(), format, config)
privateKey: formatObject(revokedKey, format, config),
publicKey: formatObject(revokedKey.toPublic(), format, config)
} : {
privateKey: null,
publicKey: formatKey(revokedKey, format, config)
publicKey: formatObject(revokedKey, format, config)
};
} catch (err) {
throw util.wrapError('Error revoking key', err);
@ -158,8 +165,10 @@ export async function revokeKey({ key, revocationCertificate, reasonForRevocatio
* @returns {Promise<PrivateKey>} The unlocked key object.
* @async
*/
export async function decryptKey({ privateKey, passphrase, config }) {
export async function decryptKey({ privateKey, passphrase, config, ...rest }) {
config = { ...defaultConfig, ...config };
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
if (!privateKey.isPrivate()) {
throw new Error('Cannot decrypt a public key');
}
@ -190,8 +199,10 @@ export async function decryptKey({ privateKey, passphrase, config }) {
* @returns {Promise<PrivateKey>} The locked key object.
* @async
*/
export async function encryptKey({ privateKey, passphrase, config }) {
export async function encryptKey({ privateKey, passphrase, config, ...rest }) {
config = { ...defaultConfig, ...config };
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
if (!privateKey.isPrivate()) {
throw new Error('Cannot encrypt a public key');
}
@ -233,7 +244,7 @@ export async function encryptKey({ privateKey, passphrase, config }) {
* @param {PrivateKey|PrivateKey[]} [options.signingKeys] - Private keys for signing. If omitted message will not be signed
* @param {String|String[]} [options.passwords] - Array of passwords or a single password to encrypt the message
* @param {Object} [options.sessionKey] - Session key in the form: `{ data:Uint8Array, algorithm:String }`
* @param {Boolean} [options.armor=true] - Whether the return values should be ascii armored (true, the default) or binary (false)
* @param {'armor'|'binary'|'object'} [options.format='armor'] - Format of the returned message
* @param {Signature} [options.signature] - A detached signature to add to the encrypted message
* @param {Boolean} [options.wildcard=false] - Use a key ID of 0 instead of the public key IDs
* @param {KeyID|KeyID[]} [options.signingKeyIDs=latest-created valid signing (sub)keys] - Array of key IDs to use for signing. Each `signingKeyIDs[i]` corresponds to `signingKeys[i]`
@ -246,20 +257,22 @@ export async function encryptKey({ privateKey, passphrase, config }) {
* @async
* @static
*/
export async function encrypt({ message, encryptionKeys, signingKeys, passwords, sessionKey, armor = true, signature = null, wildcard = false, signingKeyIDs = [], encryptionKeyIDs = [], date = new Date(), signingUserIDs = [], encryptionUserIDs = [], config, ...rest }) {
export async function encrypt({ message, encryptionKeys, signingKeys, passwords, sessionKey, format = 'armor', signature = null, wildcard = false, signingKeyIDs = [], encryptionKeyIDs = [], date = new Date(), signingUserIDs = [], encryptionUserIDs = [], config, ...rest }) {
config = { ...defaultConfig, ...config };
checkMessage(message); encryptionKeys = toArray(encryptionKeys); signingKeys = toArray(signingKeys); passwords = toArray(passwords);
checkMessage(message); checkOutputMessageFormat(format);
encryptionKeys = toArray(encryptionKeys); signingKeys = toArray(signingKeys); passwords = toArray(passwords);
signingKeyIDs = toArray(signingKeyIDs); encryptionKeyIDs = toArray(encryptionKeyIDs); signingUserIDs = toArray(signingUserIDs); encryptionUserIDs = toArray(encryptionUserIDs);
if (rest.detached) {
throw new Error("The `detached` option has been removed from openpgp.encrypt, separately call openpgp.sign instead. Don't forget to remove the `privateKeys` option as well.");
}
if (rest.publicKeys) throw new Error('The `publicKeys` option has been removed from openpgp.encrypt, pass `encryptionKeys` instead');
if (rest.privateKeys) throw new Error('The `privateKeys` option has been removed from openpgp.encrypt, pass `signingKeys` instead');
if (rest.armor !== undefined) throw new Error('The `armor` option has been removed from openpgp.encrypt, pass `format` instead.');
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
if (!signingKeys) {
signingKeys = [];
}
const streaming = message.fromStream;
try {
if (signingKeys.length || signature) { // sign the message only if signing keys or signature is specified
@ -270,6 +283,9 @@ export async function encrypt({ message, encryptionKeys, signingKeys, passwords,
config
);
message = await message.encrypt(encryptionKeys, passwords, sessionKey, wildcard, encryptionKeyIDs, date, encryptionUserIDs, config);
if (format === 'object') return message;
// serialize data
const armor = format === 'armor';
const data = armor ? message.armor(config) : message.write();
return convertStream(data, streaming, armor ? 'utf8' : 'binary');
} catch (err) {
@ -313,6 +329,7 @@ export async function decrypt({ message, decryptionKeys, passwords, sessionKeys,
checkMessage(message); verificationKeys = toArray(verificationKeys); decryptionKeys = toArray(decryptionKeys); passwords = toArray(passwords); sessionKeys = toArray(sessionKeys);
if (rest.privateKeys) throw new Error('The `privateKeys` option has been removed from openpgp.decrypt, pass `decryptionKeys` instead');
if (rest.publicKeys) throw new Error('The `publicKeys` option has been removed from openpgp.decrypt, pass `verificationKeys` instead');
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
try {
const decrypted = await message.decrypt(decryptionKeys, passwords, sessionKeys, date, config);
@ -359,24 +376,28 @@ export async function decrypt({ message, decryptionKeys, passwords, sessionKeys,
* @param {Object} options
* @param {CleartextMessage|Message} options.message - (cleartext) message to be signed
* @param {PrivateKey|PrivateKey[]} options.signingKeys - Array of keys or single key with decrypted secret key data to sign cleartext
* @param {Boolean} [options.armor=true] - Whether the return values should be ascii armored (true, the default) or binary (false)
* @param {'armor'|'binary'|'object'} [options.format='armor'] - Format of the returned message
* @param {Boolean} [options.detached=false] - If the return value should contain a detached signature
* @param {KeyID|KeyID[]} [options.signingKeyIDs=latest-created valid signing (sub)keys] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to signingKeys[i]
* @param {Date} [options.date=current date] - Override the creation date of the signature
* @param {Object|Object[]} [options.signingUserIDs=primary user IDs] - Array of user IDs to sign with, one per key in `signingKeys`, e.g. `[{ name: 'Steve Sender', email: 'steve@openpgp.org' }]`
* @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
* @returns {Promise<MaybeStream<String>|MaybeStream<Uint8Array>>} Signed message (string if `armor` was true, the default; Uint8Array if `armor` was false).
* @returns {Promise<MaybeStream<String|Uint8Array>>} Signed message (string if `armor` was true, the default; Uint8Array if `armor` was false).
* @async
* @static
*/
export async function sign({ message, signingKeys, armor = true, detached = false, signingKeyIDs = [], date = new Date(), signingUserIDs = [], config, ...rest }) {
export async function sign({ message, signingKeys, format = 'armor', detached = false, signingKeyIDs = [], date = new Date(), signingUserIDs = [], config, ...rest }) {
config = { ...defaultConfig, ...config };
checkCleartextOrMessage(message);
if (rest.privateKeys) throw new Error('The `privateKeys` option has been removed from openpgp.sign, pass `signingKeys` instead');
if (message instanceof CleartextMessage && !armor) throw new Error("Can't sign non-armored cleartext message");
if (message instanceof CleartextMessage && detached) throw new Error("Can't detach-sign a cleartext message");
checkCleartextOrMessage(message); checkOutputMessageFormat(format);
signingKeys = toArray(signingKeys); signingKeyIDs = toArray(signingKeyIDs); signingUserIDs = toArray(signingUserIDs);
if (rest.privateKeys) throw new Error('The `privateKeys` option has been removed from openpgp.sign, pass `signingKeys` instead');
if (rest.armor !== undefined) throw new Error('The `armor` option has been removed from openpgp.sign, pass `format` instead.');
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
if (message instanceof CleartextMessage && format === 'binary') throw new Error('Cannot return signed cleartext message in binary format');
if (message instanceof CleartextMessage && detached) throw new Error('Cannot detach-sign a cleartext message');
if (!signingKeys || signingKeys.length === 0) {
throw new Error('No signing keys provided');
}
@ -388,6 +409,9 @@ export async function sign({ message, signingKeys, armor = true, detached = fals
} else {
signature = await message.sign(signingKeys, undefined, signingKeyIDs, date, signingUserIDs, config);
}
if (format === 'object') return signature;
const armor = format === 'armor';
signature = armor ? signature.armor(config) : signature.write();
if (detached) {
signature = stream.transformPair(message.packets.write(), async (readable, writable) => {
@ -431,13 +455,13 @@ export async function sign({ message, signingKeys, armor = true, detached = fals
*/
export async function verify({ message, verificationKeys, expectSigned = false, format = 'utf8', signature = null, date = new Date(), config, ...rest }) {
config = { ...defaultConfig, ...config };
checkCleartextOrMessage(message);
checkCleartextOrMessage(message); verificationKeys = toArray(verificationKeys);
if (rest.publicKeys) throw new Error('The `publicKeys` option has been removed from openpgp.verify, pass `verificationKeys` instead');
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
if (message instanceof CleartextMessage && format === 'binary') throw new Error("Can't return cleartext message data as binary");
if (message instanceof CleartextMessage && signature) throw new Error("Can't verify detached cleartext signature");
verificationKeys = toArray(verificationKeys);
try {
const result = {};
if (signature) {
@ -487,6 +511,7 @@ export async function generateSessionKey({ encryptionKeys, date = new Date(), en
config = { ...defaultConfig, ...config };
encryptionKeys = toArray(encryptionKeys); encryptionUserIDs = toArray(encryptionUserIDs);
if (rest.publicKeys) throw new Error('The `publicKeys` option has been removed from openpgp.generateSessionKey, pass `encryptionKeys` instead');
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
try {
const sessionKeys = await Message.generateSessionKey(encryptionKeys, date, encryptionUserIDs, config);
@ -505,7 +530,7 @@ export async function generateSessionKey({ encryptionKeys, date = new Date(), en
* @param {String} [options.aeadAlgorithm] - AEAD algorithm, e.g. 'eax' or 'ocb'
* @param {PublicKey|PublicKey[]} [options.encryptionKeys] - Array of public keys or single key, used to encrypt the key
* @param {String|String[]} [options.passwords] - Passwords for the message
* @param {Boolean} [options.armor=true] - Whether the return values should be ascii armored (true, the default) or binary (false)
* @param {'armor'|'binary'} [options.format='armor'] - Format of the returned value
* @param {Boolean} [options.wildcard=false] - Use a key ID of 0 instead of the public key IDs
* @param {KeyID|KeyID[]} [options.encryptionKeyIDs=latest-created valid encryption (sub)keys] - Array of key IDs to use for encryption. Each encryptionKeyIDs[i] corresponds to encryptionKeys[i]
* @param {Date} [options.date=current date] - Override the date
@ -515,14 +540,16 @@ export async function generateSessionKey({ encryptionKeys, date = new Date(), en
* @async
* @static
*/
export async function encryptSessionKey({ data, algorithm, aeadAlgorithm, encryptionKeys, passwords, armor = true, wildcard = false, encryptionKeyIDs = [], date = new Date(), encryptionUserIDs = [], config, ...rest }) {
export async function encryptSessionKey({ data, algorithm, aeadAlgorithm, encryptionKeys, passwords, format = 'armor', wildcard = false, encryptionKeyIDs = [], date = new Date(), encryptionUserIDs = [], config, ...rest }) {
config = { ...defaultConfig, ...config };
checkBinary(data); checkString(algorithm, 'algorithm'); encryptionKeys = toArray(encryptionKeys); passwords = toArray(passwords); encryptionKeyIDs = toArray(encryptionKeyIDs); encryptionUserIDs = toArray(encryptionUserIDs);
checkBinary(data); checkString(algorithm, 'algorithm'); checkOutputMessageFormat(format);
encryptionKeys = toArray(encryptionKeys); passwords = toArray(passwords); encryptionKeyIDs = toArray(encryptionKeyIDs); encryptionUserIDs = toArray(encryptionUserIDs);
if (rest.publicKeys) throw new Error('The `publicKeys` option has been removed from openpgp.encryptSessionKey, pass `encryptionKeys` instead');
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
try {
const message = await Message.encryptSessionKey(data, algorithm, aeadAlgorithm, encryptionKeys, passwords, wildcard, encryptionKeyIDs, date, encryptionUserIDs, config);
return armor ? message.armor(config) : message.write();
return formatObject(message, format, config);
} catch (err) {
throw util.wrapError('Error encrypting session key', err);
}
@ -547,6 +574,7 @@ export async function decryptSessionKeys({ message, decryptionKeys, passwords, d
config = { ...defaultConfig, ...config };
checkMessage(message); decryptionKeys = toArray(decryptionKeys); passwords = toArray(passwords);
if (rest.privateKeys) throw new Error('The `privateKeys` option has been removed from openpgp.decryptSessionKeys, pass `decryptionKeys` instead');
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
try {
const sessionKeys = await message.decryptSessionKeys(decryptionKeys, passwords, date, config);
@ -588,6 +616,11 @@ function checkCleartextOrMessage(message) {
throw new Error('Parameter [message] needs to be of type Message or CleartextMessage');
}
}
function checkOutputMessageFormat(format) {
if (format !== 'armor' && format !== 'binary' && format !== 'object') {
throw new Error(`Unsupported format ${format}`);
}
}
/**
* Normalize parameter to an array if it is not undefined.
@ -652,20 +685,20 @@ function linkStreams(result, message) {
}
/**
* Convert the key object to the given format
* @param {Key} key
* Convert the object to the given format
* @param {Key|Message} object
* @param {'armor'|'binary'|'object'} format
* @param {Object} config - Full configuration
* @returns {String|Uint8Array|Object}
*/
function formatKey(key, format, config) {
function formatObject(object, format, config) {
switch (format) {
case 'object':
return key;
return object;
case 'armor':
return key.armor(config);
return object.armor(config);
case 'binary':
return key.write();
return object.write();
default:
throw new Error(`Unsupported format ${format}`);
}

View File

@ -71,7 +71,7 @@ export class Signature {
* @async
* @static
*/
export async function readSignature({ armoredSignature, binarySignature, config }) {
export async function readSignature({ armoredSignature, binarySignature, config, ...rest }) {
config = { ...defaultConfig, ...config };
let input = armoredSignature || binarySignature;
if (!input) {
@ -83,6 +83,8 @@ export async function readSignature({ armoredSignature, binarySignature, config
if (binarySignature && !util.isUint8Array(binarySignature)) {
throw new Error('readSignature: options.binarySignature must be a Uint8Array');
}
const unknownOptions = Object.keys(rest); if (unknownOptions.length > 0) throw new Error(`Unknown option: ${unknownOptions.join(', ')}`);
if (armoredSignature) {
const { type, data } = await unarmor(input, config);
if (type !== enums.armor.signature) {

View File

@ -182,11 +182,11 @@ vAFM3jjrAQDgJPXsv8PqCrLGDuMa/2r6SgzYd03aw/xt1WM6hgUvhQD+J54Z
const userIDs = { name: 'Test User', email: 'text2@example.com' };
const { privateKey } = await openpgp.generateKey({ userIDs, format: 'object' });
const encKey = await openpgp.encryptKey({ privateKey, userIDs, passphrase });
const encKey = await openpgp.encryptKey({ privateKey, passphrase });
expect(encKey.keyPacket.s2k.c).to.equal(openpgp.config.s2kIterationCountByte);
const config = { s2kIterationCountByte: 123 };
const encKey2 = await openpgp.encryptKey({ privateKey, userIDs, passphrase, config });
const encKey2 = await openpgp.encryptKey({ privateKey, passphrase, config });
expect(encKey2.keyPacket.s2k.c).to.equal(config.s2kIterationCountByte);
} finally {
openpgp.config.s2kIterationCountByte = s2kIterationCountByteVal;

View File

@ -2591,7 +2591,7 @@ function versionSpecificTests() {
expect(newKey.users.length).to.equal(1);
expect(newKey.users[0].userID.userID).to.equal('test <a@b.com>');
expect(newKey.isDecrypted()).to.be.true;
return openpgp.sign({ message: await openpgp.createCleartextMessage({ text: 'hello' }), signingKeys: newKey, armor: true }).then(async function(signed) {
return openpgp.sign({ message: await openpgp.createCleartextMessage({ text: 'hello' }), signingKeys: newKey }).then(async function(signed) {
return openpgp.verify(
{ message: await openpgp.readCleartextMessage({ cleartextMessage: signed }), verificationKeys: newKeyPublic }
).then(async function(verified) {
@ -2629,7 +2629,7 @@ function versionSpecificTests() {
const opt2 = { privateKey, userIDs: userID2, format: 'object' };
return openpgp.reformatKey(opt2).then(async function({ privateKey: newKey, publicKey: newKeyPublic }) {
const encrypted = await openpgp.encrypt({
message: await openpgp.createMessage({ text: 'hello' }), encryptionKeys: newKey.toPublic(), signingKeys: newKey, armor: true, config: { minRSABits: 1024 }
message: await openpgp.createMessage({ text: 'hello' }), encryptionKeys: newKey.toPublic(), signingKeys: newKey, config: { minRSABits: 1024 }
});
const decrypted = await openpgp.decrypt({
message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: newKey, verificationKeys: newKeyPublic, config: { minRSABits: 1024 }
@ -3442,10 +3442,10 @@ VYGdb3eNlV8CfoEC
expect(sessionKey.algorithm).to.equal('aes128');
const config = { minRSABits: 1024 };
await openpgp.encrypt({
message: await openpgp.createMessage({ text: 'hello' }), encryptionKeys: publicKey, signingKeys: privateKey, encryptionUserIDs: { name: 'Test User', email: 'b@c.com' }, armor: false, config
message: await openpgp.createMessage({ text: 'hello' }), encryptionKeys: publicKey, signingKeys: privateKey, encryptionUserIDs: { name: 'Test User', email: 'b@c.com' }, format: 'binary', config
});
await expect(openpgp.encrypt({
message: await openpgp.createMessage({ text: 'hello' }), encryptionKeys: publicKey, signingKeys: privateKey, encryptionUserIDs: { name: 'Test User', email: 'c@c.com' }, armor: false, config
message: await openpgp.createMessage({ text: 'hello' }), encryptionKeys: publicKey, signingKeys: privateKey, encryptionUserIDs: { name: 'Test User', email: 'c@c.com' }, format: 'binary', config
})).to.be.rejectedWith('Could not find user that matches that user ID');
});
@ -3456,7 +3456,7 @@ VYGdb3eNlV8CfoEC
privateKey: await openpgp.readKey({ armoredKey: uidlessKey }),
passphrase: 'correct horse battery staple'
});
await expect(openpgp.encrypt({ message: await openpgp.createMessage({ text: 'hello' }), encryptionKeys: publicKey, signingKeys: privateKey, armor: false })).to.be.rejectedWith('Could not find primary user');
await expect(openpgp.encrypt({ message: await openpgp.createMessage({ text: 'hello' }), encryptionKeys: publicKey, signingKeys: privateKey, format: 'binary' })).to.be.rejectedWith('Could not find primary user');
});
it('Sign - specific user', async function() {
@ -3476,17 +3476,17 @@ VYGdb3eNlV8CfoEC
privateKey.users[1].selfCertifications[0].preferredHashAlgorithms = [openpgp.enums.hash.sha512];
const config = { minRSABits: 1024 };
const signed = await openpgp.sign({
message: await openpgp.createMessage({ text: 'hello' }), signingKeys: privateKey, signingUserIDs: { name: 'Test McTestington', email: 'test@example.com' }, armor: false, config
message: await openpgp.createMessage({ text: 'hello' }), signingKeys: privateKey, signingUserIDs: { name: 'Test McTestington', email: 'test@example.com' }, format: 'binary', config
});
const signature = await openpgp.readMessage({ binaryMessage: signed });
expect(signature.packets[0].hashAlgorithm).to.equal(openpgp.enums.hash.sha512);
const encrypted = await openpgp.encrypt({
message: await openpgp.createMessage({ text: 'hello' }), passwords: 'test', signingKeys: privateKey, signingUserIDs: { name: 'Test McTestington', email: 'test@example.com' }, armor: false, config
message: await openpgp.createMessage({ text: 'hello' }), passwords: 'test', signingKeys: privateKey, signingUserIDs: { name: 'Test McTestington', email: 'test@example.com' }, format: 'binary', config
});
const { signatures } = await openpgp.decrypt({ message: await openpgp.readMessage({ binaryMessage: encrypted }), passwords: 'test' });
expect((await signatures[0].signature).packets[0].hashAlgorithm).to.equal(openpgp.enums.hash.sha512);
await expect(openpgp.encrypt({
message: await openpgp.createMessage({ text: 'hello' }), encryptionKeys: publicKey, signingKeys: privateKey, signingUserIDs: { name: 'Not Test McTestington', email: 'test@example.com' }, armor: false, config
message: await openpgp.createMessage({ text: 'hello' }), encryptionKeys: publicKey, signingKeys: privateKey, signingUserIDs: { name: 'Not Test McTestington', email: 'test@example.com' }, format: 'binary', config
})).to.be.rejectedWith('Could not find user that matches that user ID');
});
@ -3768,7 +3768,7 @@ VYGdb3eNlV8CfoEC
expect(subkey.getAlgorithmInfo().algorithm).to.be.equal('eddsa');
await subkey.verify();
expect(await newPrivateKey.getSigningKey()).to.be.equal(subkey);
const signed = await openpgp.sign({ message: await openpgp.createMessage({ text: 'the data to signed' }), signingKeys: newPrivateKey, armor:false });
const signed = await openpgp.sign({ message: await openpgp.createMessage({ text: 'the data to signed' }), signingKeys: newPrivateKey, format: 'binary' });
const message = await openpgp.readMessage({ binaryMessage: signed });
const { signatures } = await openpgp.verify({ message, verificationKeys: [newPrivateKey.toPublic()] });
expect(signatures).to.exist;
@ -3790,7 +3790,7 @@ VYGdb3eNlV8CfoEC
const publicKey = newPrivateKey.toPublic();
await subkey.verify();
expect(await newPrivateKey.getEncryptionKey()).to.be.equal(subkey);
const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: vData }), encryptionKeys: publicKey, armor:false });
const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: vData }), encryptionKeys: publicKey, format: 'binary' });
expect(encrypted).to.be.exist;
const message = await openpgp.readMessage({ binaryMessage: encrypted });
const pkSessionKeys = message.packets.filterByTag(openpgp.enums.packet.publicKeyEncryptedSessionKey);
@ -3817,7 +3817,7 @@ VYGdb3eNlV8CfoEC
expect(subkey.getAlgorithmInfo().algorithm).to.be.equal('rsaEncryptSign');
await subkey.verify();
expect(await newPrivateKey.getSigningKey()).to.be.equal(subkey);
const signed = await openpgp.sign({ message: await openpgp.createMessage({ text: 'the data to signed' }), signingKeys: newPrivateKey, armor:false });
const signed = await openpgp.sign({ message: await openpgp.createMessage({ text: 'the data to signed' }), signingKeys: newPrivateKey, format: 'binary' });
const message = await openpgp.readMessage({ binaryMessage: signed });
const { signatures } = await openpgp.verify({ message, verificationKeys: [newPrivateKey.toPublic()] });
expect(signatures).to.exist;
@ -3840,7 +3840,7 @@ VYGdb3eNlV8CfoEC
const publicKey = newPrivateKey.toPublic();
const vData = 'the data to encrypted!';
expect(await newPrivateKey.getEncryptionKey()).to.be.equal(subkey);
const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: vData }), encryptionKeys: publicKey, armor:false });
const encrypted = await openpgp.encrypt({ message: await openpgp.createMessage({ text: vData }), encryptionKeys: publicKey, format: 'binary' });
expect(encrypted).to.be.exist;
const message = await openpgp.readMessage({ binaryMessage: encrypted });
const pkSessionKeys = message.packets.filterByTag(openpgp.enums.packet.publicKeyEncryptedSessionKey);

View File

@ -1639,6 +1639,187 @@ aOU=
message: await openpgp.createMessage({ text: plaintext })
})).to.be.rejectedWith(/No signing keys provided/);
});
it('should output cleartext message of expected format', async function() {
const text = 'test';
const message = await openpgp.createCleartextMessage({ text });
const privateKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key }),
passphrase
});
const config = { minRSABits: 1024 };
const cleartextMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'armor' });
const parsedArmored = await openpgp.readCleartextMessage({ cleartextMessage });
expect(parsedArmored.text).to.equal(text);
expect(parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
await expect(openpgp.sign({ message, signingKeys: privateKey, config, format: 'binary' })).to.be.rejectedWith('');
const objectMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'object' });
expect(objectMessage.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
const verified = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config });
expect(verified.data).to.equal(text);
});
it('should output message of expected format', async function() {
const text = 'test';
const message = await openpgp.createMessage({ text });
const privateKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key }),
passphrase
});
const config = { minRSABits: 1024 };
const armoredMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'armor' });
const parsedArmored = await openpgp.readMessage({ armoredMessage });
expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);
const binaryMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'binary' });
const parsedBinary = await openpgp.readMessage({ binaryMessage });
expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);
const objectMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'object' });
expect(objectMessage.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);
const verified = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config });
expect(verified.data).to.equal(text);
});
it('should output message of expected format', async function() {
const text = 'test';
const message = await openpgp.createMessage({ text });
const privateKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key }),
passphrase
});
const config = { minRSABits: 1024 };
const armoredMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'armor' });
const parsedArmored = await openpgp.readMessage({ armoredMessage });
expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);
const binaryMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'binary' });
const parsedBinary = await openpgp.readMessage({ binaryMessage });
expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);
const objectMessage = await openpgp.sign({ message, signingKeys: privateKey, config, format: 'object' });
expect(objectMessage.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);
const verified = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config });
expect(verified.data).to.equal(text);
});
it('should output message of expected format (with streaming)', async function() {
const text = 'test';
const privateKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key }),
passphrase
});
const config = { minRSABits: 1024 };
const armoredMessage = await openpgp.sign({
message: await openpgp.createMessage({ text: stream.toStream(text) }),
signingKeys: privateKey,
format: 'armor',
config
});
const parsedArmored = await openpgp.readMessage({ armoredMessage });
expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);
const binaryMessage = await openpgp.sign({
message: await openpgp.createMessage({ text: stream.toStream(text) }),
signingKeys: privateKey,
format: 'binary',
config
});
const parsedBinary = await openpgp.readMessage({ binaryMessage });
expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);
const objectMessage = await openpgp.sign({
message: await openpgp.createMessage({ text: stream.toStream(text) }),
signingKeys: privateKey,
format: 'object',
config
});
expect(objectMessage.packets.filterByTag(openpgp.enums.packet.onePassSignature)).to.have.length(1);
objectMessage.packets[1].data = await stream.readToEnd(objectMessage.packets[1].data);
objectMessage.packets[2].signedHashValue = await stream.readToEnd(objectMessage.packets[2].signedHashValue);
const { data: streamedData } = await openpgp.verify({ message: objectMessage, verificationKeys: privateKey, expectSigned: true, config });
expect(await stream.readToEnd(streamedData)).to.equal(text);
expect(streamedData).to.equal(text);
});
it('should output message of expected format (detached)', async function() {
const text = 'test';
const message = await openpgp.createMessage({ text });
const privateKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key }),
passphrase
});
const config = { minRSABits: 1024 };
const armoredSignature = await openpgp.sign({ message, signingKeys: privateKey, detached: true, config, format: 'armor' });
const parsedArmored = await openpgp.readSignature({ armoredSignature });
expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
const binarySignature = await openpgp.sign({ message, signingKeys: privateKey, detached: true, config, format: 'binary' });
const parsedBinary = await openpgp.readSignature({ binarySignature });
expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
const objectSignature = await openpgp.sign({ message, signingKeys: privateKey, detached: true, config, format: 'object' });
expect(objectSignature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
const verified = await openpgp.verify({ message, signature: objectSignature, verificationKeys: privateKey, expectSigned: true, config });
expect(verified.data).to.equal(text);
});
it('should output message of expected format (detached, with streaming)', async function() {
const text = 'test';
const privateKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key }),
passphrase
});
const config = { minRSABits: 1024 };
const armoredSignature = await openpgp.sign({
message: await openpgp.createMessage({ text: stream.toStream(text) }),
signingKeys: privateKey,
detached: true,
format: 'armor',
config
});
const parsedArmored = await openpgp.readSignature({ armoredSignature: await stream.readToEnd(armoredSignature) });
expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
const binarySignature = await openpgp.sign({
message: await openpgp.createMessage({ text: stream.toStream(text) }),
signingKeys: privateKey,
detached: true,
format: 'binary',
config
});
const parsedBinary = await openpgp.readSignature({ binarySignature: await stream.readToEnd(binarySignature) });
expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
const streamedMessage = await openpgp.createMessage({ text: stream.toStream(text) });
const objectSignature = await openpgp.sign({
message: streamedMessage,
signingKeys: privateKey,
detached: true,
format: 'object',
config
});
expect(objectSignature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
const armoredStreamedMessage = streamedMessage.armor(); // consume input message stream, to allow to read the signed hash
objectSignature.packets[0].signedHashValue = await stream.readToEnd(objectSignature.packets[0].signedHashValue);
const { data: streamedData } = await openpgp.verify({
message: await openpgp.readMessage({ armoredMessage: armoredStreamedMessage }),
signature: objectSignature,
verificationKeys: privateKey,
expectSigned: true,
config
});
expect(await stream.readToEnd(streamedData)).to.equal(text);
});
});
describe('encrypt - unit tests', function() {
@ -1648,6 +1829,87 @@ aOU=
openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: expiredKey })
).to.be.rejectedWith(/Primary key is expired/);
});
it('should output message of expected format', async function() {
const passwords = 'password';
const text = 'test';
const message = await openpgp.createMessage({ text });
const privateKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key }),
passphrase
});
const armoredMessage = await openpgp.encrypt({ message, passwords, format: 'armor' });
const parsedArmored = await openpgp.readMessage({ armoredMessage });
expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);
const binaryMessage = await openpgp.encrypt({ message, passwords, format: 'binary' });
const parsedBinary = await openpgp.readMessage({ binaryMessage });
expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);
const config = { minRSABits: 1024 };
const objectMessage = await openpgp.encrypt({ message, passwords, signingKeys: privateKey, config, format: 'object' });
expect(objectMessage.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);
const decrypted = await openpgp.decrypt({ message: objectMessage, passwords, verificationKeys: privateKey, expectSigned: true, config });
expect(decrypted.data).to.equal(text);
});
it('should output message of expected format (with streaming)', async function() {
const passwords = 'password';
const text = 'test';
const privateKey = await openpgp.decryptKey({
privateKey: await openpgp.readKey({ armoredKey: priv_key }),
passphrase
});
const armoredMessage = await openpgp.encrypt({
message: await openpgp.createMessage({ text: stream.toStream(text) }),
passwords,
format: 'armor'
});
const parsedArmored = await openpgp.readMessage({ armoredMessage });
expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);
const binaryMessage = await openpgp.encrypt({
message: await openpgp.createMessage({ text: stream.toStream(text) }),
passwords,
format: 'binary'
});
const parsedBinary = await openpgp.readMessage({ binaryMessage });
expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);
const config = { minRSABits: 1024 };
const objectMessage = await openpgp.encrypt({
message: await openpgp.createMessage({ text: stream.toStream(text) }),
passwords,
signingKeys: privateKey,
format: 'object',
config
});
expect(objectMessage.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);
const { data: streamedData } = await openpgp.decrypt({ message: objectMessage, passwords, verificationKeys: privateKey, expectSigned: true, config });
expect(await stream.readToEnd(streamedData)).to.equal(text);
});
});
describe('encryptSessionKey - unit tests', function() {
it('should output message of expected format', async function() {
const passwords = 'password';
const sessionKey = { data: new Uint8Array(16).fill(1), algorithm: 'aes128' };
const armoredMessage = await openpgp.encryptSessionKey({ ...sessionKey, passwords, format: 'armor' });
const parsedArmored = await openpgp.readMessage({ armoredMessage });
expect(parsedArmored.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);
const binaryMessage = await openpgp.encryptSessionKey({ ...sessionKey, passwords, format: 'binary' });
const parsedBinary = await openpgp.readMessage({ binaryMessage });
expect(parsedBinary.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);
const objectMessage = await openpgp.encryptSessionKey({ ...sessionKey, passwords, format: 'object' });
expect(objectMessage.packets.filterByTag(openpgp.enums.packet.symEncryptedSessionKey)).to.have.length(1);
const [decryptedSessionKey] = await openpgp.decryptSessionKeys({ message: objectMessage, passwords });
expect(decryptedSessionKey).to.deep.equal(sessionKey);
});
});
describe('encrypt, decrypt, sign, verify - integration tests', function() {
@ -1783,7 +2045,7 @@ aOU=
data: sk,
algorithm: 'aes128',
encryptionKeys: publicKey,
armor: false
format: 'binary'
}).then(async function(encrypted) {
const message = await openpgp.readMessage({ binaryMessage: encrypted });
return openpgp.decryptSessionKeys({
@ -1800,7 +2062,7 @@ aOU=
data: sk,
algorithm: 'aes128',
passwords: password1,
armor: false
format: 'binary'
}).then(async function(encrypted) {
const message = await openpgp.readMessage({ binaryMessage: encrypted });
return openpgp.decryptSessionKeys({
@ -1817,7 +2079,7 @@ aOU=
data: sk,
algorithm: 'aes128',
encryptionKeys: publicKey,
armor: false
format: 'binary'
}).then(async function(encrypted) {
const message = await openpgp.readMessage({ binaryMessage: encrypted });
const invalidPrivateKey = await openpgp.readKey({ armoredKey: priv_key });
@ -2552,7 +2814,7 @@ aOU=
it('should fail to decrypt unarmored message with garbage data appended', async function() {
const key = privateKey;
const message = await openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: key, signingKeys: key, armor: false });
const message = await openpgp.encrypt({ message: await openpgp.createMessage({ text: 'test' }), encryptionKeys: key, signingKeys: key, format: 'binary' });
const encrypted = util.concat([message, new Uint8Array([11])]);
await expect((async () => {
await openpgp.decrypt({ message: await openpgp.readMessage({ binaryMessage: encrypted }), decryptionKeys: key, verificationKeys: key });
@ -2705,7 +2967,7 @@ aOU=
const encOpt = {
message: await openpgp.createMessage({ text: plaintext }),
passwords: password1,
armor: false
format: 'binary'
};
const decOpt = {
passwords: password1
@ -2723,7 +2985,7 @@ aOU=
const encOpt = {
message: await openpgp.createMessage({ binary: new Uint8Array([0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]) }),
passwords: password1,
armor: false
format: 'binary'
};
const decOpt = {
passwords: password1,
@ -2950,7 +3212,7 @@ aOU=
const signOpt = {
message,
signingKeys: privateKey,
armor: false
format: 'binary'
};
const verifyOpt = {
verificationKeys: publicKey
@ -2974,7 +3236,7 @@ aOU=
message,
signingKeys: privateKey,
detached: true,
armor: false
format: 'binary'
};
const verifyOpt = {
message,
@ -3002,7 +3264,7 @@ aOU=
signingKeys: privateKey_1337,
detached: true,
date: past,
armor: false
format: 'binary'
};
const verifyOpt = {
message,
@ -3040,7 +3302,7 @@ aOU=
signingKeys: privateKey_2038_2045,
detached: true,
date: future,
armor: false
format: 'binary'
};
const verifyOpt = {
verificationKeys: publicKey_2038_2045,
@ -3066,7 +3328,7 @@ aOU=
const signOpt = {
message: await openpgp.createMessage({ binary: data }),
signingKeys: privateKey,
armor: false
format: 'binary'
};
const verifyOpt = {
verificationKeys: publicKey,
@ -3105,7 +3367,7 @@ aOU=
const signOpt = {
message: await openpgp.createMessage({ binary: dataStream }),
signingKeys: privateKey,
armor: false
format: 'binary'
};
const verifyOpt = {
verificationKeys: publicKey,
@ -3140,7 +3402,7 @@ aOU=
message: await openpgp.createMessage({ text: plaintext, date: future }),
encryptionKeys: publicKey_2038_2045,
date: future,
armor: false
format: 'binary'
};
return openpgp.encrypt(encryptOpt).then(async function (encrypted) {
@ -3161,7 +3423,7 @@ aOU=
message: await openpgp.createMessage({ binary: data, date: past }),
encryptionKeys: publicKey_2000_2008,
date: past,
armor: false
format: 'binary'
};
return openpgp.encrypt(encryptOpt).then(async function (encrypted) {
@ -3182,7 +3444,7 @@ aOU=
encryptionKeys: publicKey_2000_2008,
signingKeys: privateKey_2000_2008,
date: past,
armor: false
format: 'binary'
};
return openpgp.encrypt(encryptOpt).then(async function (encrypted) {
@ -3210,7 +3472,7 @@ aOU=
encryptionKeys: publicKey_2038_2045,
signingKeys: privateKey_2038_2045,
date: future,
armor: false
format: 'binary'
};
return openpgp.encrypt(encryptOpt).then(async function (encrypted) {
@ -3239,7 +3501,7 @@ aOU=
encryptionKeys: publicKey_2038_2045,
signingKeys: privateKey_2038_2045,
date: future,
armor: false
format: 'binary'
};
return openpgp.encrypt(encryptOpt).then(async function (encrypted) {

View File

@ -1462,7 +1462,7 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA
});
const config = { minRSABits: 1024 };
return openpgp.sign({ signingKeys: privKey, message: await openpgp.createMessage({ binary: plaintext }), armor: false, config }).then(async signed => {
return openpgp.sign({ signingKeys: privKey, message: await openpgp.createMessage({ binary: plaintext }), format: 'binary', config }).then(async signed => {
const message = await openpgp.readMessage({ binaryMessage: signed });
return openpgp.verify({ verificationKeys: pubKey, message, format: 'binary', config });

View File

@ -257,7 +257,7 @@ function tests() {
const encrypted = await openpgp.encrypt({
message: await openpgp.createMessage({ binary: data }),
passwords: ['test'],
armor: false
format: 'binary'
});
expect(stream.isStream(encrypted)).to.equal(expectedType);
@ -285,7 +285,7 @@ function tests() {
const encrypted = await openpgp.encrypt({
message: await openpgp.createMessage({ binary: data }),
passwords: ['test'],
armor: false
format: 'binary'
});
expect(stream.isStream(encrypted)).to.equal(expectedType);
@ -316,7 +316,7 @@ function tests() {
message: await openpgp.createMessage({ binary: data }),
encryptionKeys: pubKey,
signingKeys: privKey,
armor: false,
format: 'binary',
config: { minRSABits: 1024 }
});
expect(stream.isStream(encrypted)).to.equal(expectedType);
@ -352,7 +352,7 @@ function tests() {
message: await openpgp.createMessage({ binary: data }),
encryptionKeys: pub,
signingKeys: priv,
armor: false
format: 'binary'
});
expect(stream.isStream(encrypted)).to.equal(expectedType);
@ -387,7 +387,7 @@ function tests() {
message: await openpgp.createMessage({ binary: data }),
encryptionKeys: pub,
signingKeys: priv,
armor: false
format: 'binary'
});
expect(stream.isStream(encrypted)).to.equal(expectedType);
@ -814,7 +814,7 @@ function tests() {
const encrypted = await openpgp.encrypt({
message: await openpgp.createMessage({ binary: data }),
passwords: ['test'],
armor: false
format: 'binary'
});
expect(stream.isStream(encrypted)).to.equal(expectedType);
@ -1035,7 +1035,7 @@ module.exports = () => describe('Streaming', function() {
const encrypted = await openpgp.encrypt({
message: await openpgp.createMessage({ binary: data }),
passwords: ['test'],
armor: false
format: 'binary'
});
expect(stream.isStream(encrypted)).to.equal('node');

View File

@ -21,14 +21,12 @@ async function generateTestData() {
type: 'rsa',
rsaBits: 2048,
subkeys: [],
sign: false,
format: 'object'
});
const signed = await openpgp.sign({
message: await createCleartextMessage({ text: 'I am batman' }),
signingKeys: victimPrivKey,
armor: true
signingKeys: victimPrivKey
});
return {
victimPubKey: victimPrivKey.toPublic(),
@ -37,7 +35,7 @@ async function generateTestData() {
};
}
async function testSubkeyTrust() {
module.exports = () => it('Does not trust subkeys without Primary Key Binding Signature', async function() {
// attacker only has his own private key,
// the victim's public key and a signed message
const { victimPubKey, attackerPrivKey, signed } = await generateTestData();
@ -68,8 +66,7 @@ async function testSubkeyTrust() {
message: await readCleartextMessage({ cleartextMessage: signed }),
verificationKeys: fakeKey
});
// expect the signature to have the expected keyID, but be invalid due to fake key binding signature in the subkey
expect(verifyAttackerIsBatman.signatures[0].keyID.equals(victimPubKey.subkeys[0].getKeyID())).to.be.true;
await expect(verifyAttackerIsBatman.signatures[0].verified).to.be.rejectedWith(/Could not find valid signing key packet/);
}
module.exports = () => it('Does not trust subkeys without Primary Key Binding Signature', testSubkeyTrust);
});

View File

@ -12,13 +12,13 @@ import {
readMessage, createMessage, Message, createCleartextMessage,
encrypt, decrypt, sign, verify, config, enums,
generateSessionKey, encryptSessionKey, decryptSessionKeys,
LiteralDataPacket, PacketList, CompressedDataPacket, PublicKeyPacket, PublicSubkeyPacket, SecretKeyPacket, SecretSubkeyPacket
LiteralDataPacket, PacketList, CompressedDataPacket, PublicKeyPacket, PublicSubkeyPacket, SecretKeyPacket, SecretSubkeyPacket, CleartextMessage
} from '../..';
(async () => {
// Generate keys
const keyOptions = { userIDs: [{ email: "user@corp.co" }], config: { v5Keys: true } };
const keyOptions = { userIDs: [{ email: 'user@corp.co' }], config: { v5Keys: true } };
const { privateKey: privateKeyArmored, publicKey: publicKeyArmored } = await generateKey(keyOptions);
const { privateKey: privateKeyBinary } = await generateKey({ ...keyOptions, format: 'binary' });
const { privateKey, publicKey, revocationCertificate } = await generateKey({ ...keyOptions, format: 'object' });
@ -75,7 +75,7 @@ import {
// Encrypt binary message (unarmored)
const binary = new Uint8Array([1, 2]);
const binaryMessage = await createMessage({ binary });
const encryptedBinary: Uint8Array = await encrypt({ encryptionKeys: publicKeys, message: binaryMessage, armor: false });
const encryptedBinary: Uint8Array = await encrypt({ encryptionKeys: publicKeys, message: binaryMessage, format: 'binary' });
expect(encryptedBinary).to.be.instanceOf(Uint8Array);
// Decrypt text message (armored)
@ -91,14 +91,17 @@ import {
expect(decryptedBinaryData).to.deep.equal(binary);
// Encrypt message (inspect packets)
const encryptedMessage = await readMessage({ binaryMessage: encryptedBinary });
expect(encryptedMessage).to.be.instanceOf(Message);
const encryptedBinaryObject: Message<Uint8Array> = await encrypt({ encryptionKeys: publicKeys, message: binaryMessage, format: 'object' });
expect(encryptedBinaryObject).to.be.instanceOf(Message);
const encryptedTextObject: Message<string> = await encrypt({ encryptionKeys: publicKeys, message: textMessage, format: 'object' });
expect(encryptedTextObject).to.be.instanceOf(Message);
// Session key functions
// Get session keys from encrypted message
const sessionKeys = await decryptSessionKeys({ message: await readMessage({ binaryMessage: encryptedBinary }), decryptionKeys: privateKeys });
expect(sessionKeys).to.have.length(1);
// eslint-disable-next-line no-unused-vars
const encryptedSessionKeys: string = await encryptSessionKey({ ...sessionKeys[0], passwords: 'pass', algorithm: 'aes128', aeadAlgorithm: 'eax' });
expect(encryptedSessionKeys).to.include('-----BEGIN PGP MESSAGE-----');
const newSessionKey = await generateSessionKey({ encryptionKeys: privateKey.toPublic() });
expect(newSessionKey.data).to.exist;
expect(newSessionKey.algorithm).to.exist;
@ -107,6 +110,8 @@ import {
const cleartextMessage = await createCleartextMessage({ text: 'hello' });
const clearSignedArmor = await sign({ signingKeys: privateKeys, message: cleartextMessage });
expect(clearSignedArmor).to.include('-----BEGIN PGP SIGNED MESSAGE-----');
const clearSignedObject: CleartextMessage = await sign({ signingKeys: privateKeys, message: cleartextMessage, format: 'object' });
expect(clearSignedObject).to.be.instanceOf(CleartextMessage);
// @ts-expect-error PublicKey not assignable to PrivateKey
try { await sign({ signingKeys: publicKeys, message: cleartextMessage }); } catch (e) {}
// @ts-expect-error Key not assignable to PrivateKey
@ -115,10 +120,14 @@ import {
// Sign text message (armored)
const textSignedArmor: string = await sign({ signingKeys: privateKeys, message: textMessage });
expect(textSignedArmor).to.include('-----BEGIN PGP MESSAGE-----');
// Sign text message (unarmored)
const textSignedBinary: Uint8Array = await sign({ signingKeys: privateKeys, message: binaryMessage, armor: false });
const textSignedBinary: Uint8Array = await sign({ signingKeys: privateKeys, message: binaryMessage, format: 'binary' });
expect(textSignedBinary).to.be.instanceOf(Uint8Array);
// Sign text and binary messages (inspect packages)
const binarySignedObject: Message<Uint8Array> = await sign({ signingKeys: privateKeys, message: binaryMessage, format: 'object' });
expect(binarySignedObject).to.be.instanceOf(Message);
const textSignedObject: Message<string> = await sign({ signingKeys: privateKeys, message: textMessage, format: 'object' });
expect(textSignedObject).to.be.instanceOf(Message);
// Verify signed text message (armored)
const signedMessage = await readMessage({ armoredMessage: textSignedArmor });
@ -168,7 +177,7 @@ import {
// // Detached - sign binary message (unarmored)
// const message = await createMessage({ text });
// const signed = await sign({ privateKeys, message, detached: true, armor: false });
// const signed = await sign({ privateKeys, message, detached: true, format: 'binary' });
// console.log(signed); // Uint8Array
// // Streaming - encrypt text message on Node.js (armored)
@ -182,7 +191,7 @@ import {
// // Streaming - encrypt binary message on Node.js (unarmored)
// const data = fs.createReadStream(filename);
// const message = await createMessage({ binary: data });
// const encrypted = await encrypt({ publicKeys, message, armor: false });
// const encrypted = await encrypt({ publicKeys, message, format: 'binary' });
// encrypted.pipe(targetStream);
console.log('TypeScript definitions are correct');