mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-06-10 08:06:43 +00:00
Merge pull request #1802
Determine signature hash prefs based on recipient keys instead of signing key
This commit is contained in:
commit
fb72ea449a
@ -59,20 +59,22 @@ export class CleartextMessage {
|
||||
|
||||
/**
|
||||
* Sign the cleartext message
|
||||
* @param {Array<Key>} privateKeys - private keys with decrypted secret key data for signing
|
||||
* @param {Array<Key>} signingKeys - private keys with decrypted secret key data for signing
|
||||
* @param {Array<Key>} recipientKeys - recipient keys to get the signing preferences from
|
||||
* @param {Signature} [signature] - Any existing detached signature
|
||||
* @param {Array<module:type/keyid~KeyID>} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to privateKeys[i]
|
||||
* @param {Date} [date] - The creation time of the signature that should be created
|
||||
* @param {Array} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
|
||||
* @param {Array} [signingKeyIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
|
||||
* @param {Array} [recipientUserIDs] - User IDs associated with `recipientKeys` to get the signing preferences from
|
||||
* @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }]
|
||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||
* @returns {Promise<CleartextMessage>} New cleartext message with signed content.
|
||||
* @async
|
||||
*/
|
||||
async sign(privateKeys, signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], config = defaultConfig) {
|
||||
async sign(signingKeys, recipientKeys = [], signature = null, signingKeyIDs = [], date = new Date(), signingUserIDs = [], recipientUserIDs = [], notations = [], config = defaultConfig) {
|
||||
const literalDataPacket = new LiteralDataPacket();
|
||||
literalDataPacket.setText(this.text);
|
||||
const newSignature = new Signature(await createSignaturePackets(literalDataPacket, privateKeys, signature, signingKeyIDs, date, userIDs, notations, true, config));
|
||||
const newSignature = new Signature(await createSignaturePackets(literalDataPacket, signingKeys, recipientKeys, signature, signingKeyIDs, date, signingUserIDs, recipientUserIDs, notations, true, config));
|
||||
return new CleartextMessage(this.text, newSignature);
|
||||
}
|
||||
|
||||
|
@ -82,6 +82,9 @@ export async function generate(algo) {
|
||||
*/
|
||||
export async function sign(algo, hashAlgo, message, publicKey, privateKey, hashed) {
|
||||
if (hash.getHashByteLength(hashAlgo) < hash.getHashByteLength(getPreferredHashAlgo(algo))) {
|
||||
// Enforce digest sizes:
|
||||
// - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
|
||||
// - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
|
||||
throw new Error('Hash algorithm too weak for EdDSA.');
|
||||
}
|
||||
switch (algo) {
|
||||
@ -129,6 +132,9 @@ export async function sign(algo, hashAlgo, message, publicKey, privateKey, hashe
|
||||
*/
|
||||
export async function verify(algo, hashAlgo, { RS }, m, publicKey, hashed) {
|
||||
if (hash.getHashByteLength(hashAlgo) < hash.getHashByteLength(getPreferredHashAlgo(algo))) {
|
||||
// Enforce digest sizes:
|
||||
// - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
|
||||
// - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
|
||||
throw new Error('Hash algorithm too weak for EdDSA.');
|
||||
}
|
||||
switch (algo) {
|
||||
|
@ -46,7 +46,9 @@ export async function sign(oid, hashAlgo, message, publicKey, privateKey, hashed
|
||||
const curve = new CurveWithOID(oid);
|
||||
checkPublicPointEnconding(curve, publicKey);
|
||||
if (hash.getHashByteLength(hashAlgo) < hash.getHashByteLength(enums.hash.sha256)) {
|
||||
// Enforce digest sizes, since the constraint was already present in RFC4880bis:
|
||||
// see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
|
||||
// and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
|
||||
throw new Error('Hash algorithm too weak for EdDSA.');
|
||||
}
|
||||
const { RS: signature } = await eddsaSign(enums.publicKey.ed25519, hashAlgo, message, publicKey.subarray(1), privateKey, hashed);
|
||||
@ -73,6 +75,9 @@ export async function verify(oid, hashAlgo, { r, s }, m, publicKey, hashed) {
|
||||
const curve = new CurveWithOID(oid);
|
||||
checkPublicPointEnconding(curve, publicKey);
|
||||
if (hash.getHashByteLength(hashAlgo) < hash.getHashByteLength(enums.hash.sha256)) {
|
||||
// Enforce digest sizes, since the constraint was already present in RFC4880bis:
|
||||
// see https://tools.ietf.org/id/draft-ietf-openpgp-rfc4880bis-10.html#section-15-7.2
|
||||
// and https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
|
||||
throw new Error('Hash algorithm too weak for EdDSA.');
|
||||
}
|
||||
const RS = util.concatUint8Array([r, s]);
|
||||
|
@ -246,7 +246,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options, conf
|
||||
const signatureProperties = getKeySignatureProperties();
|
||||
signatureProperties.signatureType = enums.signature.key;
|
||||
|
||||
const signaturePacket = await helper.createSignaturePacket(dataToSign, null, secretKeyPacket, signatureProperties, options.date, undefined, undefined, undefined, config);
|
||||
const signaturePacket = await helper.createSignaturePacket(dataToSign, [], secretKeyPacket, signatureProperties, options.date, undefined, undefined, undefined, config);
|
||||
packetlist.push(signaturePacket);
|
||||
}
|
||||
|
||||
@ -262,7 +262,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options, conf
|
||||
signatureProperties.isPrimaryUserID = true;
|
||||
}
|
||||
|
||||
const signaturePacket = await helper.createSignaturePacket(dataToSign, null, secretKeyPacket, signatureProperties, options.date, undefined, undefined, undefined, config);
|
||||
const signaturePacket = await helper.createSignaturePacket(dataToSign, [], secretKeyPacket, signatureProperties, options.date, undefined, undefined, undefined, config);
|
||||
|
||||
return { userIDPacket, signaturePacket };
|
||||
})).then(list => {
|
||||
@ -286,7 +286,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options, conf
|
||||
// Add revocation signature packet for creating a revocation certificate.
|
||||
// This packet should be removed before returning the key.
|
||||
const dataToSign = { key: secretKeyPacket };
|
||||
packetlist.push(await helper.createSignaturePacket(dataToSign, null, secretKeyPacket, {
|
||||
packetlist.push(await helper.createSignaturePacket(dataToSign, [], secretKeyPacket, {
|
||||
signatureType: enums.signature.keyRevocation,
|
||||
reasonForRevocationFlag: enums.reasonForRevocation.noReason,
|
||||
reasonForRevocationString: ''
|
||||
|
@ -90,7 +90,7 @@ export async function createBindingSignature(subkey, primaryKey, options, config
|
||||
const signatureProperties = { signatureType: enums.signature.subkeyBinding };
|
||||
if (options.sign) {
|
||||
signatureProperties.keyFlags = [enums.keyFlags.signData];
|
||||
signatureProperties.embeddedSignature = await createSignaturePacket(dataToSign, null, subkey, {
|
||||
signatureProperties.embeddedSignature = await createSignaturePacket(dataToSign, [], subkey, {
|
||||
signatureType: enums.signature.keyBinding
|
||||
}, options.date, undefined, undefined, undefined, config);
|
||||
} else {
|
||||
@ -100,41 +100,95 @@ export async function createBindingSignature(subkey, primaryKey, options, config
|
||||
signatureProperties.keyExpirationTime = options.keyExpirationTime;
|
||||
signatureProperties.keyNeverExpires = false;
|
||||
}
|
||||
const subkeySignaturePacket = await createSignaturePacket(dataToSign, null, primaryKey, signatureProperties, options.date, undefined, undefined, undefined, config);
|
||||
const subkeySignaturePacket = await createSignaturePacket(dataToSign, [], primaryKey, signatureProperties, options.date, undefined, undefined, undefined, config);
|
||||
return subkeySignaturePacket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred signature hash algorithm of a key
|
||||
* @param {Key} [key] - The key to get preferences from
|
||||
* @param {SecretKeyPacket|SecretSubkeyPacket} keyPacket - key packet used for signing
|
||||
* Returns the preferred signature hash algorithm for a set of keys.
|
||||
* @param {Array<Key>} [targetKeys] - The keys to get preferences from
|
||||
* @param {SecretKeyPacket|SecretSubkeyPacket} signingKeyPacket - key packet used for signing
|
||||
* @param {Date} [date] - Use the given date for verification instead of the current time
|
||||
* @param {Object} [userID] - User ID
|
||||
* @param {Object} [targetUserID] - User IDs corresponding to `targetKeys` to get preferences from
|
||||
* @param {Object} config - full configuration
|
||||
* @returns {Promise<enums.hash>}
|
||||
* @async
|
||||
*/
|
||||
export async function getPreferredHashAlgo(key, keyPacket, date = new Date(), userID = {}, config) {
|
||||
let hashAlgo = config.preferredHashAlgorithm;
|
||||
let prefAlgo = hashAlgo;
|
||||
if (key) {
|
||||
const selfCertification = await key.getPrimarySelfSignature(date, userID, config);
|
||||
if (selfCertification.preferredHashAlgorithms) {
|
||||
[prefAlgo] = selfCertification.preferredHashAlgorithms;
|
||||
hashAlgo = crypto.hash.getHashByteLength(hashAlgo) <= crypto.hash.getHashByteLength(prefAlgo) ?
|
||||
prefAlgo : hashAlgo;
|
||||
export async function getPreferredHashAlgo(targetKeys, signingKeyPacket, date = new Date(), targetUserIDs = [], config) {
|
||||
/**
|
||||
* If `preferredSenderAlgo` appears in the prefs of all recipients, we pick it; otherwise, we use the
|
||||
* strongest supported algo (`defaultAlgo` is always implicitly supported by all keys).
|
||||
* if no keys are available, `preferredSenderAlgo` is returned.
|
||||
* For ECC signing key, the curve preferred hash is taken into account as well (see logic below).
|
||||
*/
|
||||
const defaultAlgo = enums.hash.sha256; // MUST implement
|
||||
const preferredSenderAlgo = config.preferredHashAlgorithm;
|
||||
|
||||
const supportedAlgosPerTarget = await Promise.all(targetKeys.map(async (key, i) => {
|
||||
const selfCertification = await key.getPrimarySelfSignature(date, targetUserIDs[i], config);
|
||||
const targetPrefs = selfCertification.preferredHashAlgorithms;
|
||||
return targetPrefs;
|
||||
}));
|
||||
const supportedAlgosMap = new Map(); // use Map over object to preserve numeric keys
|
||||
for (const supportedAlgos of supportedAlgosPerTarget) {
|
||||
for (const hashAlgo of supportedAlgos) {
|
||||
try {
|
||||
// ensure that `hashAlgo` is recognized/implemented by us, otherwise e.g. `getHashByteLength` will throw later on
|
||||
const supportedAlgo = enums.write(enums.hash, hashAlgo);
|
||||
supportedAlgosMap.set(
|
||||
supportedAlgo,
|
||||
supportedAlgosMap.has(supportedAlgo) ? supportedAlgosMap.get(supportedAlgo) + 1 : 1
|
||||
);
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
switch (keyPacket.algorithm) {
|
||||
case enums.publicKey.ecdsa:
|
||||
case enums.publicKey.eddsaLegacy:
|
||||
case enums.publicKey.ed25519:
|
||||
case enums.publicKey.ed448:
|
||||
prefAlgo = crypto.getPreferredCurveHashAlgo(keyPacket.algorithm, keyPacket.publicParams.oid);
|
||||
const isSupportedHashAlgo = hashAlgo => targetKeys.length === 0 || supportedAlgosMap.get(hashAlgo) === targetKeys.length || hashAlgo === defaultAlgo;
|
||||
const getStrongestSupportedHashAlgo = () => {
|
||||
if (supportedAlgosMap.size === 0) {
|
||||
return defaultAlgo;
|
||||
}
|
||||
const sortedHashAlgos = Array.from(supportedAlgosMap.keys())
|
||||
.filter(hashAlgo => isSupportedHashAlgo(hashAlgo))
|
||||
.sort((algoA, algoB) => crypto.hash.getHashByteLength(algoA) - crypto.hash.getHashByteLength(algoB));
|
||||
const strongestHashAlgo = sortedHashAlgos[0];
|
||||
// defaultAlgo is always implicilty supported, and might be stronger than the rest
|
||||
return crypto.hash.getHashByteLength(strongestHashAlgo) >= crypto.hash.getHashByteLength(defaultAlgo) ? strongestHashAlgo : defaultAlgo;
|
||||
};
|
||||
|
||||
const eccAlgos = new Set([
|
||||
enums.publicKey.ecdsa,
|
||||
enums.publicKey.eddsaLegacy,
|
||||
enums.publicKey.ed25519,
|
||||
enums.publicKey.ed448
|
||||
]);
|
||||
|
||||
if (eccAlgos.has(signingKeyPacket.algorithm)) {
|
||||
// For ECC, the returned hash algo MUST be at least as strong as `preferredCurveHashAlgo`, see:
|
||||
// - ECDSA: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.2-5
|
||||
// - EdDSALegacy: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.3-3
|
||||
// - Ed25519: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.4-4
|
||||
// - Ed448: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.5-4
|
||||
// Hence, we return the `preferredHashAlgo` as long as it's supported and strong enough;
|
||||
// Otherwise, we look at the strongest supported algo, and ultimately fallback to the curve
|
||||
// preferred algo, even if not supported by all targets.
|
||||
const preferredCurveAlgo = crypto.getPreferredCurveHashAlgo(signingKeyPacket.algorithm, signingKeyPacket.publicParams.oid);
|
||||
|
||||
const preferredSenderAlgoIsSupported = isSupportedHashAlgo(preferredSenderAlgo);
|
||||
const preferredSenderAlgoStrongerThanCurveAlgo = crypto.hash.getHashByteLength(preferredSenderAlgo) >= crypto.hash.getHashByteLength(preferredCurveAlgo);
|
||||
|
||||
if (preferredSenderAlgoIsSupported && preferredSenderAlgoStrongerThanCurveAlgo) {
|
||||
return preferredSenderAlgo;
|
||||
} else {
|
||||
const strongestSupportedAlgo = getStrongestSupportedHashAlgo();
|
||||
return crypto.hash.getHashByteLength(strongestSupportedAlgo) >= crypto.hash.getHashByteLength(preferredCurveAlgo) ?
|
||||
strongestSupportedAlgo :
|
||||
preferredCurveAlgo;
|
||||
}
|
||||
}
|
||||
|
||||
return crypto.hash.getHashByteLength(hashAlgo) <= crypto.hash.getHashByteLength(prefAlgo) ?
|
||||
prefAlgo : hashAlgo;
|
||||
// `preferredSenderAlgo` may be weaker than the default, but we do not guard against this,
|
||||
// since it was manually set by the sender.
|
||||
return isSupportedHashAlgo(preferredSenderAlgo) ? preferredSenderAlgo : getStrongestSupportedHashAlgo();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -205,7 +259,7 @@ export async function getPreferredCipherSuite(keys = [], date = new Date(), user
|
||||
/**
|
||||
* Create signature packet
|
||||
* @param {Object} dataToSign - Contains packets to be signed
|
||||
* @param {PrivateKey} privateKey - key to get preferences from
|
||||
* @param {Array<Key>} recipientKeys - keys to get preferences from
|
||||
* @param {SecretKeyPacket|
|
||||
* SecretSubkeyPacket} signingKeyPacket secret key packet for signing
|
||||
* @param {Object} [signatureProperties] - Properties to write on the signature packet before signing
|
||||
@ -216,7 +270,7 @@ export async function getPreferredCipherSuite(keys = [], date = new Date(), user
|
||||
* @param {Object} config - full configuration
|
||||
* @returns {Promise<SignaturePacket>} Signature packet.
|
||||
*/
|
||||
export async function createSignaturePacket(dataToSign, privateKey, signingKeyPacket, signatureProperties, date, userID, notations = [], detached = false, config) {
|
||||
export async function createSignaturePacket(dataToSign, recipientKeys, signingKeyPacket, signatureProperties, date, recipientUserIDs, notations = [], detached = false, config) {
|
||||
if (signingKeyPacket.isDummy()) {
|
||||
throw new Error('Cannot sign with a gnu-dummy key.');
|
||||
}
|
||||
@ -226,7 +280,7 @@ export async function createSignaturePacket(dataToSign, privateKey, signingKeyPa
|
||||
const signaturePacket = new SignaturePacket();
|
||||
Object.assign(signaturePacket, signatureProperties);
|
||||
signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
|
||||
signaturePacket.hashAlgorithm = await getPreferredHashAlgo(privateKey, signingKeyPacket, date, userID, config);
|
||||
signaturePacket.hashAlgorithm = await getPreferredHashAlgo(recipientKeys, signingKeyPacket, date, recipientUserIDs, config);
|
||||
signaturePacket.rawNotations = [...notations];
|
||||
await signaturePacket.sign(signingKeyPacket, dataToSign, date, detached, config);
|
||||
return signaturePacket;
|
||||
|
@ -206,7 +206,7 @@ class PrivateKey extends PublicKey {
|
||||
}
|
||||
const dataToSign = { key: this.keyPacket };
|
||||
const key = this.clone();
|
||||
key.revocationSignatures.push(await helper.createSignaturePacket(dataToSign, null, this.keyPacket, {
|
||||
key.revocationSignatures.push(await helper.createSignaturePacket(dataToSign, [], this.keyPacket, {
|
||||
signatureType: enums.signature.keyRevocation,
|
||||
reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag),
|
||||
reasonForRevocationString
|
||||
|
@ -185,7 +185,7 @@ class Subkey {
|
||||
) {
|
||||
const dataToSign = { key: primaryKey, bind: this.keyPacket };
|
||||
const subkey = new Subkey(this.keyPacket, this.mainKey);
|
||||
subkey.revocationSignatures.push(await helper.createSignaturePacket(dataToSign, null, primaryKey, {
|
||||
subkey.revocationSignatures.push(await helper.createSignaturePacket(dataToSign, [], primaryKey, {
|
||||
signatureType: enums.signature.subkeyRevocation,
|
||||
reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag),
|
||||
reasonForRevocationString
|
||||
|
@ -72,7 +72,7 @@ class User {
|
||||
throw new Error("The user's own key can only be used for self-certifications");
|
||||
}
|
||||
const signingKey = await privateKey.getSigningKey(undefined, date, undefined, config);
|
||||
return createSignaturePacket(dataToSign, privateKey, signingKey.keyPacket, {
|
||||
return createSignaturePacket(dataToSign, [privateKey], signingKey.keyPacket, {
|
||||
// Most OpenPGP implementations use generic certification (0x10)
|
||||
signatureType: enums.signature.certGeneric,
|
||||
keyFlags: [enums.keyFlags.certifyKeys | enums.keyFlags.signData]
|
||||
@ -260,7 +260,7 @@ class User {
|
||||
key: primaryKey
|
||||
};
|
||||
const user = new User(dataToSign.userID || dataToSign.userAttribute, this.mainKey);
|
||||
user.revocationSignatures.push(await createSignaturePacket(dataToSign, null, primaryKey, {
|
||||
user.revocationSignatures.push(await createSignaturePacket(dataToSign, [], primaryKey, {
|
||||
signatureType: enums.signature.certRevocation,
|
||||
reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag),
|
||||
reasonForRevocationString
|
||||
|
@ -496,16 +496,18 @@ export class Message {
|
||||
/**
|
||||
* Sign the message (the literal data packet of the message)
|
||||
* @param {Array<PrivateKey>} signingKeys - private keys with decrypted secret key data for signing
|
||||
* @param {Array<Key>} recipientKeys - recipient keys to get the signing preferences from
|
||||
* @param {Signature} [signature] - Any existing detached signature to add to the message
|
||||
* @param {Array<module:type/keyid~KeyID>} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to signingKeys[i]
|
||||
* @param {Date} [date] - Override the creation time of the signature
|
||||
* @param {Array} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
|
||||
* @param {Array<UserID>} [signingUserIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
|
||||
* @param {Array<UserID>} [recipientUserIDs] - User IDs associated with `recipientKeys` to get the signing preferences from
|
||||
* @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }]
|
||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||
* @returns {Promise<Message>} New message with signed content.
|
||||
* @async
|
||||
*/
|
||||
async sign(signingKeys = [], signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], config = defaultConfig) {
|
||||
async sign(signingKeys = [], recipientKeys = [], signature = null, signingKeyIDs = [], date = new Date(), signingUserIDs = [], recipientUserIDs = [], notations = [], config = defaultConfig) {
|
||||
const packetlist = new PacketList();
|
||||
|
||||
const literalDataPacket = this.packets.findPacket(enums.packet.literalData);
|
||||
@ -513,7 +515,7 @@ export class Message {
|
||||
throw new Error('No literal data packet to sign.');
|
||||
}
|
||||
|
||||
const signaturePackets = await createSignaturePackets(literalDataPacket, signingKeys, signature, signingKeyIDs, date, userIDs, notations, false, config); // this returns the existing signature packets as well
|
||||
const signaturePackets = await createSignaturePackets(literalDataPacket, signingKeys, recipientKeys, signature, signingKeyIDs, date, signingUserIDs, recipientUserIDs, notations, false, config); // this returns the existing signature packets as well
|
||||
const onePassSignaturePackets = signaturePackets.map(
|
||||
(signaturePacket, i) => OnePassSignaturePacket.fromSignaturePacket(signaturePacket, i === 0))
|
||||
.reverse(); // innermost OPS refers to the first signature packet
|
||||
@ -549,21 +551,23 @@ export class Message {
|
||||
/**
|
||||
* Create a detached signature for the message (the literal data packet of the message)
|
||||
* @param {Array<PrivateKey>} signingKeys - private keys with decrypted secret key data for signing
|
||||
* @param {Array<Key>} recipientKeys - recipient keys to get the signing preferences from
|
||||
* @param {Signature} [signature] - Any existing detached signature
|
||||
* @param {Array<module:type/keyid~KeyID>} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to signingKeys[i]
|
||||
* @param {Date} [date] - Override the creation time of the signature
|
||||
* @param {Array} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
|
||||
* @param {Array<UserID>} [signingUserIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
|
||||
* @param {Array<UserID>} [recipientUserIDs] - User IDs associated with `recipientKeys` to get the signing preferences from
|
||||
* @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }]
|
||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||
* @returns {Promise<Signature>} New detached signature of message content.
|
||||
* @async
|
||||
*/
|
||||
async signDetached(signingKeys = [], signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], config = defaultConfig) {
|
||||
async signDetached(signingKeys = [], recipientKeys = [], signature = null, signingKeyIDs = [], recipientKeyIDs = [], date = new Date(), userIDs = [], notations = [], config = defaultConfig) {
|
||||
const literalDataPacket = this.packets.findPacket(enums.packet.literalData);
|
||||
if (!literalDataPacket) {
|
||||
throw new Error('No literal data packet to sign.');
|
||||
}
|
||||
return new Signature(await createSignaturePackets(literalDataPacket, signingKeys, signature, signingKeyIDs, date, userIDs, notations, true, config));
|
||||
return new Signature(await createSignaturePackets(literalDataPacket, signingKeys, recipientKeys, signature, signingKeyIDs, recipientKeyIDs, date, userIDs, notations, true, config));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -698,10 +702,12 @@ export class Message {
|
||||
* Create signature packets for the message
|
||||
* @param {LiteralDataPacket} literalDataPacket - the literal data packet to sign
|
||||
* @param {Array<PrivateKey>} [signingKeys] - private keys with decrypted secret key data for signing
|
||||
* @param {Array<Key>} [recipientKeys] - recipient keys to get the signing preferences from
|
||||
* @param {Signature} [signature] - Any existing detached signature to append
|
||||
* @param {Array<module:type/keyid~KeyID>} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to signingKeys[i]
|
||||
* @param {Date} [date] - Override the creationtime of the signature
|
||||
* @param {Array} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
|
||||
* @param {Array<UserID>} [signingUserIDs] - User IDs to sign to, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
|
||||
* @param {Array<UserID>} [recipientUserIDs] - User IDs associated with `recipientKeys` to get the signing preferences from
|
||||
* @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }]
|
||||
* @param {Array} [signatureSalts] - A list of signature salts matching the number of signingKeys that should be used for v6 signatures
|
||||
* @param {Boolean} [detached] - Whether to create detached signature packets
|
||||
@ -710,7 +716,7 @@ export class Message {
|
||||
* @async
|
||||
* @private
|
||||
*/
|
||||
export async function createSignaturePackets(literalDataPacket, signingKeys, signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], detached = false, config = defaultConfig) {
|
||||
export async function createSignaturePackets(literalDataPacket, signingKeys, recipientKeys = [], signature = null, signingKeyIDs = [], date = new Date(), signingUserIDs = [], recipientUserIDs = [], notations = [], detached = false, config = defaultConfig) {
|
||||
const packetlist = new PacketList();
|
||||
|
||||
// If data packet was created from Uint8Array, use binary, otherwise use text
|
||||
@ -718,12 +724,12 @@ export async function createSignaturePackets(literalDataPacket, signingKeys, sig
|
||||
enums.signature.binary : enums.signature.text;
|
||||
|
||||
await Promise.all(signingKeys.map(async (primaryKey, i) => {
|
||||
const userID = userIDs[i];
|
||||
const signingUserID = signingUserIDs[i];
|
||||
if (!primaryKey.isPrivate()) {
|
||||
throw new Error('Need private key for signing');
|
||||
}
|
||||
const signingKey = await primaryKey.getSigningKey(signingKeyIDs[i], date, userID, config);
|
||||
return createSignaturePacket(literalDataPacket, primaryKey, signingKey.keyPacket, { signatureType }, date, userID, notations, detached, config);
|
||||
const signingKey = await primaryKey.getSigningKey(signingKeyIDs[i], date, signingUserID, config);
|
||||
return createSignaturePacket(literalDataPacket, recipientKeys.length ? recipientKeys : [primaryKey], signingKey.keyPacket, { signatureType }, date, recipientUserIDs, notations, detached, config);
|
||||
})).then(signatureList => {
|
||||
packetlist.push(...signatureList);
|
||||
});
|
||||
|
@ -290,7 +290,7 @@ export async function encrypt({ message, encryptionKeys, signingKeys, passwords,
|
||||
|
||||
try {
|
||||
if (signingKeys.length || signature) { // sign the message only if signing keys or signature is specified
|
||||
message = await message.sign(signingKeys, signature, signingKeyIDs, date, signingUserIDs, signatureNotations, config);
|
||||
message = await message.sign(signingKeys, encryptionKeys, signature, signingKeyIDs, date, signingUserIDs, encryptionKeyIDs, signatureNotations, config);
|
||||
}
|
||||
message = message.compress(
|
||||
await getPreferredCompressionAlgo(encryptionKeys, date, encryptionUserIDs, config),
|
||||
@ -392,21 +392,23 @@ 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 {Key|Key[]} options.recipientKeys - Array of keys or single to get the signing preferences from
|
||||
* @param {'armored'|'binary'|'object'} [options.format='armored'] - 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|Object[]} [options.recipientUserIDs=primary user IDs] - Array of user IDs to get the signing preferences from, one per key in `recipientKeys`
|
||||
* @param {Object|Object[]} [options.signatureNotations=[]] - Array of notations to add to the signatures, e.g. `[{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }]`
|
||||
* @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
|
||||
* @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, format = 'armored', detached = false, signingKeyIDs = [], date = new Date(), signingUserIDs = [], signatureNotations = [], config, ...rest }) {
|
||||
export async function sign({ message, signingKeys, recipientKeys = [], format = 'armored', detached = false, signingKeyIDs = [], date = new Date(), signingUserIDs = [], recipientUserIDs = [], signatureNotations = [], config, ...rest }) {
|
||||
config = { ...defaultConfig, ...config }; checkConfig(config);
|
||||
checkCleartextOrMessage(message); checkOutputMessageFormat(format);
|
||||
signingKeys = toArray(signingKeys); signingKeyIDs = toArray(signingKeyIDs); signingUserIDs = toArray(signingUserIDs); signatureNotations = toArray(signatureNotations);
|
||||
signingKeys = toArray(signingKeys); signingKeyIDs = toArray(signingKeyIDs); signingUserIDs = toArray(signingUserIDs); recipientKeys = toArray(recipientKeys); recipientUserIDs = toArray(recipientUserIDs); signatureNotations = toArray(signatureNotations);
|
||||
|
||||
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.');
|
||||
@ -422,9 +424,9 @@ export async function sign({ message, signingKeys, format = 'armored', detached
|
||||
try {
|
||||
let signature;
|
||||
if (detached) {
|
||||
signature = await message.signDetached(signingKeys, undefined, signingKeyIDs, date, signingUserIDs, signatureNotations, config);
|
||||
signature = await message.signDetached(signingKeys, recipientKeys, undefined, signingKeyIDs, date, signingUserIDs, recipientUserIDs, signatureNotations, config);
|
||||
} else {
|
||||
signature = await message.sign(signingKeys, undefined, signingKeyIDs, date, signingUserIDs, signatureNotations, config);
|
||||
signature = await message.sign(signingKeys, recipientKeys, undefined, signingKeyIDs, date, signingUserIDs, recipientUserIDs, signatureNotations, config);
|
||||
}
|
||||
if (format === 'object') return signature;
|
||||
|
||||
|
@ -7,7 +7,7 @@ chaiUse(chaiAsPromised);
|
||||
import sinon from 'sinon';
|
||||
import openpgp from '../initOpenpgp.js';
|
||||
import util from '../../src/util.js';
|
||||
import { getPreferredCipherSuite } from '../../src/key';
|
||||
import { getPreferredCipherSuite, getPreferredHashAlgo } from '../../src/key';
|
||||
import KeyID from '../../src/type/keyid.js';
|
||||
|
||||
const priv_key_arm2 =
|
||||
@ -4136,113 +4136,152 @@ CNa5yq6lyexhsn2Vs8DsX+SOSUyNJiy5FyIJ
|
||||
expect(revKey.armor()).not.to.match(/Comment: This is a revocation certificate/);
|
||||
});
|
||||
|
||||
it('getPreferredCipherSuite - one key', async function() {
|
||||
const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1], undefined, undefined, {
|
||||
...openpgp.config, preferredSymmetricAlgorithm: openpgp.enums.symmetric.aes256
|
||||
describe('getPreferredCipherSuite()', () => {
|
||||
it('getPreferredCipherSuite - one key', async function() {
|
||||
const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1], undefined, undefined, {
|
||||
...openpgp.config, preferredSymmetricAlgorithm: openpgp.enums.symmetric.aes256
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
||||
expect(aeadAlgo).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('getPreferredCipherSuite - two keys', async function() {
|
||||
const { aes128, aes192, cast5 } = openpgp.enums.symmetric;
|
||||
const [key1, key2] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const primaryUser = await key2.getPrimaryUser();
|
||||
primaryUser.selfCertification.preferredSymmetricAlgorithms = [6, aes192, cast5];
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2], undefined, undefined, {
|
||||
...openpgp.config, preferredSymmetricAlgorithm: openpgp.enums.symmetric.aes192
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(aes192);
|
||||
expect(aeadAlgo).to.equal(undefined);
|
||||
const { symmetricAlgo: symmetricAlgo2, aeadAlgo: aeadAlgo2 } = await getPreferredCipherSuite([key1, key2], undefined, undefined, {
|
||||
...openpgp.config, preferredSymmetricAlgorithm: openpgp.enums.symmetric.aes256
|
||||
});
|
||||
expect(symmetricAlgo2).to.equal(aes128);
|
||||
expect(aeadAlgo2).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('getPreferredCipherSuite - two keys - one without pref', async function() {
|
||||
const [key1, key2] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const primaryUser = await key2.getPrimaryUser();
|
||||
primaryUser.selfCertification.preferredSymmetricAlgorithms = null;
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2]);
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes128);
|
||||
expect(aeadAlgo).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('getPreferredCipherSuite with AEAD - one key - GCM', async function() {
|
||||
const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
primaryUser.selfCertification.preferredCipherSuites = [[9, 3], [9, 2]];
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1], undefined, undefined, {
|
||||
...openpgp.config,
|
||||
aeadProtect: true,
|
||||
preferredAEADAlgorithm: openpgp.enums.aead.gcm
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
||||
expect(aeadAlgo).to.equal(openpgp.enums.aead.gcm);
|
||||
});
|
||||
|
||||
it('getPreferredCipherSuite with AEAD - one key - AES256-OCB', async function() {
|
||||
const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
primaryUser.selfCertification.preferredCipherSuites = [[openpgp.enums.symmetric.aes256, openpgp.enums.aead.ocb]];
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1], undefined, undefined, {
|
||||
...openpgp.config,
|
||||
aeadProtect: true,
|
||||
preferredAEADAlgorithm: openpgp.enums.aead.gcm
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
||||
expect(aeadAlgo).to.equal(openpgp.enums.aead.ocb);
|
||||
});
|
||||
|
||||
it('getPreferredCipherSuite with AEAD - one key - AES128-GCM', async function() {
|
||||
const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
primaryUser.selfCertification.preferredCipherSuites = [[openpgp.enums.symmetric.aes128, openpgp.enums.aead.gcm]];
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1], undefined, undefined, {
|
||||
...openpgp.config,
|
||||
aeadProtect: true,
|
||||
preferredAEADAlgorithm: openpgp.enums.aead.gcm
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes128);
|
||||
expect(aeadAlgo).to.equal(openpgp.enums.aead.gcm);
|
||||
});
|
||||
|
||||
it('getPreferredCipherSuite with AEAD - two keys - one without pref', async function() {
|
||||
const keys = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const key1 = keys[0];
|
||||
const key2 = keys[1];
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
primaryUser.selfCertification.preferredCipherSuites = [[9, 3], [9, 2]];
|
||||
const primaryUser2 = await key2.getPrimaryUser();
|
||||
primaryUser2.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2], undefined, undefined, {
|
||||
...openpgp.config,
|
||||
aeadProtect: true
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes128);
|
||||
expect(aeadAlgo).to.equal(openpgp.enums.aead.ocb);
|
||||
});
|
||||
|
||||
it('getPreferredCipherSuite with AEAD - two keys - one with no support', async function() {
|
||||
const keys = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const key1 = keys[0];
|
||||
const key2 = keys[1];
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
primaryUser.selfCertification.preferredCipherSuites = [[9, 3], [9, 2]];
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2], undefined, undefined, {
|
||||
...openpgp.config,
|
||||
aeadProtect: true
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
||||
expect(aeadAlgo).to.equal(undefined);
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
||||
expect(aeadAlgo).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('getPreferredCipherSuite - two keys', async function() {
|
||||
const { aes128, aes192, cast5 } = openpgp.enums.symmetric;
|
||||
const [key1, key2] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const primaryUser = await key2.getPrimaryUser();
|
||||
primaryUser.selfCertification.preferredSymmetricAlgorithms = [6, aes192, cast5];
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2], undefined, undefined, {
|
||||
...openpgp.config, preferredSymmetricAlgorithm: openpgp.enums.symmetric.aes192
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(aes192);
|
||||
expect(aeadAlgo).to.equal(undefined);
|
||||
const { symmetricAlgo: symmetricAlgo2, aeadAlgo: aeadAlgo2 } = await getPreferredCipherSuite([key1, key2], undefined, undefined, {
|
||||
...openpgp.config, preferredSymmetricAlgorithm: openpgp.enums.symmetric.aes256
|
||||
});
|
||||
expect(symmetricAlgo2).to.equal(aes128);
|
||||
expect(aeadAlgo2).to.equal(undefined);
|
||||
});
|
||||
describe('getPreferredHashAlgo()', () => {
|
||||
it('getPreferredHashAlgo - it can handle unknown hash algorithms', async function() {
|
||||
// Preferred hash algo: SHA256 and unknown algo with ID '99'
|
||||
const signingKeyWithUnknownAlgoPref = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
it('getPreferredCipherSuite - two keys - one without pref', async function() {
|
||||
const [key1, key2] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const primaryUser = await key2.getPrimaryUser();
|
||||
primaryUser.selfCertification.preferredSymmetricAlgorithms = null;
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2]);
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes128);
|
||||
expect(aeadAlgo).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('getPreferredCipherSuite with AEAD - one key - GCM', async function() {
|
||||
const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
primaryUser.selfCertification.preferredCipherSuites = [[9, 3], [9, 2]];
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1], undefined, undefined, {
|
||||
...openpgp.config,
|
||||
aeadProtect: true,
|
||||
preferredAEADAlgorithm: openpgp.enums.aead.gcm
|
||||
xVgEZyJrexYJKwYBBAHaRw8BAQdAJwddYhjAmI6OzqxkW9cAXVBfZdSFxsaZ
|
||||
0v9YAJA50fQAAQCK5y2PWn5MEoWnMre7WDMCv3HPs92No9r7ZrmXED3ZohDT
|
||||
zQ48dGVzdEB0ZXN0Lml0PsLAEQQTFgoAgwWCZyJrewMLCQcJkPu0BwaBSfbo
|
||||
RRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5vcmdJHbHl9Kh1
|
||||
AmD2A1I0IgJEsWl12eWrRzU2C5MilKZDXQMVCGMEFgACAQIZAQKbAwIeARYh
|
||||
BDc1TCI+j6hTVaLvtfu0BwaBSfboAAA3cwEAwA/JVtszZ1PgowLYG2/ok+WL
|
||||
+AcEbvhPBBoJV6B2gLsA/2S/WIFiNLJd9xVPCsnlsh6GSqjNjEYXZIag0u14
|
||||
WoEKx10EZyJrexIKKwYBBAGXVQEFAQEHQEnAXen/dnz9PZ+oJ9BYrDV+N/6y
|
||||
c5nTJbTmMj01obBBAwEIBwAA/0izDCturSN2513OhRlrHc55biP/GL2CR6LK
|
||||
e3Zo4XCoEFDCvgQYFgoAcAWCZyJrewmQ+7QHBoFJ9uhFFAAAAAAAHAAgc2Fs
|
||||
dEBub3RhdGlvbnMub3BlbnBncGpzLm9yZ6bxx8jT55ZC4ZuKBMyd1j0ULyQ4
|
||||
PPAbypPTzwI7bN7zApsMFiEENzVMIj6PqFNVou+1+7QHBoFJ9ugAAPRMAP9k
|
||||
45AQSzIKF8JmS28I8hSUDrPCjSVh1A3Aw01F6sRYLgEA1wq81Sxnmvo6ztxK
|
||||
EVdFOaJsHYaJ0A23hIaCWML5nAs=
|
||||
=jJaL
|
||||
-----END PGP PRIVATE KEY BLOCK-----
|
||||
` });
|
||||
const config = {
|
||||
...openpgp.config,
|
||||
preferredHashAlgorithm: openpgp.enums.hash.sha512 // SHA512 is not in the key prefs
|
||||
};
|
||||
const hashAlgo = await getPreferredHashAlgo(
|
||||
[signingKeyWithUnknownAlgoPref],
|
||||
signingKeyWithUnknownAlgoPref,
|
||||
undefined,
|
||||
undefined,
|
||||
config
|
||||
);
|
||||
expect(hashAlgo).to.equal(openpgp.enums.hash.sha256);
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
||||
expect(aeadAlgo).to.equal(openpgp.enums.aead.gcm);
|
||||
});
|
||||
|
||||
it('getPreferredCipherSuite with AEAD - one key - AES256-OCB', async function() {
|
||||
const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
primaryUser.selfCertification.preferredCipherSuites = [[openpgp.enums.symmetric.aes256, openpgp.enums.aead.ocb]];
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1], undefined, undefined, {
|
||||
...openpgp.config,
|
||||
aeadProtect: true,
|
||||
preferredAEADAlgorithm: openpgp.enums.aead.gcm
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
||||
expect(aeadAlgo).to.equal(openpgp.enums.aead.ocb);
|
||||
});
|
||||
|
||||
it('getPreferredCipherSuite with AEAD - one key - AES128-GCM', async function() {
|
||||
const [key1] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
primaryUser.selfCertification.preferredCipherSuites = [[openpgp.enums.symmetric.aes128, openpgp.enums.aead.gcm]];
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1], undefined, undefined, {
|
||||
...openpgp.config,
|
||||
aeadProtect: true,
|
||||
preferredAEADAlgorithm: openpgp.enums.aead.gcm
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes128);
|
||||
expect(aeadAlgo).to.equal(openpgp.enums.aead.gcm);
|
||||
});
|
||||
|
||||
it('getPreferredCipherSuite with AEAD - two keys - one without pref', async function() {
|
||||
const keys = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const key1 = keys[0];
|
||||
const key2 = keys[1];
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
primaryUser.selfCertification.preferredCipherSuites = [[9, 3], [9, 2]];
|
||||
const primaryUser2 = await key2.getPrimaryUser();
|
||||
primaryUser2.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2], undefined, undefined, {
|
||||
...openpgp.config,
|
||||
aeadProtect: true
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes128);
|
||||
expect(aeadAlgo).to.equal(openpgp.enums.aead.ocb);
|
||||
});
|
||||
|
||||
it('getPreferredCipherSuite with AEAD - two keys - one with no support', async function() {
|
||||
const keys = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||
const key1 = keys[0];
|
||||
const key2 = keys[1];
|
||||
const primaryUser = await key1.getPrimaryUser();
|
||||
primaryUser.selfCertification.features = [9]; // Monkey-patch SEIPDv2 feature flag
|
||||
primaryUser.selfCertification.preferredCipherSuites = [[9, 3], [9, 2]];
|
||||
const { symmetricAlgo, aeadAlgo } = await getPreferredCipherSuite([key1, key2], undefined, undefined, {
|
||||
...openpgp.config,
|
||||
aeadProtect: true
|
||||
});
|
||||
expect(symmetricAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
||||
expect(aeadAlgo).to.equal(undefined);
|
||||
});
|
||||
|
||||
it('User attribute packet read & write', async function() {
|
||||
@ -4343,7 +4382,7 @@ VYGdb3eNlV8CfoEC
|
||||
privateKey.users[0].userID = openpgp.UserIDPacket.fromObject({ name: 'Test User', email: 'b@c.com' });
|
||||
// Set second user to prefer aes128. We will select this user.
|
||||
privateKey.users[1].selfCertifications[0].preferredHashAlgorithms = [openpgp.enums.hash.sha512];
|
||||
const config = { minRSABits: 1024 };
|
||||
const config = { minRSABits: 1024, preferredHashAlgorithm: openpgp.enums.hash.sha512 };
|
||||
const signed = await openpgp.sign({
|
||||
message: await openpgp.createMessage({ text: 'hello' }), signingKeys: privateKey, signingUserIDs: { name: 'Test McTestington', email: 'test@example.com' }, format: 'binary', config
|
||||
});
|
||||
|
@ -297,7 +297,7 @@ DECl1Qu4QyeXin29uEXWiekMpNlZVsEuc8icCw6ABhIZ
|
||||
=/7PI
|
||||
-----END PGP PRIVATE KEY BLOCK-----`;
|
||||
|
||||
const priv_key_sha3 = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
const priv_key_sha3_512 = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
xUsGZN8edBsAAAAgdUMlFMFCVKNo7sdUd6FVBos6NNjpUpSdrodk6BfPb/kA+3bu
|
||||
A2+WY2LwyxlX5o07WR2VSn+wuegC3v28yO0tClHCtwYfGw4AAABIBYJk3x50BAsJ
|
||||
@ -1957,8 +1957,8 @@ aOU=
|
||||
})).to.be.rejectedWith(/No signing keys provided/);
|
||||
});
|
||||
|
||||
it('Signing with key which uses sha3 should generate a valid sha3 signature', async function() {
|
||||
const privKey = await openpgp.readKey({ armoredKey: priv_key_sha3 });
|
||||
it('Signing with key which uses sha3 should generate a valid sha3 signature if `config.preferredHashAlgorithm` has been set accordingly', async function() {
|
||||
const privKey = await openpgp.readKey({ armoredKey: priv_key_sha3_512 });
|
||||
const pubKey = privKey.toPublic();
|
||||
const text = 'Hello, world.';
|
||||
const message = await openpgp.createCleartextMessage({ text });
|
||||
@ -1968,10 +1968,18 @@ aOU=
|
||||
expect(parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
|
||||
expect(
|
||||
parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)[0].hashAlgorithm
|
||||
).to.equal(openpgp.config.preferredHashAlgorithm);
|
||||
const cleartextMessageWithSHA3 = await openpgp.sign({ message, signingKeys: privKey, format: 'armored', config: { preferredHashAlgorithm: openpgp.enums.hash.sha3_512 } });
|
||||
const parsedArmoredSHA3 = await openpgp.readCleartextMessage({ cleartextMessage: cleartextMessageWithSHA3 });
|
||||
expect(parsedArmoredSHA3.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
|
||||
expect(
|
||||
parsedArmoredSHA3.signature.packets.filterByTag(openpgp.enums.packet.signature)[0].hashAlgorithm
|
||||
).to.equal(openpgp.enums.hash.sha3_512);
|
||||
|
||||
const verified = await openpgp.verify({ message: parsedArmored, verificationKeys: pubKey, expectSigned: true });
|
||||
const verifiedSHA3 = await openpgp.verify({ message: parsedArmoredSHA3, verificationKeys: pubKey, expectSigned: true });
|
||||
expect(verified.data).to.equal(text);
|
||||
expect(verifiedSHA3.data).to.equal(text);
|
||||
});
|
||||
|
||||
it('should output cleartext message of expected format', async function() {
|
||||
@ -2154,6 +2162,47 @@ aOU=
|
||||
});
|
||||
expect(await stream.readToEnd(streamedData)).to.equal(text);
|
||||
});
|
||||
|
||||
it('should sign using hash algorithm preferred by `recipientKeys` if given', async function() {
|
||||
const signingKeyWithoutSHA3Pref = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
xVgEZyID/RYJKwYBBAHaRw8BAQdAcdaUl/UXEQaT6rKNSEPmyKypikz9rIsf
|
||||
BlFAQYjtsF8AAQDiW9ls2uBBRa3vA1Odl0NNNguRBolWhR9XGpdXnVBF3w5E
|
||||
zQ48dGVzdEB0ZXN0Lml0PsLAEQQTFgoAgwWCZyID/QMLCQcJkJuH6wXn78D5
|
||||
RRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5vcmcNolfauRaj
|
||||
NnItFJ0TOsiyZZhd6bMWVR4032v64tYRywMVCAoEFgACAQIZAQKbAwIeARYh
|
||||
BGsOUiBRfu57iwuxh5uH6wXn78D5AACEQQEAz4YXoEKgOElvxRrIrkglUlpb
|
||||
ilLZVU6mXqLxRSEtZi0BAK5xooNiLYbjF42eJuCDWUWriXufI9acT/vnruFr
|
||||
p34Px10EZyID/RIKKwYBBAGXVQEFAQEHQOC8KcmOQ9+qEgoWBzc8xNgPUvoe
|
||||
IVNw+mHbljD9eFBfAwEIBwAA/3iHMqnBfuM/c9tOIWKI4advW92aMYnjexrU
|
||||
HdzPS2IoEU3CvgQYFgoAcAWCZyID/QmQm4frBefvwPlFFAAAAAAAHAAgc2Fs
|
||||
dEBub3RhdGlvbnMub3BlbnBncGpzLm9yZ5M4VuJhTqDkHF/14D0i/wL8GTtM
|
||||
fm9AIukMoYWXjGSGApsMFiEEaw5SIFF+7nuLC7GHm4frBefvwPkAAL0QAP9Z
|
||||
oR7Vxyfuje3vAyEbef1gyfMN/RkIVbMKSiwy3A2W9AEA6QcBF5zUvwmHPpA4
|
||||
+SkLLMuq/yUGT6WhAq6kASQ8vgM=
|
||||
=lluz
|
||||
-----END PGP PRIVATE KEY BLOCK-----` });
|
||||
const recipientKeyWithSHA3Pref = await openpgp.readKey({ armoredKey: priv_key_sha3_512 });
|
||||
|
||||
const text = 'Hello, world.';
|
||||
const message = await openpgp.createCleartextMessage({ text });
|
||||
|
||||
// SHA3-512 is first preference of recipient key, and should be picked,
|
||||
// even if not declared in the signing key prefs
|
||||
const cleartextMessage = await openpgp.sign({
|
||||
message,
|
||||
signingKeys: signingKeyWithoutSHA3Pref,
|
||||
recipientKeys: recipientKeyWithSHA3Pref,
|
||||
format: 'armored',
|
||||
// the preferred hash algo is expected to picked when supported by the recipient keys
|
||||
config: { preferredHashAlgorithm: openpgp.enums.hash.sha3_512 }
|
||||
});
|
||||
const parsedArmored = await openpgp.readCleartextMessage({ cleartextMessage });
|
||||
expect(parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
|
||||
expect(
|
||||
parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)[0].hashAlgorithm
|
||||
).to.equal(openpgp.enums.hash.sha3_512);
|
||||
});
|
||||
});
|
||||
|
||||
describe('encrypt - unit tests', function() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user