Merge pull request #1598 from twiss/sig-notation-creation
Add support for creating Notation Data subpackets when signing or encrypting messages
This commit is contained in:
commit
64ca5af879
7
openpgp.d.ts
vendored
7
openpgp.d.ts
vendored
|
@ -166,7 +166,7 @@ export class CleartextMessage {
|
||||||
*
|
*
|
||||||
* @param privateKeys private keys with decrypted secret key data for signing
|
* @param privateKeys private keys with decrypted secret key data for signing
|
||||||
*/
|
*/
|
||||||
sign(privateKeys: PrivateKey[], signature?: Signature, signingKeyIDs?: KeyID[], date?: Date, userIDs?: UserID[], config?: Config): void;
|
sign(privateKeys: PrivateKey[], signature?: Signature, signingKeyIDs?: KeyID[], date?: Date, userIDs?: UserID[], notations?: RawNotation[], config?: Config): void;
|
||||||
|
|
||||||
/** Verify signatures of cleartext signed message
|
/** Verify signatures of cleartext signed message
|
||||||
* @param keys array of keys to verify signatures
|
* @param keys array of keys to verify signatures
|
||||||
|
@ -285,7 +285,7 @@ export class Message<T extends MaybeStream<Data>> {
|
||||||
/** Sign the message (the literal data packet of the message)
|
/** Sign the message (the literal data packet of the message)
|
||||||
@param signingKeys private keys with decrypted secret key data for signing
|
@param signingKeys private keys with decrypted secret key data for signing
|
||||||
*/
|
*/
|
||||||
public sign(signingKeys: PrivateKey[], signature?: Signature, signingKeyIDs?: KeyID[], date?: Date, userIDs?: UserID[], config?: Config): Promise<Message<T>>;
|
public sign(signingKeys: PrivateKey[], signature?: Signature, signingKeyIDs?: KeyID[], date?: Date, userIDs?: UserID[], notations?: RawNotation[], config?: Config): Promise<Message<T>>;
|
||||||
|
|
||||||
/** Unwrap compressed message
|
/** Unwrap compressed message
|
||||||
*/
|
*/
|
||||||
|
@ -604,6 +604,8 @@ interface EncryptOptions {
|
||||||
signingUserIDs?: MaybeArray<UserID>;
|
signingUserIDs?: MaybeArray<UserID>;
|
||||||
/** (optional) array of user IDs to encrypt for, e.g. { name:'Robert Receiver', email:'robert@openpgp.org' } */
|
/** (optional) array of user IDs to encrypt for, e.g. { name:'Robert Receiver', email:'robert@openpgp.org' } */
|
||||||
encryptionUserIDs?: MaybeArray<UserID>;
|
encryptionUserIDs?: MaybeArray<UserID>;
|
||||||
|
/** (optional) array of notations to add to the signatures, e.g. { name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true } */
|
||||||
|
signatureNotations?: MaybeArray<RawNotation>;
|
||||||
config?: PartialConfig;
|
config?: PartialConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -637,6 +639,7 @@ interface SignOptions {
|
||||||
signingKeyIDs?: MaybeArray<KeyID>;
|
signingKeyIDs?: MaybeArray<KeyID>;
|
||||||
date?: Date;
|
date?: Date;
|
||||||
signingUserIDs?: MaybeArray<UserID>;
|
signingUserIDs?: MaybeArray<UserID>;
|
||||||
|
signatureNotations?: MaybeArray<RawNotation>;
|
||||||
config?: PartialConfig;
|
config?: PartialConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,14 +64,15 @@ export class CleartextMessage {
|
||||||
* @param {Array<module:type/keyid~KeyID>} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to privateKeys[i]
|
* @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 {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} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
|
||||||
|
* @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true }]
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<CleartextMessage>} New cleartext message with signed content.
|
* @returns {Promise<CleartextMessage>} New cleartext message with signed content.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async sign(privateKeys, signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], config = defaultConfig) {
|
async sign(privateKeys, signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], config = defaultConfig) {
|
||||||
const literalDataPacket = new LiteralDataPacket();
|
const literalDataPacket = new LiteralDataPacket();
|
||||||
literalDataPacket.setText(this.text);
|
literalDataPacket.setText(this.text);
|
||||||
const newSignature = new Signature(await createSignaturePackets(literalDataPacket, privateKeys, signature, signingKeyIDs, date, userIDs, true, config));
|
const newSignature = new Signature(await createSignaturePackets(literalDataPacket, privateKeys, signature, signingKeyIDs, date, userIDs, notations, true, config));
|
||||||
return new CleartextMessage(this.text, newSignature);
|
return new CleartextMessage(this.text, newSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -249,7 +249,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options, conf
|
||||||
signatureType: enums.signature.keyRevocation,
|
signatureType: enums.signature.keyRevocation,
|
||||||
reasonForRevocationFlag: enums.reasonForRevocation.noReason,
|
reasonForRevocationFlag: enums.reasonForRevocation.noReason,
|
||||||
reasonForRevocationString: ''
|
reasonForRevocationString: ''
|
||||||
}, options.date, undefined, undefined, config));
|
}, options.date, undefined, undefined, undefined, config));
|
||||||
|
|
||||||
if (options.passphrase) {
|
if (options.passphrase) {
|
||||||
secretKeyPacket.clearPrivateParams();
|
secretKeyPacket.clearPrivateParams();
|
||||||
|
|
|
@ -96,7 +96,7 @@ export async function createBindingSignature(subkey, primaryKey, options, config
|
||||||
subkeySignaturePacket.keyFlags = [enums.keyFlags.signData];
|
subkeySignaturePacket.keyFlags = [enums.keyFlags.signData];
|
||||||
subkeySignaturePacket.embeddedSignature = await createSignaturePacket(dataToSign, null, subkey, {
|
subkeySignaturePacket.embeddedSignature = await createSignaturePacket(dataToSign, null, subkey, {
|
||||||
signatureType: enums.signature.keyBinding
|
signatureType: enums.signature.keyBinding
|
||||||
}, options.date, undefined, undefined, config);
|
}, options.date, undefined, undefined, undefined, config);
|
||||||
} else {
|
} else {
|
||||||
subkeySignaturePacket.keyFlags = [enums.keyFlags.encryptCommunication | enums.keyFlags.encryptStorage];
|
subkeySignaturePacket.keyFlags = [enums.keyFlags.encryptCommunication | enums.keyFlags.encryptStorage];
|
||||||
}
|
}
|
||||||
|
@ -192,11 +192,12 @@ export async function getPreferredAlgo(type, keys = [], date = new Date(), userI
|
||||||
* @param {Object} [signatureProperties] - Properties to write on the signature packet before signing
|
* @param {Object} [signatureProperties] - Properties to write on the signature packet before signing
|
||||||
* @param {Date} [date] - Override the creationtime of the signature
|
* @param {Date} [date] - Override the creationtime of the signature
|
||||||
* @param {Object} [userID] - User ID
|
* @param {Object} [userID] - User ID
|
||||||
|
* @param {Array} [notations] - Notation Data to add to the signature, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true }]
|
||||||
* @param {Object} [detached] - Whether to create a detached signature packet
|
* @param {Object} [detached] - Whether to create a detached signature packet
|
||||||
* @param {Object} config - full configuration
|
* @param {Object} config - full configuration
|
||||||
* @returns {Promise<SignaturePacket>} Signature packet.
|
* @returns {Promise<SignaturePacket>} Signature packet.
|
||||||
*/
|
*/
|
||||||
export async function createSignaturePacket(dataToSign, privateKey, signingKeyPacket, signatureProperties, date, userID, detached = false, config) {
|
export async function createSignaturePacket(dataToSign, privateKey, signingKeyPacket, signatureProperties, date, userID, notations = [], detached = false, config) {
|
||||||
if (signingKeyPacket.isDummy()) {
|
if (signingKeyPacket.isDummy()) {
|
||||||
throw new Error('Cannot sign with a gnu-dummy key.');
|
throw new Error('Cannot sign with a gnu-dummy key.');
|
||||||
}
|
}
|
||||||
|
@ -207,6 +208,7 @@ export async function createSignaturePacket(dataToSign, privateKey, signingKeyPa
|
||||||
Object.assign(signaturePacket, signatureProperties);
|
Object.assign(signaturePacket, signatureProperties);
|
||||||
signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
|
signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
|
||||||
signaturePacket.hashAlgorithm = await getPreferredHashAlgo(privateKey, signingKeyPacket, date, userID, config);
|
signaturePacket.hashAlgorithm = await getPreferredHashAlgo(privateKey, signingKeyPacket, date, userID, config);
|
||||||
|
signaturePacket.rawNotations = notations;
|
||||||
await signaturePacket.sign(signingKeyPacket, dataToSign, date, detached);
|
await signaturePacket.sign(signingKeyPacket, dataToSign, date, detached);
|
||||||
return signaturePacket;
|
return signaturePacket;
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,7 +191,7 @@ class PrivateKey extends PublicKey {
|
||||||
signatureType: enums.signature.keyRevocation,
|
signatureType: enums.signature.keyRevocation,
|
||||||
reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag),
|
reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag),
|
||||||
reasonForRevocationString
|
reasonForRevocationString
|
||||||
}, date, undefined, undefined, config));
|
}, date, undefined, undefined, undefined, config));
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,7 @@ class Subkey {
|
||||||
signatureType: enums.signature.subkeyRevocation,
|
signatureType: enums.signature.subkeyRevocation,
|
||||||
reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag),
|
reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag),
|
||||||
reasonForRevocationString
|
reasonForRevocationString
|
||||||
}, date, undefined, false, config));
|
}, date, undefined, undefined, false, config));
|
||||||
await subkey.update(this);
|
await subkey.update(this);
|
||||||
return subkey;
|
return subkey;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ class User {
|
||||||
// Most OpenPGP implementations use generic certification (0x10)
|
// Most OpenPGP implementations use generic certification (0x10)
|
||||||
signatureType: enums.signature.certGeneric,
|
signatureType: enums.signature.certGeneric,
|
||||||
keyFlags: [enums.keyFlags.certifyKeys | enums.keyFlags.signData]
|
keyFlags: [enums.keyFlags.certifyKeys | enums.keyFlags.signData]
|
||||||
}, date, undefined, undefined, config);
|
}, date, undefined, undefined, undefined, config);
|
||||||
}));
|
}));
|
||||||
await user.update(this, date, config);
|
await user.update(this, date, config);
|
||||||
return user;
|
return user;
|
||||||
|
@ -265,7 +265,7 @@ class User {
|
||||||
signatureType: enums.signature.certRevocation,
|
signatureType: enums.signature.certRevocation,
|
||||||
reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag),
|
reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag),
|
||||||
reasonForRevocationString
|
reasonForRevocationString
|
||||||
}, date, undefined, false, config));
|
}, date, undefined, undefined, false, config));
|
||||||
await user.update(this);
|
await user.update(this);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
|
@ -476,11 +476,12 @@ export class Message {
|
||||||
* @param {Array<module:type/keyid~KeyID>} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to signingKeys[i]
|
* @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 {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} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
|
||||||
|
* @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true }]
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Message>} New message with signed content.
|
* @returns {Promise<Message>} New message with signed content.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async sign(signingKeys = [], signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], config = defaultConfig) {
|
async sign(signingKeys = [], signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], config = defaultConfig) {
|
||||||
const packetlist = new PacketList();
|
const packetlist = new PacketList();
|
||||||
|
|
||||||
const literalDataPacket = this.packets.findPacket(enums.packet.literalData);
|
const literalDataPacket = this.packets.findPacket(enums.packet.literalData);
|
||||||
|
@ -530,7 +531,7 @@ export class Message {
|
||||||
});
|
});
|
||||||
|
|
||||||
packetlist.push(literalDataPacket);
|
packetlist.push(literalDataPacket);
|
||||||
packetlist.push(...(await createSignaturePackets(literalDataPacket, signingKeys, signature, signingKeyIDs, date, userIDs, false, config)));
|
packetlist.push(...(await createSignaturePackets(literalDataPacket, signingKeys, signature, signingKeyIDs, date, userIDs, notations, false, config)));
|
||||||
|
|
||||||
return new Message(packetlist);
|
return new Message(packetlist);
|
||||||
}
|
}
|
||||||
|
@ -563,16 +564,17 @@ export class Message {
|
||||||
* @param {Array<module:type/keyid~KeyID>} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to signingKeys[i]
|
* @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 {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} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
|
||||||
|
* @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true }]
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Signature>} New detached signature of message content.
|
* @returns {Promise<Signature>} New detached signature of message content.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async signDetached(signingKeys = [], signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], config = defaultConfig) {
|
async signDetached(signingKeys = [], signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], config = defaultConfig) {
|
||||||
const literalDataPacket = this.packets.findPacket(enums.packet.literalData);
|
const literalDataPacket = this.packets.findPacket(enums.packet.literalData);
|
||||||
if (!literalDataPacket) {
|
if (!literalDataPacket) {
|
||||||
throw new Error('No literal data packet to sign.');
|
throw new Error('No literal data packet to sign.');
|
||||||
}
|
}
|
||||||
return new Signature(await createSignaturePackets(literalDataPacket, signingKeys, signature, signingKeyIDs, date, userIDs, true, config));
|
return new Signature(await createSignaturePackets(literalDataPacket, signingKeys, signature, signingKeyIDs, date, userIDs, notations, true, config));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -705,13 +707,14 @@ export class Message {
|
||||||
* @param {Array<module:type/keyid~KeyID>} [signingKeyIDs] - Array of key IDs to use for signing. Each signingKeyIDs[i] corresponds to signingKeys[i]
|
* @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 {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} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
|
||||||
|
* @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true }]
|
||||||
* @param {Boolean} [detached] - Whether to create detached signature packets
|
* @param {Boolean} [detached] - Whether to create detached signature packets
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<PacketList>} List of signature packets.
|
* @returns {Promise<PacketList>} List of signature packets.
|
||||||
* @async
|
* @async
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export async function createSignaturePackets(literalDataPacket, signingKeys, signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], detached = false, config = defaultConfig) {
|
export async function createSignaturePackets(literalDataPacket, signingKeys, signature = null, signingKeyIDs = [], date = new Date(), userIDs = [], notations = [], detached = false, config = defaultConfig) {
|
||||||
const packetlist = new PacketList();
|
const packetlist = new PacketList();
|
||||||
|
|
||||||
// If data packet was created from Uint8Array, use binary, otherwise use text
|
// If data packet was created from Uint8Array, use binary, otherwise use text
|
||||||
|
@ -724,7 +727,7 @@ export async function createSignaturePackets(literalDataPacket, signingKeys, sig
|
||||||
throw new Error('Need private key for signing');
|
throw new Error('Need private key for signing');
|
||||||
}
|
}
|
||||||
const signingKey = await primaryKey.getSigningKey(signingKeyIDs[i], date, userID, config);
|
const signingKey = await primaryKey.getSigningKey(signingKeyIDs[i], date, userID, config);
|
||||||
return createSignaturePacket(literalDataPacket, primaryKey, signingKey.keyPacket, { signatureType }, date, userID, detached, config);
|
return createSignaturePacket(literalDataPacket, primaryKey, signingKey.keyPacket, { signatureType }, date, userID, notations, detached, config);
|
||||||
})).then(signatureList => {
|
})).then(signatureList => {
|
||||||
packetlist.push(...signatureList);
|
packetlist.push(...signatureList);
|
||||||
});
|
});
|
||||||
|
|
|
@ -256,16 +256,17 @@ export async function encryptKey({ privateKey, passphrase, config, ...rest }) {
|
||||||
* @param {Date} [options.date=current date] - Override the creation date of the message signature
|
* @param {Date} [options.date=current date] - Override the creation date of the message 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.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.encryptionUserIDs=primary user IDs] - Array of user IDs to encrypt for, one per key in `encryptionKeys`, e.g. `[{ name: 'Robert Receiver', email: 'robert@openpgp.org' }]`
|
* @param {Object|Object[]} [options.encryptionUserIDs=primary user IDs] - Array of user IDs to encrypt for, one per key in `encryptionKeys`, e.g. `[{ name: 'Robert Receiver', email: 'robert@openpgp.org' }]`
|
||||||
|
* @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 }]`
|
||||||
* @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
|
* @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
|
||||||
* @returns {Promise<MaybeStream<String>|MaybeStream<Uint8Array>>} Encrypted message (string if `armor` was true, the default; Uint8Array if `armor` was false).
|
* @returns {Promise<MaybeStream<String>|MaybeStream<Uint8Array>>} Encrypted message (string if `armor` was true, the default; Uint8Array if `armor` was false).
|
||||||
* @async
|
* @async
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
export async function encrypt({ message, encryptionKeys, signingKeys, passwords, sessionKey, format = 'armored', signature = null, wildcard = false, signingKeyIDs = [], encryptionKeyIDs = [], date = new Date(), signingUserIDs = [], encryptionUserIDs = [], config, ...rest }) {
|
export async function encrypt({ message, encryptionKeys, signingKeys, passwords, sessionKey, format = 'armored', signature = null, wildcard = false, signingKeyIDs = [], encryptionKeyIDs = [], date = new Date(), signingUserIDs = [], encryptionUserIDs = [], signatureNotations = [], config, ...rest }) {
|
||||||
config = { ...defaultConfig, ...config }; checkConfig(config);
|
config = { ...defaultConfig, ...config }; checkConfig(config);
|
||||||
checkMessage(message); checkOutputMessageFormat(format);
|
checkMessage(message); checkOutputMessageFormat(format);
|
||||||
encryptionKeys = toArray(encryptionKeys); signingKeys = toArray(signingKeys); passwords = toArray(passwords);
|
encryptionKeys = toArray(encryptionKeys); signingKeys = toArray(signingKeys); passwords = toArray(passwords);
|
||||||
signingKeyIDs = toArray(signingKeyIDs); encryptionKeyIDs = toArray(encryptionKeyIDs); signingUserIDs = toArray(signingUserIDs); encryptionUserIDs = toArray(encryptionUserIDs);
|
signingKeyIDs = toArray(signingKeyIDs); encryptionKeyIDs = toArray(encryptionKeyIDs); signingUserIDs = toArray(signingUserIDs); encryptionUserIDs = toArray(encryptionUserIDs); signatureNotations = toArray(signatureNotations);
|
||||||
if (rest.detached) {
|
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.");
|
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.");
|
||||||
}
|
}
|
||||||
|
@ -280,7 +281,7 @@ export async function encrypt({ message, encryptionKeys, signingKeys, passwords,
|
||||||
const streaming = message.fromStream;
|
const streaming = message.fromStream;
|
||||||
try {
|
try {
|
||||||
if (signingKeys.length || signature) { // sign the message only if signing keys or signature is specified
|
if (signingKeys.length || signature) { // sign the message only if signing keys or signature is specified
|
||||||
message = await message.sign(signingKeys, signature, signingKeyIDs, date, signingUserIDs, config);
|
message = await message.sign(signingKeys, signature, signingKeyIDs, date, signingUserIDs, signatureNotations, config);
|
||||||
}
|
}
|
||||||
message = message.compress(
|
message = message.compress(
|
||||||
await getPreferredAlgo('compression', encryptionKeys, date, encryptionUserIDs, config),
|
await getPreferredAlgo('compression', encryptionKeys, date, encryptionUserIDs, config),
|
||||||
|
@ -387,15 +388,16 @@ export async function decrypt({ message, decryptionKeys, passwords, sessionKeys,
|
||||||
* @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 {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 {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.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.signatureNotations=[]] - Array of notations to add to the signatures, e.g. `[{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true }]`
|
||||||
* @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
|
* @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).
|
* @returns {Promise<MaybeStream<String|Uint8Array>>} Signed message (string if `armor` was true, the default; Uint8Array if `armor` was false).
|
||||||
* @async
|
* @async
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
export async function sign({ message, signingKeys, format = 'armored', detached = false, signingKeyIDs = [], date = new Date(), signingUserIDs = [], config, ...rest }) {
|
export async function sign({ message, signingKeys, format = 'armored', detached = false, signingKeyIDs = [], date = new Date(), signingUserIDs = [], signatureNotations = [], config, ...rest }) {
|
||||||
config = { ...defaultConfig, ...config }; checkConfig(config);
|
config = { ...defaultConfig, ...config }; checkConfig(config);
|
||||||
checkCleartextOrMessage(message); checkOutputMessageFormat(format);
|
checkCleartextOrMessage(message); checkOutputMessageFormat(format);
|
||||||
signingKeys = toArray(signingKeys); signingKeyIDs = toArray(signingKeyIDs); signingUserIDs = toArray(signingUserIDs);
|
signingKeys = toArray(signingKeys); signingKeyIDs = toArray(signingKeyIDs); signingUserIDs = toArray(signingUserIDs); signatureNotations = toArray(signatureNotations);
|
||||||
|
|
||||||
if (rest.privateKeys) throw new Error('The `privateKeys` option has been removed from openpgp.sign, pass `signingKeys` instead');
|
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.');
|
if (rest.armor !== undefined) throw new Error('The `armor` option has been removed from openpgp.sign, pass `format` instead.');
|
||||||
|
@ -411,9 +413,9 @@ export async function sign({ message, signingKeys, format = 'armored', detached
|
||||||
try {
|
try {
|
||||||
let signature;
|
let signature;
|
||||||
if (detached) {
|
if (detached) {
|
||||||
signature = await message.signDetached(signingKeys, undefined, signingKeyIDs, date, signingUserIDs, config);
|
signature = await message.signDetached(signingKeys, undefined, signingKeyIDs, date, signingUserIDs, signatureNotations, config);
|
||||||
} else {
|
} else {
|
||||||
signature = await message.sign(signingKeys, undefined, signingKeyIDs, date, signingUserIDs, config);
|
signature = await message.sign(signingKeys, undefined, signingKeyIDs, date, signingUserIDs, signatureNotations, config);
|
||||||
}
|
}
|
||||||
if (format === 'object') return signature;
|
if (format === 'object') return signature;
|
||||||
|
|
||||||
|
|
|
@ -260,13 +260,14 @@ class SignaturePacket {
|
||||||
// MUST NOT be included in the signature.
|
// MUST NOT be included in the signature.
|
||||||
arr.push(writeSubPacket(sub.issuer, this.issuerKeyID.write()));
|
arr.push(writeSubPacket(sub.issuer, this.issuerKeyID.write()));
|
||||||
}
|
}
|
||||||
this.rawNotations.forEach(([{ name, value, humanReadable }]) => {
|
this.rawNotations.forEach(({ name, value, humanReadable }) => {
|
||||||
bytes = [new Uint8Array([humanReadable ? 0x80 : 0, 0, 0, 0])];
|
bytes = [new Uint8Array([humanReadable ? 0x80 : 0, 0, 0, 0])];
|
||||||
|
const encodedName = util.encodeUTF8(name);
|
||||||
// 2 octets of name length
|
// 2 octets of name length
|
||||||
bytes.push(util.writeNumber(name.length, 2));
|
bytes.push(util.writeNumber(encodedName.length, 2));
|
||||||
// 2 octets of value length
|
// 2 octets of value length
|
||||||
bytes.push(util.writeNumber(value.length, 2));
|
bytes.push(util.writeNumber(value.length, 2));
|
||||||
bytes.push(util.stringToUint8Array(name));
|
bytes.push(encodedName);
|
||||||
bytes.push(value);
|
bytes.push(value);
|
||||||
bytes = util.concat(bytes);
|
bytes = util.concat(bytes);
|
||||||
arr.push(writeSubPacket(sub.notationData, bytes));
|
arr.push(writeSubPacket(sub.notationData, bytes));
|
||||||
|
@ -284,20 +285,20 @@ class SignaturePacket {
|
||||||
arr.push(writeSubPacket(sub.keyServerPreferences, bytes));
|
arr.push(writeSubPacket(sub.keyServerPreferences, bytes));
|
||||||
}
|
}
|
||||||
if (this.preferredKeyServer !== null) {
|
if (this.preferredKeyServer !== null) {
|
||||||
arr.push(writeSubPacket(sub.preferredKeyServer, util.stringToUint8Array(this.preferredKeyServer)));
|
arr.push(writeSubPacket(sub.preferredKeyServer, util.encodeUTF8(this.preferredKeyServer)));
|
||||||
}
|
}
|
||||||
if (this.isPrimaryUserID !== null) {
|
if (this.isPrimaryUserID !== null) {
|
||||||
arr.push(writeSubPacket(sub.primaryUserID, new Uint8Array([this.isPrimaryUserID ? 1 : 0])));
|
arr.push(writeSubPacket(sub.primaryUserID, new Uint8Array([this.isPrimaryUserID ? 1 : 0])));
|
||||||
}
|
}
|
||||||
if (this.policyURI !== null) {
|
if (this.policyURI !== null) {
|
||||||
arr.push(writeSubPacket(sub.policyURI, util.stringToUint8Array(this.policyURI)));
|
arr.push(writeSubPacket(sub.policyURI, util.encodeUTF8(this.policyURI)));
|
||||||
}
|
}
|
||||||
if (this.keyFlags !== null) {
|
if (this.keyFlags !== null) {
|
||||||
bytes = util.stringToUint8Array(util.uint8ArrayToString(this.keyFlags));
|
bytes = util.stringToUint8Array(util.uint8ArrayToString(this.keyFlags));
|
||||||
arr.push(writeSubPacket(sub.keyFlags, bytes));
|
arr.push(writeSubPacket(sub.keyFlags, bytes));
|
||||||
}
|
}
|
||||||
if (this.signersUserID !== null) {
|
if (this.signersUserID !== null) {
|
||||||
arr.push(writeSubPacket(sub.signersUserID, util.stringToUint8Array(this.signersUserID)));
|
arr.push(writeSubPacket(sub.signersUserID, util.encodeUTF8(this.signersUserID)));
|
||||||
}
|
}
|
||||||
if (this.reasonForRevocationFlag !== null) {
|
if (this.reasonForRevocationFlag !== null) {
|
||||||
bytes = util.stringToUint8Array(String.fromCharCode(this.reasonForRevocationFlag) + this.reasonForRevocationString);
|
bytes = util.stringToUint8Array(String.fromCharCode(this.reasonForRevocationFlag) + this.reasonForRevocationString);
|
||||||
|
@ -354,7 +355,7 @@ class SignaturePacket {
|
||||||
let mypos = 0;
|
let mypos = 0;
|
||||||
|
|
||||||
// The leftmost bit denotes a "critical" packet
|
// The leftmost bit denotes a "critical" packet
|
||||||
const critical = bytes[mypos] & 0x80;
|
const critical = !!(bytes[mypos] & 0x80);
|
||||||
const type = bytes[mypos] & 0x7F;
|
const type = bytes[mypos] & 0x7F;
|
||||||
|
|
||||||
if (!hashed) {
|
if (!hashed) {
|
||||||
|
@ -437,13 +438,13 @@ class SignaturePacket {
|
||||||
const n = util.readNumber(bytes.subarray(mypos, mypos + 2));
|
const n = util.readNumber(bytes.subarray(mypos, mypos + 2));
|
||||||
mypos += 2;
|
mypos += 2;
|
||||||
|
|
||||||
const name = util.uint8ArrayToString(bytes.subarray(mypos, mypos + m));
|
const name = util.decodeUTF8(bytes.subarray(mypos, mypos + m));
|
||||||
const value = bytes.subarray(mypos + m, mypos + m + n);
|
const value = bytes.subarray(mypos + m, mypos + m + n);
|
||||||
|
|
||||||
this.rawNotations.push({ name, humanReadable, value, critical });
|
this.rawNotations.push({ name, humanReadable, value, critical });
|
||||||
|
|
||||||
if (humanReadable) {
|
if (humanReadable) {
|
||||||
this.notations[name] = util.uint8ArrayToString(value);
|
this.notations[name] = util.decodeUTF8(value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -461,7 +462,7 @@ class SignaturePacket {
|
||||||
break;
|
break;
|
||||||
case enums.signatureSubpacket.preferredKeyServer:
|
case enums.signatureSubpacket.preferredKeyServer:
|
||||||
// Preferred Key Server
|
// Preferred Key Server
|
||||||
this.preferredKeyServer = util.uint8ArrayToString(bytes.subarray(mypos, bytes.length));
|
this.preferredKeyServer = util.decodeUTF8(bytes.subarray(mypos, bytes.length));
|
||||||
break;
|
break;
|
||||||
case enums.signatureSubpacket.primaryUserID:
|
case enums.signatureSubpacket.primaryUserID:
|
||||||
// Primary User ID
|
// Primary User ID
|
||||||
|
@ -469,7 +470,7 @@ class SignaturePacket {
|
||||||
break;
|
break;
|
||||||
case enums.signatureSubpacket.policyURI:
|
case enums.signatureSubpacket.policyURI:
|
||||||
// Policy URI
|
// Policy URI
|
||||||
this.policyURI = util.uint8ArrayToString(bytes.subarray(mypos, bytes.length));
|
this.policyURI = util.decodeUTF8(bytes.subarray(mypos, bytes.length));
|
||||||
break;
|
break;
|
||||||
case enums.signatureSubpacket.keyFlags:
|
case enums.signatureSubpacket.keyFlags:
|
||||||
// Key Flags
|
// Key Flags
|
||||||
|
@ -477,12 +478,12 @@ class SignaturePacket {
|
||||||
break;
|
break;
|
||||||
case enums.signatureSubpacket.signersUserID:
|
case enums.signatureSubpacket.signersUserID:
|
||||||
// Signer's User ID
|
// Signer's User ID
|
||||||
this.signersUserID = util.uint8ArrayToString(bytes.subarray(mypos, bytes.length));
|
this.signersUserID = util.decodeUTF8(bytes.subarray(mypos, bytes.length));
|
||||||
break;
|
break;
|
||||||
case enums.signatureSubpacket.reasonForRevocation:
|
case enums.signatureSubpacket.reasonForRevocation:
|
||||||
// Reason for Revocation
|
// Reason for Revocation
|
||||||
this.reasonForRevocationFlag = bytes[mypos++];
|
this.reasonForRevocationFlag = bytes[mypos++];
|
||||||
this.reasonForRevocationString = util.uint8ArrayToString(bytes.subarray(mypos, bytes.length));
|
this.reasonForRevocationString = util.decodeUTF8(bytes.subarray(mypos, bytes.length));
|
||||||
break;
|
break;
|
||||||
case enums.signatureSubpacket.features:
|
case enums.signatureSubpacket.features:
|
||||||
// Features
|
// Features
|
||||||
|
|
|
@ -1138,6 +1138,48 @@ eSvSZutLuKKbidSYMLhWROPlwKc2GU2ws6PrLZAyCAel/lU=
|
||||||
expect(await sig.verified).to.be.true;
|
expect(await sig.verified).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Can create notations', async function() {
|
||||||
|
const privKey = await openpgp.decryptKey({
|
||||||
|
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
|
||||||
|
passphrase: 'hello world'
|
||||||
|
});
|
||||||
|
|
||||||
|
const config = { minRSABits: 1024 };
|
||||||
|
const message_with_notation = await openpgp.encrypt({
|
||||||
|
message: await openpgp.createMessage({ text: 'test' }),
|
||||||
|
encryptionKeys: privKey,
|
||||||
|
signingKeys: privKey,
|
||||||
|
signatureNotations: [
|
||||||
|
{
|
||||||
|
name: 'test@example.com',
|
||||||
|
value: new TextEncoder().encode('test'),
|
||||||
|
humanReadable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'séparation-de-domaine@proton.ch',
|
||||||
|
value: new Uint8Array([0, 1, 2, 3]),
|
||||||
|
humanReadable: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
config
|
||||||
|
});
|
||||||
|
const { signatures: [sig] } = await openpgp.decrypt({
|
||||||
|
message: await openpgp.readMessage({ armoredMessage: message_with_notation }),
|
||||||
|
decryptionKeys: privKey,
|
||||||
|
verificationKeys: privKey
|
||||||
|
});
|
||||||
|
const { packets: [{ rawNotations: notations }] } = await sig.signature;
|
||||||
|
expect(notations).to.have.length(2);
|
||||||
|
expect(notations[0].name).to.equal('test@example.com');
|
||||||
|
expect(notations[0].value).to.deep.equal(new Uint8Array([116, 101, 115, 116]));
|
||||||
|
expect(notations[0].humanReadable).to.be.true;
|
||||||
|
expect(notations[0].critical).to.be.false;
|
||||||
|
expect(notations[1].name).to.equal('séparation-de-domaine@proton.ch');
|
||||||
|
expect(notations[1].value).to.deep.equal(new Uint8Array([0, 1, 2, 3]));
|
||||||
|
expect(notations[1].humanReadable).to.be.false;
|
||||||
|
expect(notations[1].critical).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
it('Verify cleartext signed message with two signatures with openpgp.verify', async function() {
|
it('Verify cleartext signed message with two signatures with openpgp.verify', async function() {
|
||||||
const cleartextMessage =
|
const cleartextMessage =
|
||||||
['-----BEGIN PGP SIGNED MESSAGE-----',
|
['-----BEGIN PGP SIGNED MESSAGE-----',
|
||||||
|
|
|
@ -134,6 +134,14 @@ import {
|
||||||
const textSignedObject: Message<string> = await sign({ signingKeys: privateKeys, message: textMessage, format: 'object' });
|
const textSignedObject: Message<string> = await sign({ signingKeys: privateKeys, message: textMessage, format: 'object' });
|
||||||
expect(textSignedObject).to.be.instanceOf(Message);
|
expect(textSignedObject).to.be.instanceOf(Message);
|
||||||
|
|
||||||
|
// Sign text message (armored)
|
||||||
|
const textSignedWithNotations: string = await sign({ signingKeys: privateKeys, message: textMessage, signatureNotations: [{
|
||||||
|
name: 'test@example.org',
|
||||||
|
value: new TextEncoder().encode('test'),
|
||||||
|
humanReadable: true
|
||||||
|
}] });
|
||||||
|
expect(textSignedWithNotations).to.include('-----BEGIN PGP MESSAGE-----');
|
||||||
|
|
||||||
// Verify signed text message (armored)
|
// Verify signed text message (armored)
|
||||||
const signedMessage = await readMessage({ armoredMessage: textSignedArmor });
|
const signedMessage = await readMessage({ armoredMessage: textSignedArmor });
|
||||||
const verifiedText = await verify({ verificationKeys: publicKeys, message: signedMessage });
|
const verifiedText = await verify({ verificationKeys: publicKeys, message: signedMessage });
|
||||||
|
|
Loading…
Reference in New Issue
Block a user