Fix various signature verification issues (#1302)
- Throw on signature parsing (e.g. in `openpgp.readSignature`) if the creation time subpacket is missing - `SignaturePacket.verify` now directly checks for signature creation and expiration times. This makes it easier to thoroughly check the validity of signatures. Also: - `openpgp.revokeKey` now takes a `date` to check the provided revocation certificate - `openpgp.decryptSessionKeys` now takes a `date` to check the validity of the provided private keys - whenever a `date` is used internally, the function accepts a `date` param to allow passing the correct date - Add tests for all of the above - Like `openpgp.generateKey`, `openpgp.reformatKey` now also requires `options.userIDs` - Simplify calling `SubKey.isRevoked/update/getExpirationTime` by adding the `SubKey.mainKey` field to hold the reference of the corresponding `Key` Breaking changes in low-level functions: - Added/removed `date` params: - `Key.update(key, config)` -> `update(key, date, config)` - `Key.applyRevocationCertificate(revocationCertificate, config)` -> `applyRevocationCertificate(revocationCertificate, date, config)` - `Key.signAllUsers(privateKeys, config)` -> `signAllUsers(privateKeys, date, config)` - `Key.verifyAllUsers(keys, config)` -> `verifyAllUsers(keys, date, config)` - `new SignaturePacket(date)` -> `new SignaturePacket()` - `SignaturePacket.sign(key, data, detached)` -> `sign(key, data, date, detached)` - `Message.sign(primaryKey, privateKeys, config)` -> `sign(primaryKey, privateKeys, date, config)` - `Message.decrypt(privateKeys, passwords, sessionKeys, config)` -> `decrypt(privateKeys, passwords, sessionKeys, date, config)` - `Message.decryptSessionKeys(privateKeys, passwords, config)` -> `decryptSessionKeys(privateKeys, passwords, date, config)` - Removed `primaryKey` params: - `SubKey.isRevoked(primaryKey, signature, key, date, config)` -> `isRevoked(signature, key, date, config)` - `SubKey.update(subKey, primaryKey, date, config)` -> `update(subKey, date, config)` - `SubKey.getExpirationTime(primaryKey, date, config)` -> `getExpirationTime(date, config)`
This commit is contained in:
parent
ab7dedf0a5
commit
0e088aec28
22
openpgp.d.ts
vendored
22
openpgp.d.ts
vendored
|
@ -24,7 +24,6 @@ export function encryptKey(options: { privateKey: PrivateKey; passphrase?: strin
|
||||||
export function reformatKey(options: { privateKey: PrivateKey; userIDs?: UserID|UserID[]; passphrase?: string; keyExpirationTime?: number; config?: PartialConfig }): Promise<KeyPair>;
|
export function reformatKey(options: { privateKey: PrivateKey; userIDs?: UserID|UserID[]; passphrase?: string; keyExpirationTime?: number; config?: PartialConfig }): Promise<KeyPair>;
|
||||||
|
|
||||||
export abstract class Key {
|
export abstract class Key {
|
||||||
private primaryKey: PublicKeyPacket | SecretKeyPacket;
|
|
||||||
private keyPacket: PublicKeyPacket | SecretKeyPacket;
|
private keyPacket: PublicKeyPacket | SecretKeyPacket;
|
||||||
public subKeys: SubKey[];
|
public subKeys: SubKey[];
|
||||||
public users: User[];
|
public users: User[];
|
||||||
|
@ -38,12 +37,12 @@ export abstract class Key {
|
||||||
public isPrivate(): boolean;
|
public isPrivate(): boolean;
|
||||||
public isPublic(): boolean;
|
public isPublic(): boolean;
|
||||||
public toPublic(): PublicKey;
|
public toPublic(): PublicKey;
|
||||||
public update(sourceKey: PublicKey, config?: Config): Promise<PublicKey>;
|
public update(sourceKey: PublicKey, date?: Date, config?: Config): Promise<PublicKey>;
|
||||||
public signPrimaryUser(privateKeys: PrivateKey[], date?: Date, userID?: UserID, config?: Config): Promise<PublicKey>
|
public signPrimaryUser(privateKeys: PrivateKey[], date?: Date, userID?: UserID, config?: Config): Promise<PublicKey>
|
||||||
public signAllUsers(privateKeys: PrivateKey[], config?: Config): Promise<PublicKey>
|
public signAllUsers(privateKeys: PrivateKey[], date?: Date, config?: Config): Promise<PublicKey>
|
||||||
public verifyPrimaryKey(date?: Date, userID?: UserID, config?: Config): Promise<void>; // throws on error
|
public verifyPrimaryKey(date?: Date, userID?: UserID, config?: Config): Promise<void>; // throws on error
|
||||||
public verifyPrimaryUser(publicKeys: PublicKey[], date?: Date, userIDs?: UserID, config?: Config): Promise<{ keyID: KeyID, valid: boolean | null }[]>;
|
public verifyPrimaryUser(publicKeys: PublicKey[], date?: Date, userIDs?: UserID, config?: Config): Promise<{ keyID: KeyID, valid: boolean | null }[]>;
|
||||||
public verifyAllUsers(publicKeys: PublicKey[], config?: Config): Promise<{ userID: string, keyID: KeyID, valid: boolean | null }[]>;
|
public verifyAllUsers(publicKeys: PublicKey[], date?: Date, config?: Config): Promise<{ userID: string, keyID: KeyID, valid: boolean | null }[]>;
|
||||||
public isRevoked(signature: SignaturePacket, key?: AnyKeyPacket, date?: Date, config?: Config): Promise<boolean>;
|
public isRevoked(signature: SignaturePacket, key?: AnyKeyPacket, date?: Date, config?: Config): Promise<boolean>;
|
||||||
public getRevocationCertificate(date?: Date, config?: Config): Promise<Stream<string> | string | undefined>;
|
public getRevocationCertificate(date?: Date, config?: Config): Promise<Stream<string> | string | undefined>;
|
||||||
public getEncryptionKey(keyID?: KeyID, date?: Date | null, userID?: UserID, config?: Config): Promise<PublicKey | SubKey>;
|
public getEncryptionKey(keyID?: KeyID, date?: Date | null, userID?: UserID, config?: Config): Promise<PublicKey | SubKey>;
|
||||||
|
@ -68,16 +67,17 @@ export class PrivateKey extends PublicKey {
|
||||||
public isDecrypted(): boolean;
|
public isDecrypted(): boolean;
|
||||||
public addSubkey(options: SubKeyOptions): Promise<PrivateKey>;
|
public addSubkey(options: SubKeyOptions): Promise<PrivateKey>;
|
||||||
public getDecryptionKeys(keyID?: KeyID, date?: Date | null, userID?: UserID, config?: Config): Promise<PrivateKey | SubKey>
|
public getDecryptionKeys(keyID?: KeyID, date?: Date | null, userID?: UserID, config?: Config): Promise<PrivateKey | SubKey>
|
||||||
public update(sourceKey: PublicKey, config?: Config): Promise<PrivateKey>;
|
public update(sourceKey: PublicKey, date?: Date, config?: Config): Promise<PrivateKey>;
|
||||||
public getKeys(keyID?: KeyID): (PrivateKey | SubKey)[];
|
public getKeys(keyID?: KeyID): (PrivateKey | SubKey)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SubKey {
|
export class SubKey {
|
||||||
constructor(subKeyPacket: SecretSubkeyPacket | PublicSubkeyPacket);
|
constructor(subKeyPacket: SecretSubkeyPacket | PublicSubkeyPacket, mainKey: PublicKey);
|
||||||
private keyPacket: SecretSubkeyPacket | PublicSubkeyPacket;
|
private keyPacket: SecretSubkeyPacket | PublicSubkeyPacket;
|
||||||
|
private mainKey: PublicKey;
|
||||||
public bindingSignatures: SignaturePacket[];
|
public bindingSignatures: SignaturePacket[];
|
||||||
public revocationSignatures: SignaturePacket[];
|
public revocationSignatures: SignaturePacket[];
|
||||||
public verify(primaryKey: PublicKeyPacket | SecretKeyPacket, date?: Date, config?: Config): Promise<SignaturePacket>;
|
public verify(date?: Date, config?: Config): Promise<SignaturePacket>;
|
||||||
public isDecrypted(): boolean;
|
public isDecrypted(): boolean;
|
||||||
public getFingerprint(): string;
|
public getFingerprint(): string;
|
||||||
public getCreationTime(): Date;
|
public getCreationTime(): Date;
|
||||||
|
@ -230,7 +230,7 @@ export class Message<T extends MaybeStream<Data>> {
|
||||||
/** Decrypt the message
|
/** Decrypt the message
|
||||||
@param decryptionKeys array of private keys with decrypted secret data
|
@param decryptionKeys array of private keys with decrypted secret data
|
||||||
*/
|
*/
|
||||||
public decrypt(decryptionKeys?: PrivateKey[], passwords?: string[], sessionKeys?: SessionKey[], config?: Config): Promise<Message<MaybeStream<Data>>>;
|
public decrypt(decryptionKeys?: PrivateKey[], passwords?: string[], sessionKeys?: SessionKey[], date?: Date, config?: Config): Promise<Message<MaybeStream<Data>>>;
|
||||||
|
|
||||||
/** Encrypt the message
|
/** Encrypt the message
|
||||||
@param encryptionKeys array of public keys, used to encrypt the message
|
@param encryptionKeys array of public keys, used to encrypt the message
|
||||||
|
@ -444,7 +444,7 @@ export class SignaturePacket extends BasePacket {
|
||||||
public signatureData: null | Uint8Array;
|
public signatureData: null | Uint8Array;
|
||||||
public unhashedSubpackets: null | Uint8Array;
|
public unhashedSubpackets: null | Uint8Array;
|
||||||
public signedHashValue: null | Uint8Array;
|
public signedHashValue: null | Uint8Array;
|
||||||
public created: Date;
|
public created: Date | null;
|
||||||
public signatureExpirationTime: null | number;
|
public signatureExpirationTime: null | number;
|
||||||
public signatureNeverExpires: boolean;
|
public signatureNeverExpires: boolean;
|
||||||
public exportable: null | boolean;
|
public exportable: null | boolean;
|
||||||
|
@ -480,8 +480,8 @@ export class SignaturePacket extends BasePacket {
|
||||||
public preferredAEADAlgorithms: enums.aead[] | null;
|
public preferredAEADAlgorithms: enums.aead[] | null;
|
||||||
public verified: null | boolean;
|
public verified: null | boolean;
|
||||||
public revoked: null | boolean;
|
public revoked: null | boolean;
|
||||||
public sign(key: AnySecretKeyPacket, data: Uint8Array, detached?: boolean): Promise<void>;
|
public sign(key: AnySecretKeyPacket, data: Uint8Array, date?: Date, detached?: boolean): Promise<void>;
|
||||||
public verify(key: AnyKeyPacket, signatureType: enums.signature, data: Uint8Array, detached?: boolean, config?: Config): Promise<void>; // throws on error
|
public verify(key: AnyKeyPacket, signatureType: enums.signature, data: Uint8Array, date?: Date, detached?: boolean, config?: Config): Promise<void>; // throws on error
|
||||||
public isExpired(date?: Date): boolean;
|
public isExpired(date?: Date): boolean;
|
||||||
public getExpirationTime(): Date | typeof Infinity;
|
public getExpirationTime(): Date | typeof Infinity;
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ export async function reformat(options, config) {
|
||||||
throw new Error('Cannot reformat a public key');
|
throw new Error('Cannot reformat a public key');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (privateKey.primaryKey.isDummy()) {
|
if (privateKey.keyPacket.isDummy()) {
|
||||||
throw new Error('Cannot reformat a gnu-dummy primary key');
|
throw new Error('Cannot reformat a gnu-dummy primary key');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options, conf
|
||||||
const dataToSign = {};
|
const dataToSign = {};
|
||||||
dataToSign.userID = userIDPacket;
|
dataToSign.userID = userIDPacket;
|
||||||
dataToSign.key = secretKeyPacket;
|
dataToSign.key = secretKeyPacket;
|
||||||
const signaturePacket = new SignaturePacket(options.date);
|
const signaturePacket = new SignaturePacket();
|
||||||
signaturePacket.signatureType = enums.signature.certGeneric;
|
signaturePacket.signatureType = enums.signature.certGeneric;
|
||||||
signaturePacket.publicKeyAlgorithm = secretKeyPacket.algorithm;
|
signaturePacket.publicKeyAlgorithm = secretKeyPacket.algorithm;
|
||||||
signaturePacket.hashAlgorithm = await helper.getPreferredHashAlgo(null, secretKeyPacket, undefined, undefined, config);
|
signaturePacket.hashAlgorithm = await helper.getPreferredHashAlgo(null, secretKeyPacket, undefined, undefined, config);
|
||||||
|
@ -205,7 +205,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options, conf
|
||||||
signaturePacket.keyExpirationTime = options.keyExpirationTime;
|
signaturePacket.keyExpirationTime = options.keyExpirationTime;
|
||||||
signaturePacket.keyNeverExpires = false;
|
signaturePacket.keyNeverExpires = false;
|
||||||
}
|
}
|
||||||
await signaturePacket.sign(secretKeyPacket, dataToSign);
|
await signaturePacket.sign(secretKeyPacket, dataToSign, options.date);
|
||||||
|
|
||||||
return { userIDPacket, signaturePacket };
|
return { userIDPacket, signaturePacket };
|
||||||
})).then(list => {
|
})).then(list => {
|
||||||
|
|
|
@ -37,45 +37,42 @@ export async function generateSecretKey(options, config) {
|
||||||
/**
|
/**
|
||||||
* Returns the valid and non-expired signature that has the latest creation date, while ignoring signatures created in the future.
|
* Returns the valid and non-expired signature that has the latest creation date, while ignoring signatures created in the future.
|
||||||
* @param {Array<SignaturePacket>} signatures - List of signatures
|
* @param {Array<SignaturePacket>} signatures - List of signatures
|
||||||
|
* @param {PublicKeyPacket|PublicSubkeyPacket} publicKey - Public key packet to verify the signature
|
||||||
* @param {Date} date - Use the given date instead of the current time
|
* @param {Date} date - Use the given date instead of the current time
|
||||||
* @param {Object} config - full configuration
|
* @param {Object} config - full configuration
|
||||||
* @returns {Promise<SignaturePacket>} The latest valid signature.
|
* @returns {Promise<SignaturePacket>} The latest valid signature.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
export async function getLatestValidSignature(signatures, primaryKey, signatureType, dataToVerify, date = new Date(), config) {
|
export async function getLatestValidSignature(signatures, publicKey, signatureType, dataToVerify, date = new Date(), config) {
|
||||||
let signature;
|
let latestValid;
|
||||||
let exception;
|
let exception;
|
||||||
for (let i = signatures.length - 1; i >= 0; i--) {
|
for (let i = signatures.length - 1; i >= 0; i--) {
|
||||||
try {
|
try {
|
||||||
if (
|
if (
|
||||||
(!signature || signatures[i].created >= signature.created) &&
|
(!latestValid || signatures[i].created >= latestValid.created)
|
||||||
// check binding signature is not expired (ie, check for V4 expiration time)
|
|
||||||
!signatures[i].isExpired(date)
|
|
||||||
) {
|
) {
|
||||||
// check binding signature is verified
|
await signatures[i].verify(publicKey, signatureType, dataToVerify, date, undefined, config);
|
||||||
signatures[i].verified || await signatures[i].verify(primaryKey, signatureType, dataToVerify, undefined, config);
|
latestValid = signatures[i];
|
||||||
signature = signatures[i];
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
exception = e;
|
exception = e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!signature) {
|
if (!latestValid) {
|
||||||
throw util.wrapError(
|
throw util.wrapError(
|
||||||
`Could not find valid ${enums.read(enums.signature, signatureType)} signature in key ${primaryKey.getKeyID().toHex()}`
|
`Could not find valid ${enums.read(enums.signature, signatureType)} signature in key ${publicKey.getKeyID().toHex()}`
|
||||||
.replace('certGeneric ', 'self-')
|
.replace('certGeneric ', 'self-')
|
||||||
.replace(/([a-z])([A-Z])/g, (_, $1, $2) => $1 + ' ' + $2.toLowerCase())
|
.replace(/([a-z])([A-Z])/g, (_, $1, $2) => $1 + ' ' + $2.toLowerCase())
|
||||||
, exception);
|
, exception);
|
||||||
}
|
}
|
||||||
return signature;
|
return latestValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isDataExpired(keyPacket, signature, date = new Date()) {
|
export function isDataExpired(keyPacket, signature, date = new Date()) {
|
||||||
const normDate = util.normalizeDate(date);
|
const normDate = util.normalizeDate(date);
|
||||||
if (normDate !== null) {
|
if (normDate !== null) {
|
||||||
const expirationTime = getExpirationTime(keyPacket, signature);
|
const expirationTime = getKeyExpirationTime(keyPacket, signature);
|
||||||
return !(keyPacket.created <= normDate && normDate <= expirationTime) ||
|
return !(keyPacket.created <= normDate && normDate <= expirationTime);
|
||||||
(signature && signature.isExpired(date));
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -91,7 +88,7 @@ export async function createBindingSignature(subkey, primaryKey, options, config
|
||||||
const dataToSign = {};
|
const dataToSign = {};
|
||||||
dataToSign.key = primaryKey;
|
dataToSign.key = primaryKey;
|
||||||
dataToSign.bind = subkey;
|
dataToSign.bind = subkey;
|
||||||
const subkeySignaturePacket = new SignaturePacket(options.date);
|
const subkeySignaturePacket = new SignaturePacket();
|
||||||
subkeySignaturePacket.signatureType = enums.signature.subkeyBinding;
|
subkeySignaturePacket.signatureType = enums.signature.subkeyBinding;
|
||||||
subkeySignaturePacket.publicKeyAlgorithm = primaryKey.algorithm;
|
subkeySignaturePacket.publicKeyAlgorithm = primaryKey.algorithm;
|
||||||
subkeySignaturePacket.hashAlgorithm = await getPreferredHashAlgo(null, subkey, undefined, undefined, config);
|
subkeySignaturePacket.hashAlgorithm = await getPreferredHashAlgo(null, subkey, undefined, undefined, config);
|
||||||
|
@ -107,7 +104,7 @@ export async function createBindingSignature(subkey, primaryKey, options, config
|
||||||
subkeySignaturePacket.keyExpirationTime = options.keyExpirationTime;
|
subkeySignaturePacket.keyExpirationTime = options.keyExpirationTime;
|
||||||
subkeySignaturePacket.keyNeverExpires = false;
|
subkeySignaturePacket.keyNeverExpires = false;
|
||||||
}
|
}
|
||||||
await subkeySignaturePacket.sign(primaryKey, dataToSign);
|
await subkeySignaturePacket.sign(primaryKey, dataToSign, options.date);
|
||||||
return subkeySignaturePacket;
|
return subkeySignaturePacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,6 +186,7 @@ export async function getPreferredAlgo(type, keys = [], date = new Date(), userI
|
||||||
/**
|
/**
|
||||||
* Create signature packet
|
* Create signature packet
|
||||||
* @param {Object} dataToSign - Contains packets to be signed
|
* @param {Object} dataToSign - Contains packets to be signed
|
||||||
|
* @param {PrivateKey} privateKey - key to get preferences from
|
||||||
* @param {SecretKeyPacket|
|
* @param {SecretKeyPacket|
|
||||||
* SecretSubkeyPacket} signingKeyPacket secret key packet for signing
|
* SecretSubkeyPacket} signingKeyPacket secret key packet for signing
|
||||||
* @param {Object} [signatureProperties] - Properties to write on the signature packet before signing
|
* @param {Object} [signatureProperties] - Properties to write on the signature packet before signing
|
||||||
|
@ -205,11 +203,11 @@ export async function createSignaturePacket(dataToSign, privateKey, signingKeyPa
|
||||||
if (!signingKeyPacket.isDecrypted()) {
|
if (!signingKeyPacket.isDecrypted()) {
|
||||||
throw new Error('Private key is not decrypted.');
|
throw new Error('Private key is not decrypted.');
|
||||||
}
|
}
|
||||||
const signaturePacket = new SignaturePacket(date);
|
const signaturePacket = new SignaturePacket();
|
||||||
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);
|
||||||
await signaturePacket.sign(signingKeyPacket, dataToSign, detached);
|
await signaturePacket.sign(signingKeyPacket, dataToSign, date, detached);
|
||||||
return signaturePacket;
|
return signaturePacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,16 +216,17 @@ export async function createSignaturePacket(dataToSign, privateKey, signingKeyPa
|
||||||
* @param {Object} source
|
* @param {Object} source
|
||||||
* @param {Object} dest
|
* @param {Object} dest
|
||||||
* @param {String} attr
|
* @param {String} attr
|
||||||
* @param {Function} checkFn - optional, signature only merged if true
|
* @param {Date} [date] - date to use for signature expiration check, instead of the current time
|
||||||
|
* @param {(SignaturePacket) => Boolean} [checkFn] - signature only merged if true
|
||||||
*/
|
*/
|
||||||
export async function mergeSignatures(source, dest, attr, checkFn) {
|
export async function mergeSignatures(source, dest, attr, date = new Date(), checkFn) {
|
||||||
source = source[attr];
|
source = source[attr];
|
||||||
if (source) {
|
if (source) {
|
||||||
if (!dest[attr].length) {
|
if (!dest[attr].length) {
|
||||||
dest[attr] = source;
|
dest[attr] = source;
|
||||||
} else {
|
} else {
|
||||||
await Promise.all(source.map(async function(sourceSig) {
|
await Promise.all(source.map(async function(sourceSig) {
|
||||||
if (!sourceSig.isExpired() && (!checkFn || await checkFn(sourceSig)) &&
|
if (!sourceSig.isExpired(date) && (!checkFn || await checkFn(sourceSig)) &&
|
||||||
!dest[attr].some(function(destSig) {
|
!dest[attr].some(function(destSig) {
|
||||||
return util.equalsUint8Array(destSig.writeParams(), sourceSig.writeParams());
|
return util.equalsUint8Array(destSig.writeParams(), sourceSig.writeParams());
|
||||||
})) {
|
})) {
|
||||||
|
@ -256,7 +255,6 @@ export async function mergeSignatures(source, dest, attr, checkFn) {
|
||||||
*/
|
*/
|
||||||
export async function isDataRevoked(primaryKey, signatureType, dataToVerify, revocations, signature, key, date = new Date(), config) {
|
export async function isDataRevoked(primaryKey, signatureType, dataToVerify, revocations, signature, key, date = new Date(), config) {
|
||||||
key = key || primaryKey;
|
key = key || primaryKey;
|
||||||
const normDate = util.normalizeDate(date);
|
|
||||||
const revocationKeyIDs = [];
|
const revocationKeyIDs = [];
|
||||||
await Promise.all(revocations.map(async function(revocationSignature) {
|
await Promise.all(revocations.map(async function(revocationSignature) {
|
||||||
try {
|
try {
|
||||||
|
@ -269,10 +267,11 @@ export async function isDataRevoked(primaryKey, signatureType, dataToVerify, rev
|
||||||
// third-party revocation signatures here. (It could also be revoking a
|
// third-party revocation signatures here. (It could also be revoking a
|
||||||
// third-party key certification, which should only affect
|
// third-party key certification, which should only affect
|
||||||
// `verifyAllCertifications`.)
|
// `verifyAllCertifications`.)
|
||||||
(!signature || revocationSignature.issuerKeyID.equals(signature.issuerKeyID)) &&
|
!signature || revocationSignature.issuerKeyID.equals(signature.issuerKeyID)
|
||||||
!(config.revocationsExpire && revocationSignature.isExpired(normDate))
|
|
||||||
) {
|
) {
|
||||||
revocationSignature.verified || await revocationSignature.verify(key, signatureType, dataToVerify, undefined, config);
|
await revocationSignature.verify(
|
||||||
|
key, signatureType, dataToVerify, config.revocationsExpire ? date : null, false, config
|
||||||
|
);
|
||||||
|
|
||||||
// TODO get an identifier of the revoked object instead
|
// TODO get an identifier of the revoked object instead
|
||||||
revocationKeyIDs.push(revocationSignature.issuerKeyID);
|
revocationKeyIDs.push(revocationSignature.issuerKeyID);
|
||||||
|
@ -288,7 +287,14 @@ export async function isDataRevoked(primaryKey, signatureType, dataToVerify, rev
|
||||||
return revocationKeyIDs.length > 0;
|
return revocationKeyIDs.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getExpirationTime(keyPacket, signature) {
|
/**
|
||||||
|
* Returns key expiration time based on the given certification signature.
|
||||||
|
* The expiration time of the signature is ignored.
|
||||||
|
* @param {PublicSubkeyPacket|PublicKeyPacket} keyPacket - key to check
|
||||||
|
* @param {SignaturePacket} signature - signature to process
|
||||||
|
* @returns {Date|Infinity} expiration time or infinity if the key does not expire
|
||||||
|
*/
|
||||||
|
export function getKeyExpirationTime(keyPacket, signature) {
|
||||||
let expirationTime;
|
let expirationTime;
|
||||||
// check V4 expiration time
|
// check V4 expiration time
|
||||||
if (signature.keyNeverExpires === false) {
|
if (signature.keyNeverExpires === false) {
|
||||||
|
@ -355,10 +361,6 @@ export function sanitizeKeyOptions(options, subkeyDefaults = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isValidSigningKeyPacket(keyPacket, signature) {
|
export function isValidSigningKeyPacket(keyPacket, signature) {
|
||||||
if (!signature.verified || signature.revoked !== false) { // Sanity check
|
|
||||||
throw new Error('Signature not verified');
|
|
||||||
}
|
|
||||||
|
|
||||||
const keyAlgo = enums.write(enums.publicKey, keyPacket.algorithm);
|
const keyAlgo = enums.write(enums.publicKey, keyPacket.algorithm);
|
||||||
return keyAlgo !== enums.publicKey.rsaEncrypt &&
|
return keyAlgo !== enums.publicKey.rsaEncrypt &&
|
||||||
keyAlgo !== enums.publicKey.elgamal &&
|
keyAlgo !== enums.publicKey.elgamal &&
|
||||||
|
@ -368,10 +370,6 @@ export function isValidSigningKeyPacket(keyPacket, signature) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isValidEncryptionKeyPacket(keyPacket, signature) {
|
export function isValidEncryptionKeyPacket(keyPacket, signature) {
|
||||||
if (!signature.verified || signature.revoked !== false) { // Sanity check
|
|
||||||
throw new Error('Signature not verified');
|
|
||||||
}
|
|
||||||
|
|
||||||
const keyAlgo = enums.write(enums.publicKey, keyPacket.algorithm);
|
const keyAlgo = enums.write(enums.publicKey, keyPacket.algorithm);
|
||||||
return keyAlgo !== enums.publicKey.dsa &&
|
return keyAlgo !== enums.publicKey.dsa &&
|
||||||
keyAlgo !== enums.publicKey.rsaSign &&
|
keyAlgo !== enums.publicKey.rsaSign &&
|
||||||
|
@ -383,10 +381,6 @@ export function isValidEncryptionKeyPacket(keyPacket, signature) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isValidDecryptionKeyPacket(signature, config) {
|
export function isValidDecryptionKeyPacket(signature, config) {
|
||||||
if (!signature.verified) { // Sanity check
|
|
||||||
throw new Error('Signature not verified');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.allowInsecureDecryptionWithSigningKeys) {
|
if (config.allowInsecureDecryptionWithSigningKeys) {
|
||||||
// This is only relevant for RSA keys, all other signing algorithms cannot decrypt
|
// This is only relevant for RSA keys, all other signing algorithms cannot decrypt
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -42,10 +42,6 @@ const allowedRevocationPackets = /*#__PURE__*/ util.constructAllowedPackets([Sig
|
||||||
* @borrows PublicKeyPacket#getCreationTime as Key#getCreationTime
|
* @borrows PublicKeyPacket#getCreationTime as Key#getCreationTime
|
||||||
*/
|
*/
|
||||||
class Key {
|
class Key {
|
||||||
get primaryKey() {
|
|
||||||
return this.keyPacket;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms packetlist to structured key data
|
* Transforms packetlist to structured key data
|
||||||
* @param {PacketList} packetlist - The packets that form a key
|
* @param {PacketList} packetlist - The packets that form a key
|
||||||
|
@ -80,7 +76,7 @@ class Key {
|
||||||
case enums.packet.publicSubkey:
|
case enums.packet.publicSubkey:
|
||||||
case enums.packet.secretSubkey:
|
case enums.packet.secretSubkey:
|
||||||
user = null;
|
user = null;
|
||||||
subKey = new SubKey(packet);
|
subKey = new SubKey(packet, this);
|
||||||
this.subKeys.push(subKey);
|
this.subKeys.push(subKey);
|
||||||
break;
|
break;
|
||||||
case enums.packet.signature:
|
case enums.packet.signature:
|
||||||
|
@ -227,10 +223,10 @@ class Key {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns last created key or key by given keyID that is available for signing and verification
|
* Returns last created key or key by given keyID that is available for signing and verification
|
||||||
* @param {module:type/keyid~KeyID} keyID, optional
|
* @param {module:type/keyid~KeyID} [keyID] - key ID of a specific key to retrieve
|
||||||
* @param {Date} [date] - Use the given date for verification instead of the current time
|
* @param {Date} [date] - use the fiven date date to to check key validity instead of the current date
|
||||||
* @param {Object} userID, optional user ID
|
* @param {Object} [userID] - filter keys for the given user ID
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Key|SubKey>} signing key
|
* @returns {Promise<Key|SubKey>} signing key
|
||||||
* @throws if no valid signing key was found
|
* @throws if no valid signing key was found
|
||||||
* @async
|
* @async
|
||||||
|
@ -243,7 +239,7 @@ class Key {
|
||||||
for (const subKey of subKeys) {
|
for (const subKey of subKeys) {
|
||||||
if (!keyID || subKey.getKeyID().equals(keyID)) {
|
if (!keyID || subKey.getKeyID().equals(keyID)) {
|
||||||
try {
|
try {
|
||||||
await subKey.verify(primaryKey, date, config);
|
await subKey.verify(date, config);
|
||||||
const dataToVerify = { key: primaryKey, bind: subKey.keyPacket };
|
const dataToVerify = { key: primaryKey, bind: subKey.keyPacket };
|
||||||
const bindingSignature = await helper.getLatestValidSignature(
|
const bindingSignature = await helper.getLatestValidSignature(
|
||||||
subKey.bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config
|
subKey.bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config
|
||||||
|
@ -281,10 +277,10 @@ class Key {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns last created key or key by given keyID that is available for encryption or decryption
|
* Returns last created key or key by given keyID that is available for encryption or decryption
|
||||||
* @param {module:type/keyid~KeyID} keyID, optional
|
* @param {module:type/keyid~KeyID} [keyID] - key ID of a specific key to retrieve
|
||||||
* @param {Date} date, optional
|
* @param {Date} [date] - use the fiven date date to to check key validity instead of the current date
|
||||||
* @param {String} userID, optional
|
* @param {Object} [userID] - filter keys for the given user ID
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Key|SubKey>} encryption key
|
* @returns {Promise<Key|SubKey>} encryption key
|
||||||
* @throws if no valid encryption key was found
|
* @throws if no valid encryption key was found
|
||||||
* @async
|
* @async
|
||||||
|
@ -298,7 +294,7 @@ class Key {
|
||||||
for (const subKey of subKeys) {
|
for (const subKey of subKeys) {
|
||||||
if (!keyID || subKey.getKeyID().equals(keyID)) {
|
if (!keyID || subKey.getKeyID().equals(keyID)) {
|
||||||
try {
|
try {
|
||||||
await subKey.verify(primaryKey, date, config);
|
await subKey.verify(date, config);
|
||||||
const dataToVerify = { key: primaryKey, bind: subKey.keyPacket };
|
const dataToVerify = { key: primaryKey, bind: subKey.keyPacket };
|
||||||
const bindingSignature = await helper.getLatestValidSignature(subKey.bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config);
|
const bindingSignature = await helper.getLatestValidSignature(subKey.bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config);
|
||||||
if (helper.isValidEncryptionKeyPacket(subKey.keyPacket, bindingSignature)) {
|
if (helper.isValidEncryptionKeyPacket(subKey.keyPacket, bindingSignature)) {
|
||||||
|
@ -332,7 +328,7 @@ class Key {
|
||||||
* SecretSubkeyPacket|
|
* SecretSubkeyPacket|
|
||||||
* PublicKeyPacket|
|
* PublicKeyPacket|
|
||||||
* SecretKeyPacket} key, optional The key to verify the signature
|
* SecretKeyPacket} key, optional The key to verify the signature
|
||||||
* @param {Date} date - Use the given date instead of the current time
|
* @param {Date} [date] - Use the given date for verification, instead of the current time
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Boolean>} True if the certificate is revoked.
|
* @returns {Promise<Boolean>} True if the certificate is revoked.
|
||||||
* @async
|
* @async
|
||||||
|
@ -360,7 +356,7 @@ class Key {
|
||||||
}
|
}
|
||||||
// check for valid, unrevoked, unexpired self signature
|
// check for valid, unrevoked, unexpired self signature
|
||||||
const { selfCertification } = await this.getPrimaryUser(date, userID, config);
|
const { selfCertification } = await this.getPrimaryUser(date, userID, config);
|
||||||
// check for expiration time
|
// check for expiration time in binding signatures
|
||||||
if (helper.isDataExpired(primaryKey, selfCertification, date)) {
|
if (helper.isDataExpired(primaryKey, selfCertification, date)) {
|
||||||
throw new Error('Primary key is expired');
|
throw new Error('Primary key is expired');
|
||||||
}
|
}
|
||||||
|
@ -371,17 +367,17 @@ class Key {
|
||||||
* When `capabilities` is null, defaults to returning the expiry date of the primary key.
|
* When `capabilities` is null, defaults to returning the expiry date of the primary key.
|
||||||
* Returns null if `capabilities` is passed and the key does not have the specified capabilities or is revoked or invalid.
|
* Returns null if `capabilities` is passed and the key does not have the specified capabilities or is revoked or invalid.
|
||||||
* Returns Infinity if the key doesn't expire.
|
* Returns Infinity if the key doesn't expire.
|
||||||
* @param {encrypt|sign|encrypt_sign} capabilities, optional
|
* @param {encrypt|sign|encrypt_sign} [capabilities] - capabilities to look up
|
||||||
* @param {module:type/keyid~KeyID} keyID, optional
|
* @param {module:type/keyid~KeyID} [keyID] - key ID of the specific key to check
|
||||||
* @param {Object} userID, optional user ID
|
* @param {Object} [userID] - User ID to consider instead of the primary user
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Date | Infinity | null>}
|
* @returns {Promise<Date | Infinity | null>}
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async getExpirationTime(capabilities, keyID, userID, config = defaultConfig) {
|
async getExpirationTime(capabilities, keyID, userID, config = defaultConfig) {
|
||||||
const primaryUser = await this.getPrimaryUser(null, userID, config);
|
const primaryUser = await this.getPrimaryUser(null, userID, config);
|
||||||
const selfCert = primaryUser.selfCertification;
|
const selfCert = primaryUser.selfCertification;
|
||||||
const keyExpiry = helper.getExpirationTime(this.keyPacket, selfCert);
|
const keyExpiry = helper.getKeyExpirationTime(this.keyPacket, selfCert);
|
||||||
const sigExpiry = selfCert.getExpirationTime();
|
const sigExpiry = selfCert.getExpirationTime();
|
||||||
let expiry = keyExpiry < sigExpiry ? keyExpiry : sigExpiry;
|
let expiry = keyExpiry < sigExpiry ? keyExpiry : sigExpiry;
|
||||||
if (capabilities === 'encrypt' || capabilities === 'encrypt_sign') {
|
if (capabilities === 'encrypt' || capabilities === 'encrypt_sign') {
|
||||||
|
@ -389,7 +385,7 @@ class Key {
|
||||||
await this.getEncryptionKey(keyID, expiry, userID, { ...config, rejectPublicKeyAlgorithms: new Set(), minRSABits: 0 }).catch(() => {}) ||
|
await this.getEncryptionKey(keyID, expiry, userID, { ...config, rejectPublicKeyAlgorithms: new Set(), minRSABits: 0 }).catch(() => {}) ||
|
||||||
await this.getEncryptionKey(keyID, null, userID, { ...config, rejectPublicKeyAlgorithms: new Set(), minRSABits: 0 }).catch(() => {});
|
await this.getEncryptionKey(keyID, null, userID, { ...config, rejectPublicKeyAlgorithms: new Set(), minRSABits: 0 }).catch(() => {});
|
||||||
if (!encryptKey) return null;
|
if (!encryptKey) return null;
|
||||||
const encryptExpiry = await encryptKey.getExpirationTime(this.keyPacket, undefined, config);
|
const encryptExpiry = await encryptKey.getExpirationTime(null, config);
|
||||||
if (encryptExpiry < expiry) expiry = encryptExpiry;
|
if (encryptExpiry < expiry) expiry = encryptExpiry;
|
||||||
}
|
}
|
||||||
if (capabilities === 'sign' || capabilities === 'encrypt_sign') {
|
if (capabilities === 'sign' || capabilities === 'encrypt_sign') {
|
||||||
|
@ -397,7 +393,7 @@ class Key {
|
||||||
await this.getSigningKey(keyID, expiry, userID, { ...config, rejectPublicKeyAlgorithms: new Set(), minRSABits: 0 }).catch(() => {}) ||
|
await this.getSigningKey(keyID, expiry, userID, { ...config, rejectPublicKeyAlgorithms: new Set(), minRSABits: 0 }).catch(() => {}) ||
|
||||||
await this.getSigningKey(keyID, null, userID, { ...config, rejectPublicKeyAlgorithms: new Set(), minRSABits: 0 }).catch(() => {});
|
await this.getSigningKey(keyID, null, userID, { ...config, rejectPublicKeyAlgorithms: new Set(), minRSABits: 0 }).catch(() => {});
|
||||||
if (!signKey) return null;
|
if (!signKey) return null;
|
||||||
const signExpiry = await signKey.getExpirationTime(this.keyPacket, undefined, config);
|
const signExpiry = await signKey.getExpirationTime(null, config);
|
||||||
if (signExpiry < expiry) expiry = signExpiry;
|
if (signExpiry < expiry) expiry = signExpiry;
|
||||||
}
|
}
|
||||||
return expiry;
|
return expiry;
|
||||||
|
@ -467,11 +463,12 @@ class Key {
|
||||||
* If the source key is a private key and the destination key is public,
|
* If the source key is a private key and the destination key is public,
|
||||||
* a private key is returned.
|
* a private key is returned.
|
||||||
* @param {Key} sourceKey - Source key to merge
|
* @param {Key} sourceKey - Source key to merge
|
||||||
|
* @param {Date} [date] - Date to verify validity of signatures and keys
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Key>} updated key
|
* @returns {Promise<Key>} updated key
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async update(sourceKey, config = defaultConfig) {
|
async update(sourceKey, date = new Date(), config = defaultConfig) {
|
||||||
if (!this.hasSameFingerprintAs(sourceKey)) {
|
if (!this.hasSameFingerprintAs(sourceKey)) {
|
||||||
throw new Error('Primary key fingerprints must be equal to update the key');
|
throw new Error('Primary key fingerprints must be equal to update the key');
|
||||||
}
|
}
|
||||||
|
@ -495,11 +492,11 @@ class Key {
|
||||||
// hence we don't need to convert the destination key type
|
// hence we don't need to convert the destination key type
|
||||||
const updatedKey = this.clone();
|
const updatedKey = this.clone();
|
||||||
// revocation signatures
|
// revocation signatures
|
||||||
await helper.mergeSignatures(sourceKey, updatedKey, 'revocationSignatures', srcRevSig => {
|
await helper.mergeSignatures(sourceKey, updatedKey, 'revocationSignatures', date, srcRevSig => {
|
||||||
return helper.isDataRevoked(updatedKey.keyPacket, enums.signature.keyRevocation, updatedKey, [srcRevSig], null, sourceKey.keyPacket, undefined, config);
|
return helper.isDataRevoked(updatedKey.keyPacket, enums.signature.keyRevocation, updatedKey, [srcRevSig], null, sourceKey.keyPacket, date, config);
|
||||||
});
|
});
|
||||||
// direct signatures
|
// direct signatures
|
||||||
await helper.mergeSignatures(sourceKey, updatedKey, 'directSignatures');
|
await helper.mergeSignatures(sourceKey, updatedKey, 'directSignatures', date);
|
||||||
// update users
|
// update users
|
||||||
await Promise.all(sourceKey.users.map(async srcUser => {
|
await Promise.all(sourceKey.users.map(async srcUser => {
|
||||||
// multiple users with the same ID/attribute are not explicitly disallowed by the spec
|
// multiple users with the same ID/attribute are not explicitly disallowed by the spec
|
||||||
|
@ -510,7 +507,7 @@ class Key {
|
||||||
));
|
));
|
||||||
if (usersToUpdate.length > 0) {
|
if (usersToUpdate.length > 0) {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
usersToUpdate.map(userToUpdate => userToUpdate.update(srcUser, updatedKey.keyPacket, config))
|
usersToUpdate.map(userToUpdate => userToUpdate.update(srcUser, updatedKey.keyPacket, date, config))
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
updatedKey.users.push(srcUser);
|
updatedKey.users.push(srcUser);
|
||||||
|
@ -524,7 +521,7 @@ class Key {
|
||||||
));
|
));
|
||||||
if (subkeysToUpdate.length > 0) {
|
if (subkeysToUpdate.length > 0) {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
subkeysToUpdate.map(subkeyToUpdate => subkeyToUpdate.update(srcSubkey, updatedKey.keyPacket, config))
|
subkeysToUpdate.map(subkeyToUpdate => subkeyToUpdate.update(srcSubkey, date, config))
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
updatedKey.subKeys.push(srcSubkey);
|
updatedKey.subKeys.push(srcSubkey);
|
||||||
|
@ -555,11 +552,12 @@ class Key {
|
||||||
* This adds the first signature packet in the armored text to the key,
|
* This adds the first signature packet in the armored text to the key,
|
||||||
* if it is a valid revocation signature.
|
* if it is a valid revocation signature.
|
||||||
* @param {String} revocationCertificate - armored revocation certificate
|
* @param {String} revocationCertificate - armored revocation certificate
|
||||||
|
* @param {Date} [date] - Date to verify the certificate
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Key>} Revoked key.
|
* @returns {Promise<Key>} Revoked key.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async applyRevocationCertificate(revocationCertificate, config = defaultConfig) {
|
async applyRevocationCertificate(revocationCertificate, date = new Date(), config = defaultConfig) {
|
||||||
const input = await unarmor(revocationCertificate, config);
|
const input = await unarmor(revocationCertificate, config);
|
||||||
const packetlist = await PacketList.fromBinary(input.data, allowedRevocationPackets, config);
|
const packetlist = await PacketList.fromBinary(input.data, allowedRevocationPackets, config);
|
||||||
const revocationSignature = packetlist.findPacket(enums.packet.signature);
|
const revocationSignature = packetlist.findPacket(enums.packet.signature);
|
||||||
|
@ -569,11 +567,8 @@ class Key {
|
||||||
if (!revocationSignature.issuerKeyID.equals(this.getKeyID())) {
|
if (!revocationSignature.issuerKeyID.equals(this.getKeyID())) {
|
||||||
throw new Error('Revocation signature does not match key');
|
throw new Error('Revocation signature does not match key');
|
||||||
}
|
}
|
||||||
if (revocationSignature.isExpired()) {
|
|
||||||
throw new Error('Revocation signature is expired');
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await revocationSignature.verify(this.keyPacket, enums.signature.keyRevocation, { key: this.keyPacket }, undefined, config);
|
await revocationSignature.verify(this.keyPacket, enums.signature.keyRevocation, { key: this.keyPacket }, date, undefined, config);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw util.wrapError('Could not verify revocation signature', e);
|
throw util.wrapError('Could not verify revocation signature', e);
|
||||||
}
|
}
|
||||||
|
@ -593,7 +588,7 @@ class Key {
|
||||||
*/
|
*/
|
||||||
async signPrimaryUser(privateKeys, date, userID, config = defaultConfig) {
|
async signPrimaryUser(privateKeys, date, userID, config = defaultConfig) {
|
||||||
const { index, user } = await this.getPrimaryUser(date, userID, config);
|
const { index, user } = await this.getPrimaryUser(date, userID, config);
|
||||||
const userSign = await user.sign(this.keyPacket, privateKeys, config);
|
const userSign = await user.sign(this.keyPacket, privateKeys, date, config);
|
||||||
const key = this.clone();
|
const key = this.clone();
|
||||||
key.users[index] = userSign;
|
key.users[index] = userSign;
|
||||||
return key;
|
return key;
|
||||||
|
@ -602,15 +597,16 @@ class Key {
|
||||||
/**
|
/**
|
||||||
* Signs all users of key
|
* Signs all users of key
|
||||||
* @param {Array<PrivateKey>} privateKeys - decrypted private keys for signing
|
* @param {Array<PrivateKey>} privateKeys - decrypted private keys for signing
|
||||||
|
* @param {Date} [date] - Use the given date for signing, instead of the current time
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Key>} Key with new certificate signature.
|
* @returns {Promise<Key>} Key with new certificate signature.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async signAllUsers(privateKeys, config = defaultConfig) {
|
async signAllUsers(privateKeys, date = new Date(), config = defaultConfig) {
|
||||||
const that = this;
|
const that = this;
|
||||||
const key = this.clone();
|
const key = this.clone();
|
||||||
key.users = await Promise.all(this.users.map(function(user) {
|
key.users = await Promise.all(this.users.map(function(user) {
|
||||||
return user.sign(that.keyPacket, privateKeys, config);
|
return user.sign(that.keyPacket, privateKeys, date, config);
|
||||||
}));
|
}));
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
@ -629,11 +625,11 @@ class Key {
|
||||||
* }>>} List of signer's keyID and validity of signature
|
* }>>} List of signer's keyID and validity of signature
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async verifyPrimaryUser(keys, date, userID, config = defaultConfig) {
|
async verifyPrimaryUser(keys, date = new Date(), userID, config = defaultConfig) {
|
||||||
const primaryKey = this.keyPacket;
|
const primaryKey = this.keyPacket;
|
||||||
const { user } = await this.getPrimaryUser(date, userID, config);
|
const { user } = await this.getPrimaryUser(date, userID, config);
|
||||||
const results = keys ? await user.verifyAllCertifications(primaryKey, keys, undefined, config) :
|
const results = keys ? await user.verifyAllCertifications(primaryKey, keys, date, config) :
|
||||||
[{ keyID: primaryKey.getKeyID(), valid: await user.verify(primaryKey, undefined, config).catch(() => false) }];
|
[{ keyID: primaryKey.getKeyID(), valid: await user.verify(primaryKey, date, config).catch(() => false) }];
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -642,6 +638,7 @@ class Key {
|
||||||
* - if no arguments are given, verifies the self certificates;
|
* - if no arguments are given, verifies the self certificates;
|
||||||
* - otherwise, verifies all certificates signed with given keys.
|
* - otherwise, verifies all certificates signed with given keys.
|
||||||
* @param {Array<Key>} keys - array of keys to verify certificate signatures
|
* @param {Array<Key>} keys - array of keys to verify certificate signatures
|
||||||
|
* @param {Date} [date] - Use the given date for verification instead of the current time
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Array<{
|
* @returns {Promise<Array<{
|
||||||
* userID: String,
|
* userID: String,
|
||||||
|
@ -650,12 +647,12 @@ class Key {
|
||||||
* }>>} List of userID, signer's keyID and validity of signature
|
* }>>} List of userID, signer's keyID and validity of signature
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async verifyAllUsers(keys, config = defaultConfig) {
|
async verifyAllUsers(keys, date = new Date(), config = defaultConfig) {
|
||||||
const results = [];
|
const results = [];
|
||||||
const primaryKey = this.keyPacket;
|
const primaryKey = this.keyPacket;
|
||||||
await Promise.all(this.users.map(async function(user) {
|
await Promise.all(this.users.map(async function(user) {
|
||||||
const signatures = keys ? await user.verifyAllCertifications(primaryKey, keys, undefined, config) :
|
const signatures = keys ? await user.verifyAllCertifications(primaryKey, keys, date, config) :
|
||||||
[{ keyID: primaryKey.getKeyID(), valid: await user.verify(primaryKey, undefined, config).catch(() => false) }];
|
[{ keyID: primaryKey.getKeyID(), valid: await user.verify(primaryKey, date, config).catch(() => false) }];
|
||||||
signatures.forEach(signature => {
|
signatures.forEach(signature => {
|
||||||
results.push({
|
results.push({
|
||||||
userID: user.userID.userID,
|
userID: user.userID.userID,
|
||||||
|
|
|
@ -136,8 +136,8 @@ class PrivateKey extends PublicKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
let signingKeyPacket;
|
let signingKeyPacket;
|
||||||
if (!this.primaryKey.isDummy()) {
|
if (!this.keyPacket.isDummy()) {
|
||||||
signingKeyPacket = this.primaryKey;
|
signingKeyPacket = this.keyPacket;
|
||||||
} else {
|
} else {
|
||||||
/**
|
/**
|
||||||
* It is enough to validate any signing keys
|
* It is enough to validate any signing keys
|
||||||
|
@ -196,7 +196,7 @@ class PrivateKey extends PublicKey {
|
||||||
throw new Error('Need private key for revoking');
|
throw new Error('Need private key for revoking');
|
||||||
}
|
}
|
||||||
const dataToSign = { key: this.keyPacket };
|
const dataToSign = { key: this.keyPacket };
|
||||||
const key = await this.clone();
|
const key = this.clone();
|
||||||
key.revocationSignatures.push(await helper.createSignaturePacket(dataToSign, null, this.keyPacket, {
|
key.revocationSignatures.push(await helper.createSignaturePacket(dataToSign, null, this.keyPacket, {
|
||||||
signatureType: enums.signature.keyRevocation,
|
signatureType: enums.signature.keyRevocation,
|
||||||
reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag),
|
reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag),
|
||||||
|
@ -227,7 +227,7 @@ class PrivateKey extends PublicKey {
|
||||||
if (options.rsaBits < config.minRSABits) {
|
if (options.rsaBits < config.minRSABits) {
|
||||||
throw new Error(`rsaBits should be at least ${config.minRSABits}, got: ${options.rsaBits}`);
|
throw new Error(`rsaBits should be at least ${config.minRSABits}, got: ${options.rsaBits}`);
|
||||||
}
|
}
|
||||||
const secretKeyPacket = this.primaryKey;
|
const secretKeyPacket = this.keyPacket;
|
||||||
if (secretKeyPacket.isDummy()) {
|
if (secretKeyPacket.isDummy()) {
|
||||||
throw new Error("Cannot add subkey to gnu-dummy primary key");
|
throw new Error("Cannot add subkey to gnu-dummy primary key");
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,15 @@ import defaultConfig from '../config';
|
||||||
* @borrows PublicSubkeyPacket#isDecrypted as SubKey#isDecrypted
|
* @borrows PublicSubkeyPacket#isDecrypted as SubKey#isDecrypted
|
||||||
*/
|
*/
|
||||||
class SubKey {
|
class SubKey {
|
||||||
constructor(subKeyPacket) {
|
/**
|
||||||
|
* @param {SecretSubkeyPacket|PublicSubkeyPacket} subKeyPacket - subkey packet to hold in the Subkey
|
||||||
|
* @param {Key} mainKey - reference to main Key object, containing the primary key packet corresponding to the subkey
|
||||||
|
*/
|
||||||
|
constructor(subKeyPacket, mainKey) {
|
||||||
this.keyPacket = subKeyPacket;
|
this.keyPacket = subKeyPacket;
|
||||||
this.bindingSignatures = [];
|
this.bindingSignatures = [];
|
||||||
this.revocationSignatures = [];
|
this.revocationSignatures = [];
|
||||||
|
this.mainKey = mainKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,19 +43,18 @@ class SubKey {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a binding signature of a subkey is revoked
|
* Checks if a binding signature of a subkey is revoked
|
||||||
* @param {SecretKeyPacket|
|
|
||||||
* PublicKeyPacket} primaryKey The primary key packet
|
|
||||||
* @param {SignaturePacket} signature - The binding signature to verify
|
* @param {SignaturePacket} signature - The binding signature to verify
|
||||||
* @param {PublicSubkeyPacket|
|
* @param {PublicSubkeyPacket|
|
||||||
* SecretSubkeyPacket|
|
* SecretSubkeyPacket|
|
||||||
* PublicKeyPacket|
|
* PublicKeyPacket|
|
||||||
* SecretKeyPacket} key, optional The key to verify the signature
|
* SecretKeyPacket} key, optional The key to verify the signature
|
||||||
* @param {Date} date - Use the given date instead of the current time
|
* @param {Date} [date] - Use the given date for verification instead of the current time
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Boolean>} True if the binding signature is revoked.
|
* @returns {Promise<Boolean>} True if the binding signature is revoked.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async isRevoked(primaryKey, signature, key, date = new Date(), config = defaultConfig) {
|
async isRevoked(signature, key, date = new Date(), config = defaultConfig) {
|
||||||
|
const primaryKey = this.mainKey.keyPacket;
|
||||||
return helper.isDataRevoked(
|
return helper.isDataRevoked(
|
||||||
primaryKey, enums.signature.subkeyRevocation, {
|
primaryKey, enums.signature.subkeyRevocation, {
|
||||||
key: primaryKey,
|
key: primaryKey,
|
||||||
|
@ -62,20 +66,19 @@ class SubKey {
|
||||||
/**
|
/**
|
||||||
* Verify subkey. Checks for revocation signatures, expiration time
|
* Verify subkey. Checks for revocation signatures, expiration time
|
||||||
* and valid binding signature.
|
* and valid binding signature.
|
||||||
* @param {SecretKeyPacket|
|
|
||||||
* PublicKeyPacket} primaryKey The primary key packet
|
|
||||||
* @param {Date} date - Use the given date instead of the current time
|
* @param {Date} date - Use the given date instead of the current time
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<SignaturePacket>}
|
* @returns {Promise<SignaturePacket>}
|
||||||
* @throws {Error} if the subkey is invalid.
|
* @throws {Error} if the subkey is invalid.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async verify(primaryKey, date = new Date(), config = defaultConfig) {
|
async verify(date = new Date(), config = defaultConfig) {
|
||||||
|
const primaryKey = this.mainKey.keyPacket;
|
||||||
const dataToVerify = { key: primaryKey, bind: this.keyPacket };
|
const dataToVerify = { key: primaryKey, bind: this.keyPacket };
|
||||||
// check subkey binding signatures
|
// check subkey binding signatures
|
||||||
const bindingSignature = await helper.getLatestValidSignature(this.bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config);
|
const bindingSignature = await helper.getLatestValidSignature(this.bindingSignatures, primaryKey, enums.signature.subkeyBinding, dataToVerify, date, config);
|
||||||
// check binding signature is not revoked
|
// check binding signature is not revoked
|
||||||
if (bindingSignature.revoked || await this.isRevoked(primaryKey, bindingSignature, null, date, config)) {
|
if (bindingSignature.revoked || await this.isRevoked(bindingSignature, null, date, config)) {
|
||||||
throw new Error('Subkey is revoked');
|
throw new Error('Subkey is revoked');
|
||||||
}
|
}
|
||||||
// check for expiration time
|
// check for expiration time
|
||||||
|
@ -86,16 +89,15 @@ class SubKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the expiration time of the subkey or Infinity if key does not expire
|
* Returns the expiration time of the subkey or Infinity if key does not expire.
|
||||||
* Returns null if the subkey is invalid.
|
* Returns null if the subkey is invalid.
|
||||||
* @param {SecretKeyPacket|
|
|
||||||
* PublicKeyPacket} primaryKey The primary key packet
|
|
||||||
* @param {Date} date - Use the given date instead of the current time
|
* @param {Date} date - Use the given date instead of the current time
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Date | Infinity | null>}
|
* @returns {Promise<Date | Infinity | null>}
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async getExpirationTime(primaryKey, date = new Date(), config = defaultConfig) {
|
async getExpirationTime(date = new Date(), config = defaultConfig) {
|
||||||
|
const primaryKey = this.mainKey.keyPacket;
|
||||||
const dataToVerify = { key: primaryKey, bind: this.keyPacket };
|
const dataToVerify = { key: primaryKey, bind: this.keyPacket };
|
||||||
let bindingSignature;
|
let bindingSignature;
|
||||||
try {
|
try {
|
||||||
|
@ -103,7 +105,7 @@ class SubKey {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const keyExpiry = helper.getExpirationTime(this.keyPacket, bindingSignature);
|
const keyExpiry = helper.getKeyExpirationTime(this.keyPacket, bindingSignature);
|
||||||
const sigExpiry = bindingSignature.getExpirationTime();
|
const sigExpiry = bindingSignature.getExpirationTime();
|
||||||
return keyExpiry < sigExpiry ? keyExpiry : sigExpiry;
|
return keyExpiry < sigExpiry ? keyExpiry : sigExpiry;
|
||||||
}
|
}
|
||||||
|
@ -111,13 +113,13 @@ class SubKey {
|
||||||
/**
|
/**
|
||||||
* Update subkey with new components from specified subkey
|
* Update subkey with new components from specified subkey
|
||||||
* @param {SubKey} subKey - Source subkey to merge
|
* @param {SubKey} subKey - Source subkey to merge
|
||||||
* @param {SecretKeyPacket|
|
* @param {Date} [date] - Date to verify validity of signatures
|
||||||
SecretSubkeyPacket} primaryKey primary key used for validation
|
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @throws {Error} if update failed
|
* @throws {Error} if update failed
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async update(subKey, primaryKey, config = defaultConfig) {
|
async update(subKey, date = new Date(), config = defaultConfig) {
|
||||||
|
const primaryKey = this.mainKey.keyPacket;
|
||||||
if (!this.hasSameFingerprintAs(subKey)) {
|
if (!this.hasSameFingerprintAs(subKey)) {
|
||||||
throw new Error('SubKey update method: fingerprints of subkeys not equal');
|
throw new Error('SubKey update method: fingerprints of subkeys not equal');
|
||||||
}
|
}
|
||||||
|
@ -129,7 +131,7 @@ class SubKey {
|
||||||
// update missing binding signatures
|
// update missing binding signatures
|
||||||
const that = this;
|
const that = this;
|
||||||
const dataToVerify = { key: primaryKey, bind: that.keyPacket };
|
const dataToVerify = { key: primaryKey, bind: that.keyPacket };
|
||||||
await helper.mergeSignatures(subKey, this, 'bindingSignatures', async function(srcBindSig) {
|
await helper.mergeSignatures(subKey, this, 'bindingSignatures', date, async function(srcBindSig) {
|
||||||
for (let i = 0; i < that.bindingSignatures.length; i++) {
|
for (let i = 0; i < that.bindingSignatures.length; i++) {
|
||||||
if (that.bindingSignatures[i].issuerKeyID.equals(srcBindSig.issuerKeyID)) {
|
if (that.bindingSignatures[i].issuerKeyID.equals(srcBindSig.issuerKeyID)) {
|
||||||
if (srcBindSig.created > that.bindingSignatures[i].created) {
|
if (srcBindSig.created > that.bindingSignatures[i].created) {
|
||||||
|
@ -139,15 +141,15 @@ class SubKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
srcBindSig.verified || await srcBindSig.verify(primaryKey, enums.signature.subkeyBinding, dataToVerify, undefined, config);
|
await srcBindSig.verify(primaryKey, enums.signature.subkeyBinding, dataToVerify, date, undefined, config);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// revocation signatures
|
// revocation signatures
|
||||||
await helper.mergeSignatures(subKey, this, 'revocationSignatures', function(srcRevSig) {
|
await helper.mergeSignatures(subKey, this, 'revocationSignatures', date, function(srcRevSig) {
|
||||||
return helper.isDataRevoked(primaryKey, enums.signature.subkeyRevocation, dataToVerify, [srcRevSig], undefined, undefined, undefined, config);
|
return helper.isDataRevoked(primaryKey, enums.signature.subkeyRevocation, dataToVerify, [srcRevSig], undefined, undefined, date, config);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,13 +174,13 @@ class SubKey {
|
||||||
config = defaultConfig
|
config = defaultConfig
|
||||||
) {
|
) {
|
||||||
const dataToSign = { key: primaryKey, bind: this.keyPacket };
|
const dataToSign = { key: primaryKey, bind: this.keyPacket };
|
||||||
const subKey = new SubKey(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, null, primaryKey, {
|
||||||
signatureType: enums.signature.subkeyRevocation,
|
signatureType: enums.signature.subkeyRevocation,
|
||||||
reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag),
|
reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag),
|
||||||
reasonForRevocationString
|
reasonForRevocationString
|
||||||
}, date, undefined, undefined, config));
|
}, date, undefined, false, config));
|
||||||
await subKey.update(this, primaryKey);
|
await subKey.update(this);
|
||||||
return subKey;
|
return subKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,11 +38,12 @@ class User {
|
||||||
* @param {SecretKeyPacket|
|
* @param {SecretKeyPacket|
|
||||||
* PublicKeyPacket} primaryKey The primary key packet
|
* PublicKeyPacket} primaryKey The primary key packet
|
||||||
* @param {Array<Key>} privateKeys - Decrypted private keys for signing
|
* @param {Array<Key>} privateKeys - Decrypted private keys for signing
|
||||||
|
* @param {Date} date - Date to overwrite creation date of the signature
|
||||||
* @param {Object} config - Full configuration
|
* @param {Object} config - Full configuration
|
||||||
* @returns {Promise<Key>} New user with new certificate signatures.
|
* @returns {Promise<Key>} New user with new certificate signatures.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async sign(primaryKey, privateKeys, config) {
|
async sign(primaryKey, privateKeys, date, config) {
|
||||||
const dataToSign = {
|
const dataToSign = {
|
||||||
userID: this.userID,
|
userID: this.userID,
|
||||||
userAttribute: this.userAttribute,
|
userAttribute: this.userAttribute,
|
||||||
|
@ -56,14 +57,14 @@ class User {
|
||||||
if (privateKey.hasSameFingerprintAs(primaryKey)) {
|
if (privateKey.hasSameFingerprintAs(primaryKey)) {
|
||||||
throw new Error('Not implemented for self signing');
|
throw new Error('Not implemented for self signing');
|
||||||
}
|
}
|
||||||
const signingKey = await privateKey.getSigningKey(undefined, undefined, undefined, config);
|
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)
|
// 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]
|
||||||
}, undefined, undefined, undefined, config);
|
}, date, undefined, undefined, config);
|
||||||
}));
|
}));
|
||||||
await user.update(this, primaryKey);
|
await user.update(this, primaryKey, date, config);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,18 +115,15 @@ class User {
|
||||||
if (!key.getKeyIDs().some(id => id.equals(keyID))) {
|
if (!key.getKeyIDs().some(id => id.equals(keyID))) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const signingKey = await key.getSigningKey(keyID, date, undefined, config);
|
const signingKey = await key.getSigningKey(keyID, certificate.created, undefined, config);
|
||||||
if (certificate.revoked || await that.isRevoked(primaryKey, certificate, signingKey.keyPacket, date, config)) {
|
if (certificate.revoked || await that.isRevoked(primaryKey, certificate, signingKey.keyPacket, date, config)) {
|
||||||
throw new Error('User certificate is revoked');
|
throw new Error('User certificate is revoked');
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
certificate.verified || await certificate.verify(signingKey.keyPacket, enums.signature.certGeneric, dataToVerify, undefined, config);
|
await certificate.verify(signingKey.keyPacket, enums.signature.certGeneric, dataToVerify, date, undefined, config);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw util.wrapError('User certificate is invalid', e);
|
throw util.wrapError('User certificate is invalid', e);
|
||||||
}
|
}
|
||||||
if (certificate.isExpired(date)) {
|
|
||||||
throw new Error('User certificate is expired');
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}));
|
}));
|
||||||
return results.find(result => result !== null) || null;
|
return results.find(result => result !== null) || null;
|
||||||
|
@ -185,13 +183,10 @@ class User {
|
||||||
throw new Error('Self-certification is revoked');
|
throw new Error('Self-certification is revoked');
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
selfCertification.verified || await selfCertification.verify(primaryKey, enums.signature.certGeneric, dataToVerify, undefined, config);
|
await selfCertification.verify(primaryKey, enums.signature.certGeneric, dataToVerify, date, undefined, config);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw util.wrapError('Self-certification is invalid', e);
|
throw util.wrapError('Self-certification is invalid', e);
|
||||||
}
|
}
|
||||||
if (selfCertification.isExpired(date)) {
|
|
||||||
throw new Error('Self-certification is expired');
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
exception = e;
|
exception = e;
|
||||||
|
@ -205,30 +200,31 @@ class User {
|
||||||
* @param {User} user - Source user to merge
|
* @param {User} user - Source user to merge
|
||||||
* @param {SecretKeyPacket|
|
* @param {SecretKeyPacket|
|
||||||
* SecretSubkeyPacket} primaryKey primary key used for validation
|
* SecretSubkeyPacket} primaryKey primary key used for validation
|
||||||
|
* @param {Date} date - Date to verify the validity of signatures
|
||||||
* @param {Object} config - Full configuration
|
* @param {Object} config - Full configuration
|
||||||
* @returns {Promise<undefined>}
|
* @returns {Promise<undefined>}
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async update(user, primaryKey, config) {
|
async update(user, primaryKey, date, config) {
|
||||||
const dataToVerify = {
|
const dataToVerify = {
|
||||||
userID: this.userID,
|
userID: this.userID,
|
||||||
userAttribute: this.userAttribute,
|
userAttribute: this.userAttribute,
|
||||||
key: primaryKey
|
key: primaryKey
|
||||||
};
|
};
|
||||||
// self signatures
|
// self signatures
|
||||||
await mergeSignatures(user, this, 'selfCertifications', async function(srcSelfSig) {
|
await mergeSignatures(user, this, 'selfCertifications', date, async function(srcSelfSig) {
|
||||||
try {
|
try {
|
||||||
srcSelfSig.verified || await srcSelfSig.verify(primaryKey, enums.signature.certGeneric, dataToVerify, undefined, config);
|
await srcSelfSig.verify(primaryKey, enums.signature.certGeneric, dataToVerify, date, false, config);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// other signatures
|
// other signatures
|
||||||
await mergeSignatures(user, this, 'otherCertifications');
|
await mergeSignatures(user, this, 'otherCertifications', date);
|
||||||
// revocation signatures
|
// revocation signatures
|
||||||
await mergeSignatures(user, this, 'revocationSignatures', function(srcRevSig) {
|
await mergeSignatures(user, this, 'revocationSignatures', date, function(srcRevSig) {
|
||||||
return isDataRevoked(primaryKey, enums.signature.certRevocation, dataToVerify, [srcRevSig], undefined, undefined, undefined, config);
|
return isDataRevoked(primaryKey, enums.signature.certRevocation, dataToVerify, [srcRevSig], undefined, undefined, date, config);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,12 +107,13 @@ export class Message {
|
||||||
* @param {Array<PrivateKey>} [decryptionKeys] - Private keys with decrypted secret data
|
* @param {Array<PrivateKey>} [decryptionKeys] - Private keys with decrypted secret data
|
||||||
* @param {Array<String>} [passwords] - Passwords used to decrypt
|
* @param {Array<String>} [passwords] - Passwords used to decrypt
|
||||||
* @param {Array<Object>} [sessionKeys] - Session keys in the form: { data:Uint8Array, algorithm:String, [aeadAlgorithm:String] }
|
* @param {Array<Object>} [sessionKeys] - Session keys in the form: { data:Uint8Array, algorithm:String, [aeadAlgorithm:String] }
|
||||||
|
* @param {Date} [date] - Use the given date for key verification instead of the current time
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Message>} New message with decrypted content.
|
* @returns {Promise<Message>} New message with decrypted content.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async decrypt(decryptionKeys, passwords, sessionKeys, config = defaultConfig) {
|
async decrypt(decryptionKeys, passwords, sessionKeys, date = new Date(), config = defaultConfig) {
|
||||||
const keyObjs = sessionKeys || await this.decryptSessionKeys(decryptionKeys, passwords, config);
|
const keyObjs = sessionKeys || await this.decryptSessionKeys(decryptionKeys, passwords, date, config);
|
||||||
|
|
||||||
const symEncryptedPacketlist = this.packets.filterByTag(
|
const symEncryptedPacketlist = this.packets.filterByTag(
|
||||||
enums.packet.symmetricallyEncryptedData,
|
enums.packet.symmetricallyEncryptedData,
|
||||||
|
@ -157,6 +158,7 @@ export class Message {
|
||||||
* Decrypt encrypted session keys either with private keys or passwords.
|
* Decrypt encrypted session keys either with private keys or passwords.
|
||||||
* @param {Array<PrivateKey>} [decryptionKeys] - Private keys with decrypted secret data
|
* @param {Array<PrivateKey>} [decryptionKeys] - Private keys with decrypted secret data
|
||||||
* @param {Array<String>} [passwords] - Passwords used to decrypt
|
* @param {Array<String>} [passwords] - Passwords used to decrypt
|
||||||
|
* @param {Date} [date] - Use the given date for key verification, instead of current time
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<Array<{
|
* @returns {Promise<Array<{
|
||||||
* data: Uint8Array,
|
* data: Uint8Array,
|
||||||
|
@ -164,7 +166,7 @@ export class Message {
|
||||||
* }>>} array of object with potential sessionKey, algorithm pairs
|
* }>>} array of object with potential sessionKey, algorithm pairs
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async decryptSessionKeys(decryptionKeys, passwords, config = defaultConfig) {
|
async decryptSessionKeys(decryptionKeys, passwords, date = new Date(), config = defaultConfig) {
|
||||||
let keyPackets = [];
|
let keyPackets = [];
|
||||||
|
|
||||||
let exception;
|
let exception;
|
||||||
|
@ -203,7 +205,7 @@ export class Message {
|
||||||
enums.symmetric.cast5 // Golang OpenPGP fallback
|
enums.symmetric.cast5 // Golang OpenPGP fallback
|
||||||
];
|
];
|
||||||
try {
|
try {
|
||||||
const primaryUser = await decryptionKey.getPrimaryUser(undefined, undefined, config); // TODO: Pass userID from somewhere.
|
const primaryUser = await decryptionKey.getPrimaryUser(date, undefined, config); // TODO: Pass userID from somewhere.
|
||||||
if (primaryUser.selfCertification.preferredSymmetricAlgorithms) {
|
if (primaryUser.selfCertification.preferredSymmetricAlgorithms) {
|
||||||
algos = algos.concat(primaryUser.selfCertification.preferredSymmetricAlgorithms);
|
algos = algos.concat(primaryUser.selfCertification.preferredSymmetricAlgorithms);
|
||||||
}
|
}
|
||||||
|
@ -697,8 +699,7 @@ export async function createSignaturePackets(literalDataPacket, signingKeys, sig
|
||||||
* @param {SignaturePacket} signature - Signature packet
|
* @param {SignaturePacket} signature - Signature packet
|
||||||
* @param {Array<LiteralDataPacket>} literalDataList - Array of literal data packets
|
* @param {Array<LiteralDataPacket>} literalDataList - Array of literal data packets
|
||||||
* @param {Array<PublicKey>} verificationKeys - Array of public keys to verify signatures
|
* @param {Array<PublicKey>} verificationKeys - Array of public keys to verify signatures
|
||||||
* @param {Date} date - Verify the signature against the given date,
|
* @param {Date} [date] - Check signature validity with respect to the given date
|
||||||
* i.e. check signature creation time < date < expiration time
|
|
||||||
* @param {Boolean} [detached] - Whether to verify detached signature packets
|
* @param {Boolean} [detached] - Whether to verify detached signature packets
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<{
|
* @returns {Promise<{
|
||||||
|
@ -711,49 +712,41 @@ export async function createSignaturePackets(literalDataPacket, signingKeys, sig
|
||||||
*/
|
*/
|
||||||
async function createVerificationObject(signature, literalDataList, verificationKeys, date = new Date(), detached = false, config = defaultConfig) {
|
async function createVerificationObject(signature, literalDataList, verificationKeys, date = new Date(), detached = false, config = defaultConfig) {
|
||||||
let primaryKey;
|
let primaryKey;
|
||||||
let signingKey;
|
let unverifiedSigningKey;
|
||||||
let keyError;
|
|
||||||
|
|
||||||
for (const key of verificationKeys) {
|
for (const key of verificationKeys) {
|
||||||
const issuerKeys = key.getKeys(signature.issuerKeyID);
|
const issuerKeys = key.getKeys(signature.issuerKeyID);
|
||||||
if (issuerKeys.length > 0) {
|
if (issuerKeys.length > 0) {
|
||||||
primaryKey = key;
|
primaryKey = key;
|
||||||
|
unverifiedSigningKey = issuerKeys[0];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!primaryKey) {
|
|
||||||
keyError = new Error(`Could not find signing key with key ID ${signature.issuerKeyID.toHex()}`);
|
const isOnePassSignature = signature instanceof OnePassSignaturePacket;
|
||||||
} else {
|
const signaturePacketPromise = isOnePassSignature ? signature.correspondingSig : signature;
|
||||||
try {
|
|
||||||
signingKey = await primaryKey.getSigningKey(signature.issuerKeyID, null, undefined, config);
|
|
||||||
} catch (e) {
|
|
||||||
keyError = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const signaturePacket = signature.correspondingSig || signature;
|
|
||||||
const verifiedSig = {
|
const verifiedSig = {
|
||||||
keyID: signature.issuerKeyID,
|
keyID: signature.issuerKeyID,
|
||||||
verified: (async () => {
|
verified: (async () => {
|
||||||
if (keyError) {
|
if (!unverifiedSigningKey) {
|
||||||
throw keyError;
|
throw new Error(`Could not find signing key with key ID ${signature.issuerKeyID.toHex()}`);
|
||||||
}
|
}
|
||||||
await signature.verify(signingKey.keyPacket, signature.signatureType, literalDataList[0], detached, config);
|
|
||||||
const sig = await signaturePacket;
|
await signature.verify(unverifiedSigningKey.keyPacket, signature.signatureType, literalDataList[0], date, detached, config);
|
||||||
if (sig.isExpired(date) || !(
|
const signaturePacket = await signaturePacketPromise;
|
||||||
sig.created >= signingKey.getCreationTime() &&
|
if (unverifiedSigningKey.getCreationTime() > signaturePacket.created) {
|
||||||
sig.created < await (signingKey === primaryKey ?
|
throw new Error('Key is newer than the signature');
|
||||||
signingKey.getExpirationTime(undefined, undefined, undefined, config) :
|
|
||||||
signingKey.getExpirationTime(primaryKey, date, undefined, config)
|
|
||||||
)
|
|
||||||
)) {
|
|
||||||
throw new Error('Signature is expired');
|
|
||||||
}
|
}
|
||||||
|
// We pass the signature creation time to check whether the key was expired at the time of signing.
|
||||||
|
// We check this after signature verification because for streamed one-pass signatures, the creation time is not available before
|
||||||
|
await primaryKey.getSigningKey(unverifiedSigningKey.getKeyID(), signaturePacket.created, undefined, config);
|
||||||
return true;
|
return true;
|
||||||
})(),
|
})(),
|
||||||
signature: (async () => {
|
signature: (async () => {
|
||||||
const sig = await signaturePacket;
|
const signaturePacket = await signaturePacketPromise;
|
||||||
const packetlist = new PacketList();
|
const packetlist = new PacketList();
|
||||||
sig && packetlist.push(sig);
|
signaturePacket && packetlist.push(signaturePacket);
|
||||||
return new Signature(packetlist);
|
return new Signature(packetlist);
|
||||||
})()
|
})()
|
||||||
};
|
};
|
||||||
|
|
|
@ -93,6 +93,9 @@ export function generateKey({ userIDs = [], passphrase = "", type = "ecc", rsaBi
|
||||||
export function reformatKey({ privateKey, userIDs = [], passphrase = "", keyExpirationTime = 0, date, config }) {
|
export function reformatKey({ privateKey, userIDs = [], passphrase = "", keyExpirationTime = 0, date, config }) {
|
||||||
config = { ...defaultConfig, ...config };
|
config = { ...defaultConfig, ...config };
|
||||||
userIDs = toArray(userIDs);
|
userIDs = toArray(userIDs);
|
||||||
|
if (userIDs.length === 0) {
|
||||||
|
throw new Error('UserIDs are required for key reformat');
|
||||||
|
}
|
||||||
const options = { privateKey, userIDs, passphrase, keyExpirationTime, date };
|
const options = { privateKey, userIDs, passphrase, keyExpirationTime, date };
|
||||||
|
|
||||||
return reformat(options, config).then(async key => {
|
return reformat(options, config).then(async key => {
|
||||||
|
@ -119,6 +122,7 @@ export function reformatKey({ privateKey, userIDs = [], passphrase = "", keyExpi
|
||||||
* @param {Object} [options.reasonForRevocation] - Object indicating the reason for revocation
|
* @param {Object} [options.reasonForRevocation] - Object indicating the reason for revocation
|
||||||
* @param {module:enums.reasonForRevocation} [options.reasonForRevocation.flag=[noReason]{@link module:enums.reasonForRevocation}] - Flag indicating the reason for revocation
|
* @param {module:enums.reasonForRevocation} [options.reasonForRevocation.flag=[noReason]{@link module:enums.reasonForRevocation}] - Flag indicating the reason for revocation
|
||||||
* @param {String} [options.reasonForRevocation.string=""] - String explaining the reason for revocation
|
* @param {String} [options.reasonForRevocation.string=""] - String explaining the reason for revocation
|
||||||
|
* @param {Date} [options.date] - Use the given date instead of the current time to verify validity of revocation certificate (if provided), or as creation time of the revocation signature
|
||||||
* @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<Object>} The revoked key object in the form:
|
* @returns {Promise<Object>} The revoked key object in the form:
|
||||||
* `{ privateKey:PrivateKey, privateKeyArmored:String, publicKey:PublicKey, publicKeyArmored:String }`
|
* `{ privateKey:PrivateKey, privateKeyArmored:String, publicKey:PublicKey, publicKeyArmored:String }`
|
||||||
|
@ -126,13 +130,13 @@ export function reformatKey({ privateKey, userIDs = [], passphrase = "", keyExpi
|
||||||
* @async
|
* @async
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
export function revokeKey({ key, revocationCertificate, reasonForRevocation, config }) {
|
export function revokeKey({ key, revocationCertificate, reasonForRevocation, date = new Date(), config }) {
|
||||||
config = { ...defaultConfig, ...config };
|
config = { ...defaultConfig, ...config };
|
||||||
return Promise.resolve().then(() => {
|
return Promise.resolve().then(() => {
|
||||||
if (revocationCertificate) {
|
if (revocationCertificate) {
|
||||||
return key.applyRevocationCertificate(revocationCertificate, config);
|
return key.applyRevocationCertificate(revocationCertificate, date, config);
|
||||||
} else {
|
} else {
|
||||||
return key.revoke(reasonForRevocation, undefined, config);
|
return key.revoke(reasonForRevocation, date, config);
|
||||||
}
|
}
|
||||||
}).then(async key => {
|
}).then(async key => {
|
||||||
if (key.isPrivate()) {
|
if (key.isPrivate()) {
|
||||||
|
@ -309,7 +313,7 @@ export function decrypt({ message, decryptionKeys, passwords, sessionKeys, verif
|
||||||
config = { ...defaultConfig, ...config };
|
config = { ...defaultConfig, ...config };
|
||||||
checkMessage(message); verificationKeys = toArray(verificationKeys); decryptionKeys = toArray(decryptionKeys); passwords = toArray(passwords); sessionKeys = toArray(sessionKeys);
|
checkMessage(message); verificationKeys = toArray(verificationKeys); decryptionKeys = toArray(decryptionKeys); passwords = toArray(passwords); sessionKeys = toArray(sessionKeys);
|
||||||
|
|
||||||
return message.decrypt(decryptionKeys, passwords, sessionKeys, config).then(async function(decrypted) {
|
return message.decrypt(decryptionKeys, passwords, sessionKeys, date, config).then(async function(decrypted) {
|
||||||
if (!verificationKeys) {
|
if (!verificationKeys) {
|
||||||
verificationKeys = [];
|
verificationKeys = [];
|
||||||
}
|
}
|
||||||
|
@ -515,6 +519,7 @@ export function encryptSessionKey({ data, algorithm, aeadAlgorithm, encryptionKe
|
||||||
* @param {Message} options.message - A message object containing the encrypted session key packets
|
* @param {Message} options.message - A message object containing the encrypted session key packets
|
||||||
* @param {PrivateKey|PrivateKey[]} [options.decryptionKeys] - Private keys with decrypted secret key data
|
* @param {PrivateKey|PrivateKey[]} [options.decryptionKeys] - Private keys with decrypted secret key data
|
||||||
* @param {String|String[]} [options.passwords] - Passwords to decrypt the session key
|
* @param {String|String[]} [options.passwords] - Passwords to decrypt the session key
|
||||||
|
* @param {Date} [options.date] - Date to use for key verification instead of the current time
|
||||||
* @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<Object>} Array of decrypted session key, algorithm pairs in the form:
|
* @returns {Promise<Object>} Array of decrypted session key, algorithm pairs in the form:
|
||||||
* { data:Uint8Array, algorithm:String }
|
* { data:Uint8Array, algorithm:String }
|
||||||
|
@ -522,13 +527,13 @@ export function encryptSessionKey({ data, algorithm, aeadAlgorithm, encryptionKe
|
||||||
* @async
|
* @async
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
export function decryptSessionKeys({ message, decryptionKeys, passwords, config }) {
|
export function decryptSessionKeys({ message, decryptionKeys, passwords, date = new Date(), config }) {
|
||||||
config = { ...defaultConfig, ...config };
|
config = { ...defaultConfig, ...config };
|
||||||
checkMessage(message); decryptionKeys = toArray(decryptionKeys); passwords = toArray(passwords);
|
checkMessage(message); decryptionKeys = toArray(decryptionKeys); passwords = toArray(passwords);
|
||||||
|
|
||||||
return Promise.resolve().then(async function() {
|
return Promise.resolve().then(async function() {
|
||||||
|
|
||||||
return message.decryptSessionKeys(decryptionKeys, passwords, config);
|
return message.decryptSessionKeys(decryptionKeys, passwords, date, config);
|
||||||
|
|
||||||
}).catch(onError.bind(null, 'Error decrypting session keys'));
|
}).catch(onError.bind(null, 'Error decrypting session keys'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,18 @@ import enums from '../enums';
|
||||||
import util from '../util';
|
import util from '../util';
|
||||||
import defaultConfig from '../config';
|
import defaultConfig from '../config';
|
||||||
|
|
||||||
|
// Symbol to store cryptographic validity of the signature, to avoid recomputing multiple times on verification.
|
||||||
|
const verified = Symbol('verified');
|
||||||
|
|
||||||
|
// GPG puts the Issuer and Signature subpackets in the unhashed area.
|
||||||
|
// Tampering with those invalidates the signature, so we still trust them and parse them.
|
||||||
|
// All other unhashed subpackets are ignored.
|
||||||
|
const allowedUnhashedSubpackets = new Set([
|
||||||
|
enums.signatureSubpacket.issuer,
|
||||||
|
enums.signatureSubpacket.issuerFingerprint,
|
||||||
|
enums.signatureSubpacket.embeddedSignature
|
||||||
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the Signature Packet (Tag 2)
|
* Implementation of the Signature Packet (Tag 2)
|
||||||
*
|
*
|
||||||
|
@ -36,11 +48,8 @@ class SignaturePacket {
|
||||||
return enums.packet.signature;
|
return enums.packet.signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
constructor() {
|
||||||
* @param {Date} date - The creation date of the signature
|
this.version = null;
|
||||||
*/
|
|
||||||
constructor(date = new Date()) {
|
|
||||||
this.version = 4; // This is set to 5 below if we sign with a V5 key.
|
|
||||||
this.signatureType = null;
|
this.signatureType = null;
|
||||||
this.hashAlgorithm = null;
|
this.hashAlgorithm = null;
|
||||||
this.publicKeyAlgorithm = null;
|
this.publicKeyAlgorithm = null;
|
||||||
|
@ -49,7 +58,7 @@ class SignaturePacket {
|
||||||
this.unhashedSubpackets = [];
|
this.unhashedSubpackets = [];
|
||||||
this.signedHashValue = null;
|
this.signedHashValue = null;
|
||||||
|
|
||||||
this.created = util.normalizeDate(date);
|
this.created = null;
|
||||||
this.signatureExpirationTime = null;
|
this.signatureExpirationTime = null;
|
||||||
this.signatureNeverExpires = true;
|
this.signatureNeverExpires = true;
|
||||||
this.exportable = null;
|
this.exportable = null;
|
||||||
|
@ -85,8 +94,8 @@ class SignaturePacket {
|
||||||
this.issuerFingerprint = null;
|
this.issuerFingerprint = null;
|
||||||
this.preferredAEADAlgorithms = null;
|
this.preferredAEADAlgorithms = null;
|
||||||
|
|
||||||
this.verified = null;
|
|
||||||
this.revoked = null;
|
this.revoked = null;
|
||||||
|
this[verified] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,6 +117,9 @@ class SignaturePacket {
|
||||||
|
|
||||||
// hashed subpackets
|
// hashed subpackets
|
||||||
i += this.readSubPackets(bytes.subarray(i, bytes.length), true);
|
i += this.readSubPackets(bytes.subarray(i, bytes.length), true);
|
||||||
|
if (!this.created) {
|
||||||
|
throw new Error('Missing signature creation time subpacket.');
|
||||||
|
}
|
||||||
|
|
||||||
// A V4 signature hashes the packet body
|
// A V4 signature hashes the packet body
|
||||||
// starting from its first field, the version number, through the end
|
// starting from its first field, the version number, through the end
|
||||||
|
@ -152,20 +164,24 @@ class SignaturePacket {
|
||||||
* Signs provided data. This needs to be done prior to serialization.
|
* Signs provided data. This needs to be done prior to serialization.
|
||||||
* @param {SecretKeyPacket} key - Private key used to sign the message.
|
* @param {SecretKeyPacket} key - Private key used to sign the message.
|
||||||
* @param {Object} data - Contains packets to be signed.
|
* @param {Object} data - Contains packets to be signed.
|
||||||
|
* @param {Date} [date] - The signature creation time.
|
||||||
* @param {Boolean} [detached] - Whether to create a detached signature
|
* @param {Boolean} [detached] - Whether to create a detached signature
|
||||||
* @throws {Error} if signing failed
|
* @throws {Error} if signing failed
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async sign(key, data, detached = false) {
|
async sign(key, data, date = new Date(), detached = false) {
|
||||||
const signatureType = enums.write(enums.signature, this.signatureType);
|
const signatureType = enums.write(enums.signature, this.signatureType);
|
||||||
const publicKeyAlgorithm = enums.write(enums.publicKey, this.publicKeyAlgorithm);
|
const publicKeyAlgorithm = enums.write(enums.publicKey, this.publicKeyAlgorithm);
|
||||||
const hashAlgorithm = enums.write(enums.hash, this.hashAlgorithm);
|
const hashAlgorithm = enums.write(enums.hash, this.hashAlgorithm);
|
||||||
|
|
||||||
if (key.version === 5) {
|
if (key.version === 5) {
|
||||||
this.version = 5;
|
this.version = 5;
|
||||||
|
} else {
|
||||||
|
this.version = 4;
|
||||||
}
|
}
|
||||||
const arr = [new Uint8Array([this.version, signatureType, publicKeyAlgorithm, hashAlgorithm])];
|
const arr = [new Uint8Array([this.version, signatureType, publicKeyAlgorithm, hashAlgorithm])];
|
||||||
|
|
||||||
|
this.created = util.normalizeDate(date);
|
||||||
this.issuerKeyVersion = key.version;
|
this.issuerKeyVersion = key.version;
|
||||||
this.issuerFingerprint = key.getFingerprintBytes();
|
this.issuerFingerprint = key.getFingerprintBytes();
|
||||||
this.issuerKeyID = key.getKeyID();
|
this.issuerKeyID = key.getKeyID();
|
||||||
|
@ -191,7 +207,7 @@ class SignaturePacket {
|
||||||
// getLatestValidSignature(this.revocationSignatures, key, data)` later.
|
// getLatestValidSignature(this.revocationSignatures, key, data)` later.
|
||||||
// Note that this only holds up if the key and data passed to verify are the
|
// Note that this only holds up if the key and data passed to verify are the
|
||||||
// same as the ones passed to sign.
|
// same as the ones passed to sign.
|
||||||
this.verified = true;
|
this[verified] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,9 +219,10 @@ class SignaturePacket {
|
||||||
const sub = enums.signatureSubpacket;
|
const sub = enums.signatureSubpacket;
|
||||||
const arr = [];
|
const arr = [];
|
||||||
let bytes;
|
let bytes;
|
||||||
if (this.created !== null) {
|
if (this.created === null) {
|
||||||
arr.push(writeSubPacket(sub.signatureCreationTime, util.writeDate(this.created)));
|
throw new Error('Missing signature creation time');
|
||||||
}
|
}
|
||||||
|
arr.push(writeSubPacket(sub.signatureCreationTime, util.writeDate(this.created)));
|
||||||
if (this.signatureExpirationTime !== null) {
|
if (this.signatureExpirationTime !== null) {
|
||||||
arr.push(writeSubPacket(sub.signatureExpirationTime, util.writeNumber(this.signatureExpirationTime, 4)));
|
arr.push(writeSubPacket(sub.signatureExpirationTime, util.writeNumber(this.signatureExpirationTime, 4)));
|
||||||
}
|
}
|
||||||
|
@ -331,30 +348,14 @@ class SignaturePacket {
|
||||||
}
|
}
|
||||||
|
|
||||||
// V4 signature sub packets
|
// V4 signature sub packets
|
||||||
|
readSubPacket(bytes, hashed = true) {
|
||||||
readSubPacket(bytes, trusted = true) {
|
|
||||||
let mypos = 0;
|
let mypos = 0;
|
||||||
|
|
||||||
const readArray = (prop, bytes) => {
|
|
||||||
this[prop] = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < bytes.length; i++) {
|
|
||||||
this[prop].push(bytes[i]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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;
|
||||||
|
|
||||||
// GPG puts the Issuer and Signature subpackets in the unhashed area.
|
if (!hashed && !allowedUnhashedSubpackets.has(type)) {
|
||||||
// Tampering with those invalidates the signature, so we can trust them.
|
|
||||||
// Ignore all other unhashed subpackets.
|
|
||||||
if (!trusted && ![
|
|
||||||
enums.signatureSubpacket.issuer,
|
|
||||||
enums.signatureSubpacket.issuerFingerprint,
|
|
||||||
enums.signatureSubpacket.embeddedSignature
|
|
||||||
].includes(type)) {
|
|
||||||
this.unhashedSubpackets.push(bytes.subarray(mypos, bytes.length));
|
this.unhashedSubpackets.push(bytes.subarray(mypos, bytes.length));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -363,11 +364,11 @@ class SignaturePacket {
|
||||||
|
|
||||||
// subpacket type
|
// subpacket type
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 2:
|
case enums.signatureSubpacket.signatureCreationTime:
|
||||||
// Signature Creation Time
|
// Signature Creation Time
|
||||||
this.created = util.readDate(bytes.subarray(mypos, bytes.length));
|
this.created = util.readDate(bytes.subarray(mypos, bytes.length));
|
||||||
break;
|
break;
|
||||||
case 3: {
|
case enums.signatureSubpacket.signatureExpirationTime: {
|
||||||
// Signature Expiration Time in seconds
|
// Signature Expiration Time in seconds
|
||||||
const seconds = util.readNumber(bytes.subarray(mypos, bytes.length));
|
const seconds = util.readNumber(bytes.subarray(mypos, bytes.length));
|
||||||
|
|
||||||
|
@ -376,24 +377,24 @@ class SignaturePacket {
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 4:
|
case enums.signatureSubpacket.exportableCertification:
|
||||||
// Exportable Certification
|
// Exportable Certification
|
||||||
this.exportable = bytes[mypos++] === 1;
|
this.exportable = bytes[mypos++] === 1;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case enums.signatureSubpacket.trustSignature:
|
||||||
// Trust Signature
|
// Trust Signature
|
||||||
this.trustLevel = bytes[mypos++];
|
this.trustLevel = bytes[mypos++];
|
||||||
this.trustAmount = bytes[mypos++];
|
this.trustAmount = bytes[mypos++];
|
||||||
break;
|
break;
|
||||||
case 6:
|
case enums.signatureSubpacket.regularExpression:
|
||||||
// Regular Expression
|
// Regular Expression
|
||||||
this.regularExpression = bytes[mypos];
|
this.regularExpression = bytes[mypos];
|
||||||
break;
|
break;
|
||||||
case 7:
|
case enums.signatureSubpacket.revocable:
|
||||||
// Revocable
|
// Revocable
|
||||||
this.revocable = bytes[mypos++] === 1;
|
this.revocable = bytes[mypos++] === 1;
|
||||||
break;
|
break;
|
||||||
case 9: {
|
case enums.signatureSubpacket.keyExpirationTime: {
|
||||||
// Key Expiration Time in seconds
|
// Key Expiration Time in seconds
|
||||||
const seconds = util.readNumber(bytes.subarray(mypos, bytes.length));
|
const seconds = util.readNumber(bytes.subarray(mypos, bytes.length));
|
||||||
|
|
||||||
|
@ -402,11 +403,11 @@ class SignaturePacket {
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 11:
|
case enums.signatureSubpacket.preferredSymmetricAlgorithms:
|
||||||
// Preferred Symmetric Algorithms
|
// Preferred Symmetric Algorithms
|
||||||
readArray('preferredSymmetricAlgorithms', bytes.subarray(mypos, bytes.length));
|
this.preferredSymmetricAlgorithms = [...bytes.subarray(mypos, bytes.length)];
|
||||||
break;
|
break;
|
||||||
case 12:
|
case enums.signatureSubpacket.revocationKey:
|
||||||
// Revocation Key
|
// Revocation Key
|
||||||
// (1 octet of class, 1 octet of public-key algorithm ID, 20
|
// (1 octet of class, 1 octet of public-key algorithm ID, 20
|
||||||
// octets of
|
// octets of
|
||||||
|
@ -416,12 +417,12 @@ class SignaturePacket {
|
||||||
this.revocationKeyFingerprint = bytes.subarray(mypos, mypos + 20);
|
this.revocationKeyFingerprint = bytes.subarray(mypos, mypos + 20);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 16:
|
case enums.signatureSubpacket.issuer:
|
||||||
// Issuer
|
// Issuer
|
||||||
this.issuerKeyID.read(bytes.subarray(mypos, bytes.length));
|
this.issuerKeyID.read(bytes.subarray(mypos, bytes.length));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 20: {
|
case enums.signatureSubpacket.notationData: {
|
||||||
// Notation Data
|
// Notation Data
|
||||||
const humanReadable = !!(bytes[mypos] & 0x80);
|
const humanReadable = !!(bytes[mypos] & 0x80);
|
||||||
|
|
||||||
|
@ -442,48 +443,48 @@ class SignaturePacket {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 21:
|
case enums.signatureSubpacket.preferredHashAlgorithms:
|
||||||
// Preferred Hash Algorithms
|
// Preferred Hash Algorithms
|
||||||
readArray('preferredHashAlgorithms', bytes.subarray(mypos, bytes.length));
|
this.preferredHashAlgorithms = [...bytes.subarray(mypos, bytes.length)];
|
||||||
break;
|
break;
|
||||||
case 22:
|
case enums.signatureSubpacket.preferredCompressionAlgorithms:
|
||||||
// Preferred Compression Algorithms
|
// Preferred Compression Algorithms
|
||||||
readArray('preferredCompressionAlgorithms', bytes.subarray(mypos, bytes.length));
|
this.preferredCompressionAlgorithms = [...bytes.subarray(mypos, bytes.length)];
|
||||||
break;
|
break;
|
||||||
case 23:
|
case enums.signatureSubpacket.keyServerPreferences:
|
||||||
// Key Server Preferences
|
// Key Server Preferences
|
||||||
readArray('keyServerPreferences', bytes.subarray(mypos, bytes.length));
|
this.keyServerPreferences = [...bytes.subarray(mypos, bytes.length)];
|
||||||
break;
|
break;
|
||||||
case 24:
|
case enums.signatureSubpacket.preferredKeyServer:
|
||||||
// Preferred Key Server
|
// Preferred Key Server
|
||||||
this.preferredKeyServer = util.uint8ArrayToString(bytes.subarray(mypos, bytes.length));
|
this.preferredKeyServer = util.uint8ArrayToString(bytes.subarray(mypos, bytes.length));
|
||||||
break;
|
break;
|
||||||
case 25:
|
case enums.signatureSubpacket.primaryUserID:
|
||||||
// Primary User ID
|
// Primary User ID
|
||||||
this.isPrimaryUserID = bytes[mypos++] !== 0;
|
this.isPrimaryUserID = bytes[mypos++] !== 0;
|
||||||
break;
|
break;
|
||||||
case 26:
|
case enums.signatureSubpacket.policyURI:
|
||||||
// Policy URI
|
// Policy URI
|
||||||
this.policyURI = util.uint8ArrayToString(bytes.subarray(mypos, bytes.length));
|
this.policyURI = util.uint8ArrayToString(bytes.subarray(mypos, bytes.length));
|
||||||
break;
|
break;
|
||||||
case 27:
|
case enums.signatureSubpacket.keyFlags:
|
||||||
// Key Flags
|
// Key Flags
|
||||||
readArray('keyFlags', bytes.subarray(mypos, bytes.length));
|
this.keyFlags = [...bytes.subarray(mypos, bytes.length)];
|
||||||
break;
|
break;
|
||||||
case 28:
|
case enums.signatureSubpacket.signersUserID:
|
||||||
// Signer's User ID
|
// Signer's User ID
|
||||||
this.signersUserID = util.uint8ArrayToString(bytes.subarray(mypos, bytes.length));
|
this.signersUserID = util.uint8ArrayToString(bytes.subarray(mypos, bytes.length));
|
||||||
break;
|
break;
|
||||||
case 29:
|
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.uint8ArrayToString(bytes.subarray(mypos, bytes.length));
|
||||||
break;
|
break;
|
||||||
case 30:
|
case enums.signatureSubpacket.features:
|
||||||
// Features
|
// Features
|
||||||
readArray('features', bytes.subarray(mypos, bytes.length));
|
this.features = [...bytes.subarray(mypos, bytes.length)];
|
||||||
break;
|
break;
|
||||||
case 31: {
|
case enums.signatureSubpacket.signatureTarget: {
|
||||||
// Signature Target
|
// Signature Target
|
||||||
// (1 octet public-key algorithm, 1 octet hash algorithm, N octets hash)
|
// (1 octet public-key algorithm, 1 octet hash algorithm, N octets hash)
|
||||||
this.signatureTargetPublicKeyAlgorithm = bytes[mypos++];
|
this.signatureTargetPublicKeyAlgorithm = bytes[mypos++];
|
||||||
|
@ -494,12 +495,12 @@ class SignaturePacket {
|
||||||
this.signatureTargetHash = util.uint8ArrayToString(bytes.subarray(mypos, mypos + len));
|
this.signatureTargetHash = util.uint8ArrayToString(bytes.subarray(mypos, mypos + len));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 32:
|
case enums.signatureSubpacket.embeddedSignature:
|
||||||
// Embedded Signature
|
// Embedded Signature
|
||||||
this.embeddedSignature = new SignaturePacket();
|
this.embeddedSignature = new SignaturePacket();
|
||||||
this.embeddedSignature.read(bytes.subarray(mypos, bytes.length));
|
this.embeddedSignature.read(bytes.subarray(mypos, bytes.length));
|
||||||
break;
|
break;
|
||||||
case 33:
|
case enums.signatureSubpacket.issuerFingerprint:
|
||||||
// Issuer Fingerprint
|
// Issuer Fingerprint
|
||||||
this.issuerKeyVersion = bytes[mypos++];
|
this.issuerKeyVersion = bytes[mypos++];
|
||||||
this.issuerFingerprint = bytes.subarray(mypos, bytes.length);
|
this.issuerFingerprint = bytes.subarray(mypos, bytes.length);
|
||||||
|
@ -509,12 +510,12 @@ class SignaturePacket {
|
||||||
this.issuerKeyID.read(this.issuerFingerprint.subarray(-8));
|
this.issuerKeyID.read(this.issuerFingerprint.subarray(-8));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 34:
|
case enums.signatureSubpacket.preferredAEADAlgorithms:
|
||||||
// Preferred AEAD Algorithms
|
// Preferred AEAD Algorithms
|
||||||
readArray.call(this, 'preferredAEADAlgorithms', bytes.subarray(mypos, bytes.length));
|
this.preferredAEADAlgorithms = [...bytes.subarray(mypos, bytes.length)];
|
||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
const err = new Error("Unknown signature subpacket type " + type + " @:" + mypos);
|
const err = new Error(`Unknown signature subpacket type ${type}`);
|
||||||
if (critical) {
|
if (critical) {
|
||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
|
@ -654,41 +655,59 @@ class SignaturePacket {
|
||||||
* SecretSubkeyPacket|SecretKeyPacket} key - the public key to verify the signature
|
* SecretSubkeyPacket|SecretKeyPacket} key - the public key to verify the signature
|
||||||
* @param {module:enums.signature} signatureType - Expected signature type
|
* @param {module:enums.signature} signatureType - Expected signature type
|
||||||
* @param {String|Object} data - Data which on the signature applies
|
* @param {String|Object} data - Data which on the signature applies
|
||||||
|
* @param {Date} [date] - Use the given date instead of the current time to check for signature validity and expiration
|
||||||
* @param {Boolean} [detached] - Whether to verify a detached signature
|
* @param {Boolean} [detached] - Whether to verify a detached signature
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @throws {Error} if signature validation failed
|
* @throws {Error} if signature validation failed
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async verify(key, signatureType, data, detached = false, config = defaultConfig) {
|
async verify(key, signatureType, data, date = new Date(), detached = false, config = defaultConfig) {
|
||||||
const publicKeyAlgorithm = enums.write(enums.publicKey, this.publicKeyAlgorithm);
|
const publicKeyAlgorithm = enums.write(enums.publicKey, this.publicKeyAlgorithm);
|
||||||
const hashAlgorithm = enums.write(enums.hash, this.hashAlgorithm);
|
const hashAlgorithm = enums.write(enums.hash, this.hashAlgorithm);
|
||||||
|
if (!this.issuerKeyID.equals(key.getKeyID())) {
|
||||||
|
throw new Error('Signature was not issued by the given public key');
|
||||||
|
}
|
||||||
if (publicKeyAlgorithm !== enums.write(enums.publicKey, key.algorithm)) {
|
if (publicKeyAlgorithm !== enums.write(enums.publicKey, key.algorithm)) {
|
||||||
throw new Error('Public key algorithm used to sign signature does not match issuer key algorithm.');
|
throw new Error('Public key algorithm used to sign signature does not match issuer key algorithm.');
|
||||||
}
|
}
|
||||||
|
|
||||||
let toHash;
|
const isMessageSignature = signatureType === enums.signature.binary || signatureType === enums.signature.text;
|
||||||
let hash;
|
// Cryptographic validity is cached after one successful verification.
|
||||||
if (this.hashed) {
|
// However, for message signatures, we always re-verify, since the passed `data` can change
|
||||||
hash = await this.hashed;
|
const skipVerify = this[verified] && !isMessageSignature;
|
||||||
} else {
|
if (!skipVerify) {
|
||||||
toHash = this.toHash(signatureType, data, detached);
|
let toHash;
|
||||||
hash = await this.hash(signatureType, data, toHash);
|
let hash;
|
||||||
}
|
if (this.hashed) {
|
||||||
hash = await stream.readToEnd(hash);
|
hash = await this.hashed;
|
||||||
if (this.signedHashValue[0] !== hash[0] ||
|
} else {
|
||||||
this.signedHashValue[1] !== hash[1]) {
|
toHash = this.toHash(signatureType, data, detached);
|
||||||
throw new Error('Message digest did not match');
|
hash = await this.hash(signatureType, data, toHash);
|
||||||
|
}
|
||||||
|
hash = await stream.readToEnd(hash);
|
||||||
|
if (this.signedHashValue[0] !== hash[0] ||
|
||||||
|
this.signedHashValue[1] !== hash[1]) {
|
||||||
|
throw new Error('Signed digest did not match');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.params = await this.params;
|
||||||
|
|
||||||
|
this[verified] = await crypto.signature.verify(
|
||||||
|
publicKeyAlgorithm, hashAlgorithm, this.params, key.publicParams,
|
||||||
|
toHash, hash
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!this[verified]) {
|
||||||
|
throw new Error('Signature verification failed');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.params = await this.params;
|
const normDate = util.normalizeDate(date);
|
||||||
|
if (normDate && this.created > normDate) {
|
||||||
const verified = await crypto.signature.verify(
|
throw new Error('Signature creation time is in the future');
|
||||||
publicKeyAlgorithm, hashAlgorithm, this.params, key.publicParams,
|
}
|
||||||
toHash, hash
|
if (normDate && normDate > this.getExpirationTime()) {
|
||||||
);
|
throw new Error('Signature is expired');
|
||||||
if (!verified) {
|
|
||||||
throw new Error('Signature verification failed');
|
|
||||||
}
|
}
|
||||||
if (config.rejectHashAlgorithms.has(hashAlgorithm)) {
|
if (config.rejectHashAlgorithms.has(hashAlgorithm)) {
|
||||||
throw new Error('Insecure hash algorithm: ' + enums.read(enums.hash, hashAlgorithm).toUpperCase());
|
throw new Error('Insecure hash algorithm: ' + enums.read(enums.hash, hashAlgorithm).toUpperCase());
|
||||||
|
@ -705,7 +724,6 @@ class SignaturePacket {
|
||||||
if (this.revocationKeyClass !== null) {
|
if (this.revocationKeyClass !== null) {
|
||||||
throw new Error('This key is intended to be revoked with an authorized key, which OpenPGP.js does not support.');
|
throw new Error('This key is intended to be revoked with an authorized key, which OpenPGP.js does not support.');
|
||||||
}
|
}
|
||||||
this.verified = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -716,18 +734,17 @@ class SignaturePacket {
|
||||||
isExpired(date = new Date()) {
|
isExpired(date = new Date()) {
|
||||||
const normDate = util.normalizeDate(date);
|
const normDate = util.normalizeDate(date);
|
||||||
if (normDate !== null) {
|
if (normDate !== null) {
|
||||||
const expirationTime = this.getExpirationTime();
|
return !(this.created <= normDate && normDate <= this.getExpirationTime());
|
||||||
return !(this.created <= normDate && normDate <= expirationTime);
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the expiration time of the signature or Infinity if signature does not expire
|
* Returns the expiration time of the signature or Infinity if signature does not expire
|
||||||
* @returns {Date} Expiration time.
|
* @returns {Date | Infinity} Expiration time.
|
||||||
*/
|
*/
|
||||||
getExpirationTime() {
|
getExpirationTime() {
|
||||||
return !this.signatureNeverExpires ? new Date(this.created.getTime() + this.signatureExpirationTime * 1000) : Infinity;
|
return this.signatureNeverExpires ? Infinity : new Date(this.created.getTime() + this.signatureExpirationTime * 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -231,7 +231,7 @@ module.exports = () => describe('Elliptic Curve Cryptography for secp256k1 curve
|
||||||
return openpgp.generateKey(options).then(function (key) {
|
return openpgp.generateKey(options).then(function (key) {
|
||||||
expect(key).to.exist;
|
expect(key).to.exist;
|
||||||
expect(key.key).to.exist;
|
expect(key.key).to.exist;
|
||||||
expect(key.key.primaryKey).to.exist;
|
expect(key.key.keyPacket).to.exist;
|
||||||
expect(key.privateKeyArmored).to.exist;
|
expect(key.privateKeyArmored).to.exist;
|
||||||
expect(key.publicKeyArmored).to.exist;
|
expect(key.publicKeyArmored).to.exist;
|
||||||
});
|
});
|
||||||
|
|
|
@ -2415,7 +2415,7 @@ function versionSpecificTests() {
|
||||||
const actual_delta = (new Date(expiration) - new Date()) / 1000;
|
const actual_delta = (new Date(expiration) - new Date()) / 1000;
|
||||||
expect(Math.abs(actual_delta - expect_delta)).to.be.below(60);
|
expect(Math.abs(actual_delta - expect_delta)).to.be.below(60);
|
||||||
|
|
||||||
const subKeyExpiration = await key.subKeys[0].getExpirationTime(key.primaryKey);
|
const subKeyExpiration = await key.subKeys[0].getExpirationTime();
|
||||||
expect(subKeyExpiration).to.exist;
|
expect(subKeyExpiration).to.exist;
|
||||||
|
|
||||||
const actual_subKeyDelta = (new Date(subKeyExpiration) - new Date()) / 1000;
|
const actual_subKeyDelta = (new Date(subKeyExpiration) - new Date()) / 1000;
|
||||||
|
@ -2693,7 +2693,7 @@ function versionSpecificTests() {
|
||||||
// ssb cv25519 2019-03-20 [E]
|
// ssb cv25519 2019-03-20 [E]
|
||||||
// E4557C2B02FFBF4B04F87401EC336AF7133D0F85BE7FD09BAEFD9CAEB8C93965
|
// E4557C2B02FFBF4B04F87401EC336AF7133D0F85BE7FD09BAEFD9CAEB8C93965
|
||||||
const key = await openpgp.readKey({ armoredKey: v5_sample_key });
|
const key = await openpgp.readKey({ armoredKey: v5_sample_key });
|
||||||
expect(await key.primaryKey.getFingerprint()).to.equal('19347bc9872464025f99df3ec2e0000ed9884892e1f7b3ea4c94009159569b54');
|
expect(await key.keyPacket.getFingerprint()).to.equal('19347bc9872464025f99df3ec2e0000ed9884892e1f7b3ea4c94009159569b54');
|
||||||
expect(await key.subKeys[0].getFingerprint()).to.equal('e4557c2b02ffbf4b04f87401ec336af7133d0f85be7fd09baefd9caeb8c93965');
|
expect(await key.subKeys[0].getFingerprint()).to.equal('e4557c2b02ffbf4b04f87401ec336af7133d0f85be7fd09baefd9caeb8c93965');
|
||||||
await key.verifyPrimaryKey();
|
await key.verifyPrimaryKey();
|
||||||
});
|
});
|
||||||
|
@ -2816,9 +2816,7 @@ module.exports = () => describe('Key', function() {
|
||||||
expect(pubKey.subKeys).to.exist;
|
expect(pubKey.subKeys).to.exist;
|
||||||
expect(pubKey.subKeys).to.have.length(2);
|
expect(pubKey.subKeys).to.have.length(2);
|
||||||
|
|
||||||
await expect(pubKey.subKeys[0].verify(
|
await expect(pubKey.subKeys[0].verify()).to.be.rejectedWith('Subkey is revoked');
|
||||||
pubKey.primaryKey
|
|
||||||
)).to.be.rejectedWith('Subkey is revoked');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Verify status of key with non-self revocation signature', async function() {
|
it('Verify status of key with non-self revocation signature', async function() {
|
||||||
|
@ -2842,19 +2840,24 @@ module.exports = () => describe('Key', function() {
|
||||||
expect(signatures[1].valid).to.be.false;
|
expect(signatures[1].valid).to.be.false;
|
||||||
|
|
||||||
const { user } = await pubKey.getPrimaryUser();
|
const { user } = await pubKey.getPrimaryUser();
|
||||||
await expect(user.verifyCertificate(pubKey.primaryKey, user.otherCertifications[0], [certifyingKey], undefined, openpgp.config)).to.be.rejectedWith('User certificate is revoked');
|
await expect(user.verifyCertificate(pubKey.keyPacket, user.otherCertifications[0], [certifyingKey], undefined, openpgp.config)).to.be.rejectedWith('User certificate is revoked');
|
||||||
} finally {
|
} finally {
|
||||||
openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms;
|
openpgp.config.rejectPublicKeyAlgorithms = rejectPublicKeyAlgorithms;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Verify primary key with authorized revocation key in a direct-key signature', async function() {
|
||||||
|
const pubKey = await openpgp.readKey({ armoredKey: key_with_authorized_revocation_key_in_separate_sig });
|
||||||
|
await pubKey.verifyPrimaryKey();
|
||||||
|
});
|
||||||
|
|
||||||
it('Verify certificate of key with future creation date', async function() {
|
it('Verify certificate of key with future creation date', async function() {
|
||||||
const pubKey = await openpgp.readKey({ armoredKey: key_created_2030 });
|
const pubKey = await openpgp.readKey({ armoredKey: key_created_2030 });
|
||||||
const user = pubKey.users[0];
|
const user = pubKey.users[0];
|
||||||
await user.verifyCertificate(pubKey.primaryKey, user.selfCertifications[0], [pubKey], pubKey.primaryKey.created, openpgp.config);
|
await user.verifyCertificate(pubKey.keyPacket, user.selfCertifications[0], [pubKey], pubKey.keyPacket.created, openpgp.config);
|
||||||
const verifyAllResult = await user.verifyAllCertifications(pubKey.primaryKey, [pubKey], pubKey.primaryKey.created);
|
const verifyAllResult = await user.verifyAllCertifications(pubKey.keyPacket, [pubKey], pubKey.keyPacket.created);
|
||||||
expect(verifyAllResult[0].valid).to.be.true;
|
expect(verifyAllResult[0].valid).to.be.true;
|
||||||
await user.verify(pubKey.primaryKey, pubKey.primaryKey.created);
|
await user.verify(pubKey.keyPacket, pubKey.keyPacket.created);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Evaluate key flags to find valid encryption key packet', async function() {
|
it('Evaluate key flags to find valid encryption key packet', async function() {
|
||||||
|
@ -2908,29 +2911,38 @@ module.exports = () => describe('Key', function() {
|
||||||
const [, pubKey] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
const [, pubKey] = await openpgp.readKeys({ armoredKeys: twoKeys });
|
||||||
expect(pubKey).to.exist;
|
expect(pubKey).to.exist;
|
||||||
expect(pubKey).to.be.an.instanceof(openpgp.PublicKey);
|
expect(pubKey).to.be.an.instanceof(openpgp.PublicKey);
|
||||||
const expirationTime = await pubKey.subKeys[0].getExpirationTime(pubKey.primaryKey);
|
const expirationTime = await pubKey.subKeys[0].getExpirationTime();
|
||||||
expect(expirationTime.toISOString()).to.be.equal('2018-11-26T10:58:29.000Z');
|
expect(expirationTime.toISOString()).to.be.equal('2018-11-26T10:58:29.000Z');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Method getExpirationTime V4 Key with capabilities', async function() {
|
it('Method getExpirationTime V4 Key with capabilities', async function() {
|
||||||
const pubKey = await openpgp.readKey({ armoredKey: priv_key_2000_2008 });
|
const { minRSABits } = openpgp.config;
|
||||||
expect(pubKey).to.exist;
|
try {
|
||||||
expect(pubKey).to.be.an.instanceof(openpgp.PublicKey);
|
openpgp.config.minRSABits = 1024;
|
||||||
pubKey.users[0].selfCertifications[0].keyFlags = [1];
|
const privKey = await openpgp.readKey({ armoredKey: priv_key_2000_2008 });
|
||||||
const expirationTime = await pubKey.getExpirationTime();
|
privKey.users[0].selfCertifications[0].keyFlags = [1];
|
||||||
expect(expirationTime).to.equal(Infinity);
|
const expirationTime = await privKey.getExpirationTime();
|
||||||
const encryptExpirationTime = await pubKey.getExpirationTime('encrypt_sign');
|
expect(expirationTime).to.equal(Infinity);
|
||||||
expect(encryptExpirationTime.toISOString()).to.equal('2008-02-12T17:12:08.000Z');
|
const encryptExpirationTime = await privKey.getExpirationTime('encrypt_sign');
|
||||||
|
expect(encryptExpirationTime.toISOString()).to.equal('2008-02-12T17:12:08.000Z');
|
||||||
|
} finally {
|
||||||
|
openpgp.config.minRSABits = minRSABits;
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Method getExpirationTime V4 Key with capabilities - capable primary key', async function() {
|
it('Method getExpirationTime V4 Key with capabilities - capable primary key', async function() {
|
||||||
const pubKey = await openpgp.readKey({ armoredKey: priv_key_2000_2008 });
|
const { minRSABits } = openpgp.config;
|
||||||
expect(pubKey).to.exist;
|
try {
|
||||||
expect(pubKey).to.be.an.instanceof(openpgp.PublicKey);
|
openpgp.config.minRSABits = 1024;
|
||||||
const expirationTime = await pubKey.getExpirationTime();
|
const privKey = await openpgp.readKey({ armoredKey: priv_key_2000_2008 });
|
||||||
expect(expirationTime).to.equal(Infinity);
|
const expirationTime = await privKey.getExpirationTime();
|
||||||
const encryptExpirationTime = await pubKey.getExpirationTime('encrypt_sign');
|
expect(expirationTime).to.equal(Infinity);
|
||||||
expect(encryptExpirationTime).to.equal(Infinity);
|
const encryptExpirationTime = await privKey.getExpirationTime('encrypt_sign');
|
||||||
|
expect(encryptExpirationTime).to.equal(Infinity);
|
||||||
|
} finally {
|
||||||
|
openpgp.config.minRSABits = minRSABits;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decryptKey() - throw if key parameters don't correspond", async function() {
|
it("decryptKey() - throw if key parameters don't correspond", async function() {
|
||||||
|
@ -2985,7 +2997,7 @@ module.exports = () => describe('Key', function() {
|
||||||
|
|
||||||
it('makeDummy() - the converted key can be parsed', async function() {
|
it('makeDummy() - the converted key can be parsed', async function() {
|
||||||
const { key } = await openpgp.generateKey({ userIDs: { name: 'dummy', email: 'dummy@alice.com' } });
|
const { key } = await openpgp.generateKey({ userIDs: { name: 'dummy', email: 'dummy@alice.com' } });
|
||||||
key.primaryKey.makeDummy();
|
key.keyPacket.makeDummy();
|
||||||
const parsedKeys = await openpgp.readKey({ armoredKey: key.armor() });
|
const parsedKeys = await openpgp.readKey({ armoredKey: key.armor() });
|
||||||
expect(parsedKeys).to.not.be.empty;
|
expect(parsedKeys).to.not.be.empty;
|
||||||
});
|
});
|
||||||
|
@ -2993,7 +3005,7 @@ module.exports = () => describe('Key', function() {
|
||||||
it('makeDummy() - the converted key can be encrypted and decrypted', async function() {
|
it('makeDummy() - the converted key can be encrypted and decrypted', async function() {
|
||||||
const { key } = await openpgp.generateKey({ userIDs: { name: 'dummy', email: 'dummy@alice.com' } });
|
const { key } = await openpgp.generateKey({ userIDs: { name: 'dummy', email: 'dummy@alice.com' } });
|
||||||
const passphrase = 'passphrase';
|
const passphrase = 'passphrase';
|
||||||
key.primaryKey.makeDummy();
|
key.keyPacket.makeDummy();
|
||||||
expect(key.isDecrypted()).to.be.true;
|
expect(key.isDecrypted()).to.be.true;
|
||||||
const encryptedKey = await openpgp.encryptKey({ privateKey: key, passphrase });
|
const encryptedKey = await openpgp.encryptKey({ privateKey: key, passphrase });
|
||||||
expect(encryptedKey.isDecrypted()).to.be.false;
|
expect(encryptedKey.isDecrypted()).to.be.false;
|
||||||
|
@ -3006,9 +3018,9 @@ module.exports = () => describe('Key', function() {
|
||||||
privateKey: await openpgp.readKey({ armoredKey: priv_key_rsa }),
|
privateKey: await openpgp.readKey({ armoredKey: priv_key_rsa }),
|
||||||
passphrase: 'hello world'
|
passphrase: 'hello world'
|
||||||
});
|
});
|
||||||
expect(key.primaryKey.isDummy()).to.be.false;
|
expect(key.keyPacket.isDummy()).to.be.false;
|
||||||
key.primaryKey.makeDummy();
|
key.keyPacket.makeDummy();
|
||||||
expect(key.primaryKey.isDummy()).to.be.true;
|
expect(key.keyPacket.isDummy()).to.be.true;
|
||||||
await key.validate();
|
await key.validate();
|
||||||
await expect(openpgp.reformatKey({ privateKey: key, userIDs: { name: 'test', email: 'a@b.com' } })).to.be.rejectedWith(/Cannot reformat a gnu-dummy primary key/);
|
await expect(openpgp.reformatKey({ privateKey: key, userIDs: { name: 'test', email: 'a@b.com' } })).to.be.rejectedWith(/Cannot reformat a gnu-dummy primary key/);
|
||||||
});
|
});
|
||||||
|
@ -3018,27 +3030,27 @@ module.exports = () => describe('Key', function() {
|
||||||
privateKey: await openpgp.readKey({ armoredKey: priv_key_rsa }),
|
privateKey: await openpgp.readKey({ armoredKey: priv_key_rsa }),
|
||||||
passphrase: 'hello world'
|
passphrase: 'hello world'
|
||||||
});
|
});
|
||||||
expect(key.primaryKey.isDummy()).to.be.false;
|
expect(key.keyPacket.isDummy()).to.be.false;
|
||||||
key.primaryKey.makeDummy();
|
key.keyPacket.makeDummy();
|
||||||
expect(key.primaryKey.isDummy()).to.be.true;
|
expect(key.keyPacket.isDummy()).to.be.true;
|
||||||
await expect(openpgp.sign({ message: await openpgp.createMessage({ text: 'test' }), signingKeys: [key], config: { minRSABits: 1024 } })).to.be.fulfilled;
|
await expect(openpgp.sign({ message: await openpgp.createMessage({ text: 'test' }), signingKeys: [key], config: { minRSABits: 1024 } })).to.be.fulfilled;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('makeDummy() - should work for encrypted keys', async function() {
|
it('makeDummy() - should work for encrypted keys', async function() {
|
||||||
const passphrase = 'hello world';
|
const passphrase = 'hello world';
|
||||||
const key = await openpgp.readKey({ armoredKey: priv_key_rsa });
|
const key = await openpgp.readKey({ armoredKey: priv_key_rsa });
|
||||||
expect(key.primaryKey.isDummy()).to.be.false;
|
expect(key.keyPacket.isDummy()).to.be.false;
|
||||||
expect(key.primaryKey.makeDummy()).to.not.throw;
|
expect(key.keyPacket.makeDummy()).to.not.throw;
|
||||||
expect(key.primaryKey.isDummy()).to.be.true;
|
expect(key.keyPacket.isDummy()).to.be.true;
|
||||||
// dummy primary key should always be marked as not decrypted
|
// dummy primary key should always be marked as not decrypted
|
||||||
const decryptedKey = await openpgp.decryptKey({ privateKey: key, passphrase });
|
const decryptedKey = await openpgp.decryptKey({ privateKey: key, passphrase });
|
||||||
expect(decryptedKey.primaryKey.isDummy()).to.be.true;
|
expect(decryptedKey.keyPacket.isDummy()).to.be.true;
|
||||||
expect(decryptedKey.primaryKey.isEncrypted === null);
|
expect(decryptedKey.keyPacket.isEncrypted === null);
|
||||||
expect(decryptedKey.primaryKey.isDecrypted()).to.be.false;
|
expect(decryptedKey.keyPacket.isDecrypted()).to.be.false;
|
||||||
const encryptedKey = await openpgp.encryptKey({ privateKey: decryptedKey, passphrase });
|
const encryptedKey = await openpgp.encryptKey({ privateKey: decryptedKey, passphrase });
|
||||||
expect(encryptedKey.primaryKey.isDummy()).to.be.true;
|
expect(encryptedKey.keyPacket.isDummy()).to.be.true;
|
||||||
expect(encryptedKey.primaryKey.isEncrypted === null);
|
expect(encryptedKey.keyPacket.isEncrypted === null);
|
||||||
expect(encryptedKey.primaryKey.isDecrypted()).to.be.false;
|
expect(encryptedKey.keyPacket.isDecrypted()).to.be.false;
|
||||||
// confirm that the converted keys can be parsed
|
// confirm that the converted keys can be parsed
|
||||||
await openpgp.readKey({ armoredKey: encryptedKey.armor() });
|
await openpgp.readKey({ armoredKey: encryptedKey.armor() });
|
||||||
await openpgp.readKey({ armoredKey: decryptedKey.armor() });
|
await openpgp.readKey({ armoredKey: decryptedKey.armor() });
|
||||||
|
@ -3061,8 +3073,8 @@ module.exports = () => describe('Key', function() {
|
||||||
const signingKeyPacket = key.subKeys[0].keyPacket;
|
const signingKeyPacket = key.subKeys[0].keyPacket;
|
||||||
const privateParams = signingKeyPacket.privateParams;
|
const privateParams = signingKeyPacket.privateParams;
|
||||||
await key.clearPrivateParams();
|
await key.clearPrivateParams();
|
||||||
key.primaryKey.isEncrypted = false;
|
key.keyPacket.isEncrypted = false;
|
||||||
key.primaryKey.privateParams = privateParams;
|
key.keyPacket.privateParams = privateParams;
|
||||||
key.subKeys[0].keyPacket.isEncrypted = false;
|
key.subKeys[0].keyPacket.isEncrypted = false;
|
||||||
key.subKeys[0].keyPacket.privateParams = privateParams;
|
key.subKeys[0].keyPacket.privateParams = privateParams;
|
||||||
await expect(key.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(key.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
|
@ -3079,8 +3091,8 @@ module.exports = () => describe('Key', function() {
|
||||||
privateParams[name] = value;
|
privateParams[name] = value;
|
||||||
});
|
});
|
||||||
await key.clearPrivateParams();
|
await key.clearPrivateParams();
|
||||||
key.primaryKey.isEncrypted = false;
|
key.keyPacket.isEncrypted = false;
|
||||||
key.primaryKey.privateParams = privateParams;
|
key.keyPacket.privateParams = privateParams;
|
||||||
key.subKeys[0].keyPacket.isEncrypted = false;
|
key.subKeys[0].keyPacket.isEncrypted = false;
|
||||||
key.subKeys[0].keyPacket.privateParams = privateParams;
|
key.subKeys[0].keyPacket.privateParams = privateParams;
|
||||||
await expect(key.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(key.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
|
@ -3162,11 +3174,11 @@ module.exports = () => describe('Key', function() {
|
||||||
updated.verifyPrimaryKey().then(async result => {
|
updated.verifyPrimaryKey().then(async result => {
|
||||||
await expect(source.verifyPrimaryKey()).to.eventually.equal(result);
|
await expect(source.verifyPrimaryKey()).to.eventually.equal(result);
|
||||||
}),
|
}),
|
||||||
updated.users[0].verify(updated.primaryKey).then(async result => {
|
updated.users[0].verify(updated.keyPacket).then(async result => {
|
||||||
await expect(source.users[0].verify(source.primaryKey)).to.eventually.equal(result);
|
await expect(source.users[0].verify(source.keyPacket)).to.eventually.equal(result);
|
||||||
}),
|
}),
|
||||||
updated.subKeys[0].verify(updated.primaryKey).then(async result => {
|
updated.subKeys[0].verify().then(async result => {
|
||||||
await expect(source.subKeys[0].verify(source.primaryKey)).to.eventually.deep.equal(result);
|
await expect(source.subKeys[0].verify()).to.eventually.deep.equal(result);
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
@ -3182,17 +3194,11 @@ module.exports = () => describe('Key', function() {
|
||||||
const updated = await dest.update(source);
|
const updated = await dest.update(source);
|
||||||
expect(updated.isPrivate()).to.be.true;
|
expect(updated.isPrivate()).to.be.true;
|
||||||
|
|
||||||
const { selfCertification: destCertification } = await updated.getPrimaryUser();
|
await updated.verifyPrimaryKey();
|
||||||
const { selfCertification: sourceCertification } = await source.getPrimaryUser();
|
await source.verifyPrimaryKey();
|
||||||
destCertification.verified = null;
|
|
||||||
sourceCertification.verified = null;
|
|
||||||
await updated.verifyPrimaryKey().then(async () => expect(destCertification.verified).to.be.true);
|
|
||||||
await source.verifyPrimaryKey().then(async () => expect(sourceCertification.verified).to.be.true);
|
|
||||||
|
|
||||||
destCertification.verified = null;
|
await updated.users[0].verify(updated.keyPacket);
|
||||||
sourceCertification.verified = null;
|
await source.users[0].verify(source.keyPacket);
|
||||||
await updated.users[0].verify(updated.primaryKey).then(async () => expect(destCertification.verified).to.be.true);
|
|
||||||
await source.users[0].verify(source.primaryKey).then(async () => expect(sourceCertification.verified).to.be.true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('update() - merge private key into public key - mismatch throws error', async function() {
|
it('update() - merge private key into public key - mismatch throws error', async function() {
|
||||||
|
@ -3209,11 +3215,14 @@ module.exports = () => describe('Key', function() {
|
||||||
const source = await openpgp.readKey({ armoredKey: pgp_desktop_pub });
|
const source = await openpgp.readKey({ armoredKey: pgp_desktop_pub });
|
||||||
const dest = await openpgp.readKey({ armoredKey: pgp_desktop_priv });
|
const dest = await openpgp.readKey({ armoredKey: pgp_desktop_priv });
|
||||||
expect(source.subKeys[0].bindingSignatures[0]).to.exist;
|
expect(source.subKeys[0].bindingSignatures[0]).to.exist;
|
||||||
await source.subKeys[0].verify(source.primaryKey);
|
await source.subKeys[0].verify();
|
||||||
expect(dest.subKeys[0].bindingSignatures[0]).to.not.exist;
|
expect(dest.subKeys[0].bindingSignatures[0]).to.not.exist;
|
||||||
const updated = await dest.update(source);
|
const updated = await dest.update(source);
|
||||||
expect(updated.subKeys[0].bindingSignatures[0]).to.exist;
|
expect(updated.subKeys[0].bindingSignatures[0]).to.exist;
|
||||||
await updated.subKeys[0].verify(source.primaryKey);
|
// the source primary key should still verify the subkey
|
||||||
|
updated.subKeys[0].mainKey = source;
|
||||||
|
await updated.subKeys[0].verify();
|
||||||
|
updated.subKeys[0].mainKey = updated;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('update() - merge multiple subkey binding signatures', async function() {
|
it('update() - merge multiple subkey binding signatures', async function() {
|
||||||
|
@ -3221,12 +3230,12 @@ module.exports = () => describe('Key', function() {
|
||||||
const dest = await openpgp.readKey({ armoredKey: multipleBindingSignatures });
|
const dest = await openpgp.readKey({ armoredKey: multipleBindingSignatures });
|
||||||
// remove last subkey binding signature of destination subkey
|
// remove last subkey binding signature of destination subkey
|
||||||
dest.subKeys[0].bindingSignatures.length = 1;
|
dest.subKeys[0].bindingSignatures.length = 1;
|
||||||
expect((await source.subKeys[0].getExpirationTime(source.primaryKey)).toISOString()).to.equal('2015-10-18T07:41:30.000Z');
|
expect((await source.subKeys[0].getExpirationTime()).toISOString()).to.equal('2015-10-18T07:41:30.000Z');
|
||||||
expect((await dest.subKeys[0].getExpirationTime(dest.primaryKey)).toISOString()).to.equal('2018-09-07T06:03:37.000Z');
|
expect((await dest.subKeys[0].getExpirationTime()).toISOString()).to.equal('2018-09-07T06:03:37.000Z');
|
||||||
return dest.update(source).then(async updated => {
|
return dest.update(source).then(async updated => {
|
||||||
expect(updated.subKeys[0].bindingSignatures.length).to.equal(1);
|
expect(updated.subKeys[0].bindingSignatures.length).to.equal(1);
|
||||||
// destination key gets new expiration date from source key which has newer subkey binding signature
|
// destination key gets new expiration date from source key which has newer subkey binding signature
|
||||||
expect((await updated.subKeys[0].getExpirationTime(updated.primaryKey)).toISOString()).to.equal('2015-10-18T07:41:30.000Z');
|
expect((await updated.subKeys[0].getExpirationTime()).toISOString()).to.equal('2015-10-18T07:41:30.000Z');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3258,7 +3267,7 @@ module.exports = () => describe('Key', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
const subKey = pubKey.subKeys[0];
|
const subKey = pubKey.subKeys[0];
|
||||||
await subKey.revoke(privKey.primaryKey, {
|
await subKey.revoke(privKey.keyPacket, {
|
||||||
flag: openpgp.enums.reasonForRevocation.keySuperseded
|
flag: openpgp.enums.reasonForRevocation.keySuperseded
|
||||||
}).then(async revKey => {
|
}).then(async revKey => {
|
||||||
expect(revKey.revocationSignatures).to.exist.and.have.length(1);
|
expect(revKey.revocationSignatures).to.exist.and.have.length(1);
|
||||||
|
@ -3266,8 +3275,8 @@ module.exports = () => describe('Key', function() {
|
||||||
expect(revKey.revocationSignatures[0].reasonForRevocationFlag).to.equal(openpgp.enums.reasonForRevocation.keySuperseded);
|
expect(revKey.revocationSignatures[0].reasonForRevocationFlag).to.equal(openpgp.enums.reasonForRevocation.keySuperseded);
|
||||||
expect(revKey.revocationSignatures[0].reasonForRevocationString).to.equal('');
|
expect(revKey.revocationSignatures[0].reasonForRevocationString).to.equal('');
|
||||||
|
|
||||||
await subKey.verify(pubKey.primaryKey);
|
await subKey.verify();
|
||||||
await expect(revKey.verify(pubKey.primaryKey)).to.be.rejectedWith('Subkey is revoked');
|
await expect(revKey.verify()).to.be.rejectedWith('Subkey is revoked');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3491,27 +3500,27 @@ VYGdb3eNlV8CfoEC
|
||||||
|
|
||||||
it('Selects the most recent subkey binding signature', async function() {
|
it('Selects the most recent subkey binding signature', async function() {
|
||||||
const key = await openpgp.readKey({ armoredKey: multipleBindingSignatures });
|
const key = await openpgp.readKey({ armoredKey: multipleBindingSignatures });
|
||||||
expect((await key.subKeys[0].getExpirationTime(key.primaryKey)).toISOString()).to.equal('2015-10-18T07:41:30.000Z');
|
expect((await key.subKeys[0].getExpirationTime()).toISOString()).to.equal('2015-10-18T07:41:30.000Z');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Selects the most recent non-expired subkey binding signature', async function() {
|
it('Selects the most recent non-expired subkey binding signature', async function() {
|
||||||
const key = await openpgp.readKey({ armoredKey: multipleBindingSignatures });
|
const key = await openpgp.readKey({ armoredKey: multipleBindingSignatures });
|
||||||
key.subKeys[0].bindingSignatures[1].signatureNeverExpires = false;
|
key.subKeys[0].bindingSignatures[1].signatureNeverExpires = false;
|
||||||
key.subKeys[0].bindingSignatures[1].signatureExpirationTime = 0;
|
key.subKeys[0].bindingSignatures[1].signatureExpirationTime = 0;
|
||||||
expect((await key.subKeys[0].getExpirationTime(key.primaryKey)).toISOString()).to.equal('2018-09-07T06:03:37.000Z');
|
expect((await key.subKeys[0].getExpirationTime()).toISOString()).to.equal('2018-09-07T06:03:37.000Z');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Selects the most recent valid subkey binding signature', async function() {
|
it('Selects the most recent valid subkey binding signature', async function() {
|
||||||
const key = await openpgp.readKey({ armoredKey: multipleBindingSignatures });
|
const key = await openpgp.readKey({ armoredKey: multipleBindingSignatures });
|
||||||
key.subKeys[0].bindingSignatures[1].signatureData[0]++;
|
key.subKeys[0].bindingSignatures[1].signatureData[0]++;
|
||||||
expect((await key.subKeys[0].getExpirationTime(key.primaryKey)).toISOString()).to.equal('2018-09-07T06:03:37.000Z');
|
expect((await key.subKeys[0].getExpirationTime()).toISOString()).to.equal('2018-09-07T06:03:37.000Z');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Handles a key with no valid subkey binding signatures gracefully', async function() {
|
it('Handles a key with no valid subkey binding signatures gracefully', async function() {
|
||||||
const key = await openpgp.readKey({ armoredKey: multipleBindingSignatures });
|
const key = await openpgp.readKey({ armoredKey: multipleBindingSignatures });
|
||||||
key.subKeys[0].bindingSignatures[0].signatureData[0]++;
|
key.subKeys[0].bindingSignatures[0].signatureData[0]++;
|
||||||
key.subKeys[0].bindingSignatures[1].signatureData[0]++;
|
key.subKeys[0].bindingSignatures[1].signatureData[0]++;
|
||||||
expect(await key.subKeys[0].getExpirationTime(key.primaryKey)).to.be.null;
|
expect(await key.subKeys[0].getExpirationTime()).to.be.null;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Reject encryption with revoked primary user', async function() {
|
it('Reject encryption with revoked primary user', async function() {
|
||||||
|
@ -3590,11 +3599,11 @@ VYGdb3eNlV8CfoEC
|
||||||
expect(subKey).to.exist;
|
expect(subKey).to.exist;
|
||||||
expect(newPrivateKey.subKeys.length).to.be.equal(total + 1);
|
expect(newPrivateKey.subKeys.length).to.be.equal(total + 1);
|
||||||
const subkeyN = subKey.keyPacket.publicParams.n;
|
const subkeyN = subKey.keyPacket.publicParams.n;
|
||||||
const pkN = privateKey.primaryKey.publicParams.n;
|
const pkN = privateKey.keyPacket.publicParams.n;
|
||||||
expect(subkeyN.length).to.be.equal(pkN.length);
|
expect(subkeyN.length).to.be.equal(pkN.length);
|
||||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('rsaEncryptSign');
|
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('rsaEncryptSign');
|
||||||
expect(subKey.getAlgorithmInfo().bits).to.be.equal(privateKey.getAlgorithmInfo().bits);
|
expect(subKey.getAlgorithmInfo().bits).to.be.equal(privateKey.getAlgorithmInfo().bits);
|
||||||
await subKey.verify(newPrivateKey.primaryKey);
|
await subKey.verify();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Add a new default subkey to an rsaSign key', async function() {
|
it('Add a new default subkey to an rsaSign key', async function() {
|
||||||
|
@ -3654,7 +3663,7 @@ VYGdb3eNlV8CfoEC
|
||||||
const subKey = importedPrivateKey.subKeys[total];
|
const subKey = importedPrivateKey.subKeys[total];
|
||||||
expect(subKey).to.exist;
|
expect(subKey).to.exist;
|
||||||
expect(importedPrivateKey.subKeys.length).to.be.equal(total + 1);
|
expect(importedPrivateKey.subKeys.length).to.be.equal(total + 1);
|
||||||
await subKey.verify(importedPrivateKey.primaryKey);
|
await subKey.verify();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('create and add a new ec subkey to a ec key', async function() {
|
it('create and add a new ec subkey to a ec key', async function() {
|
||||||
|
@ -3677,10 +3686,10 @@ VYGdb3eNlV8CfoEC
|
||||||
expect(subKey2).to.exist;
|
expect(subKey2).to.exist;
|
||||||
expect(newPrivateKey.subKeys.length).to.be.equal(total + 1);
|
expect(newPrivateKey.subKeys.length).to.be.equal(total + 1);
|
||||||
const subkeyOid = subKey2.keyPacket.publicParams.oid;
|
const subkeyOid = subKey2.keyPacket.publicParams.oid;
|
||||||
const pkOid = privateKey.primaryKey.publicParams.oid;
|
const pkOid = privateKey.keyPacket.publicParams.oid;
|
||||||
expect(subkeyOid.getName()).to.be.equal(pkOid.getName());
|
expect(subkeyOid.getName()).to.be.equal(pkOid.getName());
|
||||||
expect(subKey2.getAlgorithmInfo().algorithm).to.be.equal('eddsa');
|
expect(subKey2.getAlgorithmInfo().algorithm).to.be.equal('eddsa');
|
||||||
await subKey2.verify(privateKey.primaryKey);
|
await subKey2.verify();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('create and add a new ecdsa subkey to a eddsa key', async function() {
|
it('create and add a new ecdsa subkey to a eddsa key', async function() {
|
||||||
|
@ -3698,7 +3707,7 @@ VYGdb3eNlV8CfoEC
|
||||||
expect(newPrivateKey.getAlgorithmInfo().algorithm).to.be.equal('eddsa');
|
expect(newPrivateKey.getAlgorithmInfo().algorithm).to.be.equal('eddsa');
|
||||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('ecdsa');
|
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('ecdsa');
|
||||||
|
|
||||||
await subKey.verify(privateKey.primaryKey);
|
await subKey.verify();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('create and add a new ecc subkey to a rsa key', async function() {
|
it('create and add a new ecc subkey to a rsa key', async function() {
|
||||||
|
@ -3716,7 +3725,7 @@ VYGdb3eNlV8CfoEC
|
||||||
expect(subKey).to.exist;
|
expect(subKey).to.exist;
|
||||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('ecdh');
|
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('ecdh');
|
||||||
expect(subKey.getAlgorithmInfo().curve).to.be.equal(openpgp.enums.curve.curve25519);
|
expect(subKey.getAlgorithmInfo().curve).to.be.equal(openpgp.enums.curve.curve25519);
|
||||||
await subKey.verify(privateKey.primaryKey);
|
await subKey.verify();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('create and add a new rsa subkey to a ecc key', async function() {
|
it('create and add a new rsa subkey to a ecc key', async function() {
|
||||||
|
@ -3732,7 +3741,7 @@ VYGdb3eNlV8CfoEC
|
||||||
expect(newPrivateKey.subKeys.length).to.be.equal(total + 1);
|
expect(newPrivateKey.subKeys.length).to.be.equal(total + 1);
|
||||||
expect(subKey.getAlgorithmInfo().bits).to.be.equal(4096);
|
expect(subKey.getAlgorithmInfo().bits).to.be.equal(4096);
|
||||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('rsaEncryptSign');
|
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('rsaEncryptSign');
|
||||||
await subKey.verify(privateKey.primaryKey);
|
await subKey.verify();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('create and add a new rsa subkey to a dsa key', async function() {
|
it('create and add a new rsa subkey to a dsa key', async function() {
|
||||||
|
@ -3745,7 +3754,7 @@ VYGdb3eNlV8CfoEC
|
||||||
expect(subKey).to.exist;
|
expect(subKey).to.exist;
|
||||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('rsaEncryptSign');
|
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('rsaEncryptSign');
|
||||||
expect(subKey.getAlgorithmInfo().bits).to.be.equal(2048);
|
expect(subKey.getAlgorithmInfo().bits).to.be.equal(2048);
|
||||||
await subKey.verify(privateKey.primaryKey);
|
await subKey.verify();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sign/verify data with the new subkey correctly using curve25519', async function() {
|
it('sign/verify data with the new subkey correctly using curve25519', async function() {
|
||||||
|
@ -3759,10 +3768,10 @@ VYGdb3eNlV8CfoEC
|
||||||
newPrivateKey = await openpgp.readKey({ armoredKey: armoredKey });
|
newPrivateKey = await openpgp.readKey({ armoredKey: armoredKey });
|
||||||
const subKey = newPrivateKey.subKeys[total];
|
const subKey = newPrivateKey.subKeys[total];
|
||||||
const subkeyOid = subKey.keyPacket.publicParams.oid;
|
const subkeyOid = subKey.keyPacket.publicParams.oid;
|
||||||
const pkOid = newPrivateKey.primaryKey.publicParams.oid;
|
const pkOid = newPrivateKey.keyPacket.publicParams.oid;
|
||||||
expect(subkeyOid.getName()).to.be.equal(pkOid.getName());
|
expect(subkeyOid.getName()).to.be.equal(pkOid.getName());
|
||||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('eddsa');
|
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('eddsa');
|
||||||
await subKey.verify(newPrivateKey.primaryKey);
|
await subKey.verify();
|
||||||
expect(await newPrivateKey.getSigningKey()).to.be.equal(subKey);
|
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, armor:false });
|
||||||
const message = await openpgp.readMessage({ binaryMessage: signed });
|
const message = await openpgp.readMessage({ binaryMessage: signed });
|
||||||
|
@ -3784,7 +3793,7 @@ VYGdb3eNlV8CfoEC
|
||||||
newPrivateKey = await openpgp.readKey({ armoredKey: armoredKey });
|
newPrivateKey = await openpgp.readKey({ armoredKey: armoredKey });
|
||||||
const subKey = newPrivateKey.subKeys[total];
|
const subKey = newPrivateKey.subKeys[total];
|
||||||
const publicKey = newPrivateKey.toPublic();
|
const publicKey = newPrivateKey.toPublic();
|
||||||
await subKey.verify(newPrivateKey.primaryKey);
|
await subKey.verify();
|
||||||
expect(await newPrivateKey.getEncryptionKey()).to.be.equal(subKey);
|
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, armor:false });
|
||||||
expect(encrypted).to.be.exist;
|
expect(encrypted).to.be.exist;
|
||||||
|
@ -3810,7 +3819,7 @@ VYGdb3eNlV8CfoEC
|
||||||
newPrivateKey = await openpgp.readKey({ armoredKey: armoredKey });
|
newPrivateKey = await openpgp.readKey({ armoredKey: armoredKey });
|
||||||
const subKey = newPrivateKey.subKeys[total];
|
const subKey = newPrivateKey.subKeys[total];
|
||||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('rsaEncryptSign');
|
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('rsaEncryptSign');
|
||||||
await subKey.verify(newPrivateKey.primaryKey);
|
await subKey.verify();
|
||||||
expect(await newPrivateKey.getSigningKey()).to.be.equal(subKey);
|
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, armor:false });
|
||||||
const message = await openpgp.readMessage({ binaryMessage: signed });
|
const message = await openpgp.readMessage({ binaryMessage: signed });
|
||||||
|
@ -3849,12 +3858,12 @@ VYGdb3eNlV8CfoEC
|
||||||
|
|
||||||
it('Subkey.verify returns the latest valid signature', async function () {
|
it('Subkey.verify returns the latest valid signature', async function () {
|
||||||
const { key: encryptionKey } = await openpgp.generateKey({ userIDs: { name: "purple" } });
|
const { key: encryptionKey } = await openpgp.generateKey({ userIDs: { name: "purple" } });
|
||||||
const encryptionKeySignature = await encryptionKey.getSubkeys()[0].verify(encryptionKey);
|
const encryptionKeySignature = await encryptionKey.getSubkeys()[0].verify();
|
||||||
expect(encryptionKeySignature instanceof openpgp.SignaturePacket).to.be.true;
|
expect(encryptionKeySignature instanceof openpgp.SignaturePacket).to.be.true;
|
||||||
expect(encryptionKeySignature.keyFlags[0] & openpgp.enums.keyFlags.encryptCommunication).to.be.equals(openpgp.enums.keyFlags.encryptCommunication);
|
expect(encryptionKeySignature.keyFlags[0] & openpgp.enums.keyFlags.encryptCommunication).to.be.equals(openpgp.enums.keyFlags.encryptCommunication);
|
||||||
expect(encryptionKeySignature.keyFlags[0] & openpgp.enums.keyFlags.encryptStorage).to.be.equals(openpgp.enums.keyFlags.encryptStorage);
|
expect(encryptionKeySignature.keyFlags[0] & openpgp.enums.keyFlags.encryptStorage).to.be.equals(openpgp.enums.keyFlags.encryptStorage);
|
||||||
const { key: signingKey } = await openpgp.generateKey({ userIDs: { name: "purple" }, subkeys: [{ sign: true }] });
|
const { key: signingKey } = await openpgp.generateKey({ userIDs: { name: "purple" }, subkeys: [{ sign: true }] });
|
||||||
const signingKeySignature = await signingKey.getSubkeys()[0].verify(signingKey);
|
const signingKeySignature = await signingKey.getSubkeys()[0].verify();
|
||||||
expect(signingKeySignature instanceof openpgp.SignaturePacket).to.be.true;
|
expect(signingKeySignature instanceof openpgp.SignaturePacket).to.be.true;
|
||||||
expect(signingKeySignature.keyFlags[0] & openpgp.enums.keyFlags.signData).to.be.equals(openpgp.enums.keyFlags.signData);
|
expect(signingKeySignature.keyFlags[0] & openpgp.enums.keyFlags.signData).to.be.equals(openpgp.enums.keyFlags.signData);
|
||||||
});
|
});
|
||||||
|
|
|
@ -736,6 +736,51 @@ AP9fcXZg/Eo55YB/B5XKLkuzDFwJaTlncrD5jcUgtVXFCg==
|
||||||
=q2yi
|
=q2yi
|
||||||
-----END PGP PRIVATE KEY BLOCK-----`;
|
-----END PGP PRIVATE KEY BLOCK-----`;
|
||||||
|
|
||||||
|
const armoredDummyPrivateKey1 = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
Version: GnuPG v1.4.11 (GNU/Linux)
|
||||||
|
|
||||||
|
lQGqBFERnrMRBADmM0hIfkI3yosjgbWo9v0Lnr3CCE+8KsMszgVS+hBu0XfGraKm
|
||||||
|
ivcA2aaJimHqVYOP7gEnwFAxHBBpeTJcu5wzCFyJwEYqVeS3nnaIhBPplSF14Duf
|
||||||
|
i6bB9RV7KxVAg6aunmM2tAutqC+a0y2rDaf7jkJoZ9gWJe2zI+vraD6fiwCgxvHo
|
||||||
|
3IgULB9RqIqpLoMgXfcjC+cD/1jeJlKRm+n71ryYwT/ECKsspFz7S36z6q3XyS8Q
|
||||||
|
QfrsUz2p1fbFicvJwIOJ8B20J/N2/nit4P0gBUTUxv3QEa7XCM/56/xrGkyBzscW
|
||||||
|
AzBoy/AK9K7GN6z13RozuAS60F1xO7MQc6Yi2VU3eASDQEKiyL/Ubf/s/rkZ+sGj
|
||||||
|
yJizBACtwCbQzA+z9XBZNUat5NPgcZz5Qeh1nwF9Nxnr6pyBv7tkrLh/3gxRGHqG
|
||||||
|
063dMbUk8pmUcJzBUyRsNiIPDoEUsLjY5zmZZmp/waAhpREsnK29WLCbqLdpUors
|
||||||
|
c1JJBsObkA1IM8TZY8YUmvsMEvBLCCanuKpclZZXqeRAeOHJ0v4DZQJHTlUBtBZU
|
||||||
|
ZXN0MiA8dGVzdDJAdGVzdC5jb20+iGIEExECACIFAlERnrMCGwMGCwkIBwMCBhUI
|
||||||
|
AgkKCwQWAgMBAh4BAheAAAoJEBEnlAPLFp74xc0AoLNZINHe0ytOsNtMCuLvc3Vd
|
||||||
|
vePUAJ9KX3L5IBqHarsa+aJHX7r796SokZ0BWARREZ6zEAQA2WkxmNbfeMzGUocN
|
||||||
|
3JEVe0o6rxGt5eGrTSmWisduDP3MURabhUXnf4T8oaeYcbJjkLLxMrJmNq55ln1e
|
||||||
|
4bSG5mDkh/ryKsV81m3F0DbqO/z/891nRSP5fondFVral4wsMOzBNgs4vVk7V/F2
|
||||||
|
0MPjR90CIhnVDKPAQbQA+3PjUR8AAwUEALn922AEE+0d7xSMMFpR7ic3Me5QEGnp
|
||||||
|
cT4ft6oc0UK5kAnvKoksZUc0hpBHjX1w3LTz847/5hRDuuDvwvGMWK8IfsjOF9T7
|
||||||
|
rK8QtJuBEyJxjoScA/YZP5vX4y0U1reUEa0EdwmVrnZzatMAe2FhlaR9PlHkOcm5
|
||||||
|
DZwkcExL0dbI/gMDArxZ+5N7kH4zYLtr9glJS/pJ7F0YJqJpNwCbqD8+8DqHD8Uv
|
||||||
|
MgQ/rtBxBJJOaF+1AjCd123hLgzIkkfdTh8loV9hDXMKeJgmiEkEGBECAAkFAlER
|
||||||
|
nrMCGwwACgkQESeUA8sWnvhBswCfdXjznvHCc73/6/MhWcv3dbeTT/wAoLyiZg8+
|
||||||
|
iY3UT9QkV9d0sMgyLkug
|
||||||
|
=GQsY
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----`;
|
||||||
|
|
||||||
|
const armoredPublicKey1 = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Version: GnuPG v1.4.11 (GNU/Linux)
|
||||||
|
|
||||||
|
mQGiBFERlw4RBAD6Bmcf2w1dtUmtCLkdxeqZLArk3vYoQAjdibxA3gXVyur7fsWb
|
||||||
|
ro0jVbBHqOCtC6jDxE2l52NP9+tTlWeVMaqqNvUE47LSaPq2DGI8Wx1Rj6bF3mTs
|
||||||
|
obYEwhGbGh/MhJnME9AHODarvk8AZbzo0+k1EwrBWF6dTUBPfqO7rGU2ewCg80WV
|
||||||
|
x5pt3evj8rRK3jQ8SMKTNRsD/1PhTdxdZTdXARAFzcW1VaaruWW0Rr1+XHKKwDCz
|
||||||
|
i7HE76SO9qjnQfZCZG75CdQxI0h8GFeN3zsDqmhob2iSz2aJ1krtjM+iZ1FBFd57
|
||||||
|
OqCV6wmk5IT0RBN12ZzMS19YvzN/ONXHrmTZlKExd9Mh9RKLeVNw+bf6JsKQEzcY
|
||||||
|
JzFkBACX9X+hDYchO/2hiTwx4iOO9Fhsuh7eIWumB3gt+aUpm1jrSbas/QLTymmk
|
||||||
|
uZuQVXI4NtnlvzlNgWv4L5s5RU5WqNGG7WSaKNdcrvJZRC2dgbUJt04J5CKrWp6R
|
||||||
|
aIYal/81Ut1778lU01PEt563TcQnUBlnjU5OR25KhfSeN5CZY7QUVGVzdCA8dGVz
|
||||||
|
dEB0ZXN0LmNvbT6IYgQTEQIAIgUCURGXDgIbAwYLCQgHAwIGFQgCCQoLBBYCAwEC
|
||||||
|
HgECF4AACgkQikDlZK/UvLSspgCfcNaOpTg1W2ucR1JwBbBGvaERfuMAnRgt3/rs
|
||||||
|
EplqEakMckCtikEnpxYe
|
||||||
|
=b2Ln
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----`;
|
||||||
|
|
||||||
function withCompression(tests) {
|
function withCompression(tests) {
|
||||||
const compressionTypes = Object.keys(openpgp.enums.compression).map(k => openpgp.enums.compression[k]);
|
const compressionTypes = Object.keys(openpgp.enums.compression).map(k => openpgp.enums.compression[k]);
|
||||||
|
|
||||||
|
@ -1058,14 +1103,14 @@ module.exports = () => describe('OpenPGP.js public api tests', function() {
|
||||||
});
|
});
|
||||||
expect(key.isDecrypted()).to.be.true;
|
expect(key.isDecrypted()).to.be.true;
|
||||||
expect(locked.isDecrypted()).to.be.false;
|
expect(locked.isDecrypted()).to.be.false;
|
||||||
expect(locked.primaryKey.isDummy()).to.be.true;
|
expect(locked.keyPacket.isDummy()).to.be.true;
|
||||||
const unlocked = await openpgp.decryptKey({
|
const unlocked = await openpgp.decryptKey({
|
||||||
privateKey: locked,
|
privateKey: locked,
|
||||||
passphrase: passphrase
|
passphrase: passphrase
|
||||||
});
|
});
|
||||||
expect(key.isDecrypted()).to.be.true;
|
expect(key.isDecrypted()).to.be.true;
|
||||||
expect(unlocked.isDecrypted()).to.be.true;
|
expect(unlocked.isDecrypted()).to.be.true;
|
||||||
expect(unlocked.primaryKey.isDummy()).to.be.true;
|
expect(unlocked.keyPacket.isDummy()).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1246,6 +1291,60 @@ module.exports = () => describe('OpenPGP.js public api tests', function() {
|
||||||
stream.readToEnd(streamedData)
|
stream.readToEnd(streamedData)
|
||||||
).to.be.eventually.rejectedWith(/Could not find signing key/);
|
).to.be.eventually.rejectedWith(/Could not find signing key/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Supports decrypting with GnuPG dummy key', async function() {
|
||||||
|
const { rejectMessageHashAlgorithms } = openpgp.config;
|
||||||
|
Object.assign(openpgp.config, { rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) });
|
||||||
|
try {
|
||||||
|
const armoredMessage = `-----BEGIN PGP MESSAGE-----
|
||||||
|
Version: GnuPG v1.4.11 (GNU/Linux)
|
||||||
|
|
||||||
|
hQEOA1N4OCSSjECBEAP/diDJCQn4e88193PgqhbfAkohk9RQ0v0MPnXpJbCRTHKO
|
||||||
|
8r9nxiAr/TQv4ZOingXdAp2JZEoE9pXxZ3r1UWew04czxmgJ8FP1ztZYWVFAWFVi
|
||||||
|
Tj930TBD7L1fY/MD4fK6xjEG7z5GT8k4tn4mLm/PpWMbarIglfMopTy1M/py2cID
|
||||||
|
/2Sj7Ikh3UFiG+zm4sViYc5roNbMy8ixeoKixxi99Mx8INa2cxNfqbabjblFyc0Z
|
||||||
|
BwmbIc+ZiY2meRNI5y/tk0gRD7hT84IXGGl6/mH00bsX/kkWdKGeTvz8s5G8RDHa
|
||||||
|
Za4HgLbXItkX/QarvRS9kvkD01ujHfj+1ZvgmOBttNfP0p8BQLIICqvg1eYD9aPB
|
||||||
|
+GtOZ2F3+k5VyBL5yIn/s65SBjNO8Fqs3aL0x+p7s1cfUzx8J8a8nWpqq/qIQIqg
|
||||||
|
ZJH6MZRKuQwscwH6NWgsSVwcnVCAXnYOpbHxFQ+j7RbF/+uiuqU+DFH/Rd5pik8b
|
||||||
|
0Dqnp0yfefrkjQ0nuvubgB6Rv89mHpnvuJfFJRInpg4lrHwLvRwdpN2HDozFHcKK
|
||||||
|
aOU=
|
||||||
|
=4iGt
|
||||||
|
-----END PGP MESSAGE-----`;
|
||||||
|
const passphrase = 'abcd';
|
||||||
|
// exercises the GnuPG s2k type 1001 extension:
|
||||||
|
// the secrets on the primary key have been stripped.
|
||||||
|
const dummyKey = await openpgp.readKey({ armoredKey: armoredDummyPrivateKey1 });
|
||||||
|
const publicKey = await openpgp.readKey({ armoredKey: armoredPublicKey1 });
|
||||||
|
const message = await openpgp.readMessage({ armoredMessage });
|
||||||
|
const primaryKeyPacket = dummyKey.keyPacket.write();
|
||||||
|
expect(dummyKey.isDecrypted()).to.be.false;
|
||||||
|
const decryptedDummyKey = await openpgp.decryptKey({ privateKey: dummyKey, passphrase });
|
||||||
|
expect(decryptedDummyKey.isDecrypted()).to.be.true;
|
||||||
|
// decrypting with a secret subkey works
|
||||||
|
const msg = await openpgp.decrypt({
|
||||||
|
message, decryptionKeys: decryptedDummyKey, verificationKeys: publicKey, config: { rejectPublicKeyAlgorithms: new Set() }
|
||||||
|
});
|
||||||
|
expect(msg.signatures).to.exist;
|
||||||
|
expect(msg.signatures).to.have.length(1);
|
||||||
|
expect(msg.signatures[0].valid).to.be.true;
|
||||||
|
expect(msg.signatures[0].signature.packets.length).to.equal(1);
|
||||||
|
// secret key operations involving the primary key should fail
|
||||||
|
await expect(openpgp.sign({
|
||||||
|
message: await openpgp.createMessage({ text: 'test' }), signingKeys: decryptedDummyKey, config: { rejectPublicKeyAlgorithms: new Set() }
|
||||||
|
})).to.eventually.be.rejectedWith(/Cannot sign with a gnu-dummy key/);
|
||||||
|
await expect(
|
||||||
|
openpgp.reformatKey({ userIDs: { name: 'test' }, privateKey: decryptedDummyKey })
|
||||||
|
).to.eventually.be.rejectedWith(/Cannot reformat a gnu-dummy primary key/);
|
||||||
|
|
||||||
|
const encryptedDummyKey = await openpgp.encryptKey({ privateKey: decryptedDummyKey, passphrase });
|
||||||
|
expect(encryptedDummyKey.isDecrypted()).to.be.false;
|
||||||
|
const primaryKeyPacket2 = encryptedDummyKey.keyPacket.write();
|
||||||
|
expect(primaryKeyPacket).to.deep.equal(primaryKeyPacket2);
|
||||||
|
} finally {
|
||||||
|
Object.assign(openpgp.config, { rejectMessageHashAlgorithms });
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('verify - unit tests', function() {
|
describe('verify - unit tests', function() {
|
||||||
|
@ -1262,6 +1361,86 @@ module.exports = () => describe('OpenPGP.js public api tests', function() {
|
||||||
|
|
||||||
describe('message', function() {
|
describe('message', function() {
|
||||||
verifyTests(false);
|
verifyTests(false);
|
||||||
|
|
||||||
|
it('verify should succeed with valid signature (expectSigned=true, with streaming)', async function () {
|
||||||
|
const publicKey = await openpgp.readKey({ armoredKey: pub_key });
|
||||||
|
const privateKey = await openpgp.decryptKey({
|
||||||
|
privateKey: await openpgp.readKey({ armoredKey: priv_key }),
|
||||||
|
passphrase
|
||||||
|
});
|
||||||
|
|
||||||
|
const signed = await openpgp.sign({
|
||||||
|
message: await openpgp.createMessage({ text: plaintext }),
|
||||||
|
signingKeys: privateKey
|
||||||
|
});
|
||||||
|
const { data: streamedData, signatures } = await openpgp.verify({
|
||||||
|
message: await openpgp.readMessage({ armoredMessage: stream.toStream(signed) }),
|
||||||
|
verificationKeys: publicKey,
|
||||||
|
expectSigned: true
|
||||||
|
});
|
||||||
|
const data = await stream.readToEnd(streamedData);
|
||||||
|
expect(data).to.equal(plaintext);
|
||||||
|
expect(await signatures[0].verified).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verify should throw on missing signature (expectSigned=true, with streaming)', async function () {
|
||||||
|
|
||||||
|
const publicKey = await openpgp.readKey({ armoredKey: pub_key });
|
||||||
|
|
||||||
|
await expect(openpgp.verify({
|
||||||
|
message: await openpgp.createMessage({ text: stream.toStream(plaintext) }),
|
||||||
|
verificationKeys: publicKey,
|
||||||
|
expectSigned: true
|
||||||
|
})).to.be.eventually.rejectedWith(/Message is not signed/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verify should throw on invalid signature (expectSigned=true, with streaming)', async function () {
|
||||||
|
const wrongPublicKey = (await openpgp.readKey({ armoredKey: priv_key_2000_2008 })).toPublic();
|
||||||
|
const privateKey = await openpgp.decryptKey({
|
||||||
|
privateKey: await openpgp.readKey({ armoredKey: priv_key }),
|
||||||
|
passphrase
|
||||||
|
});
|
||||||
|
|
||||||
|
const signed = await openpgp.sign({
|
||||||
|
message: await openpgp.createMessage({ text: plaintext }),
|
||||||
|
signingKeys: privateKey
|
||||||
|
});
|
||||||
|
const { data: streamedData } = await openpgp.verify({
|
||||||
|
message: await openpgp.readMessage({ armoredMessage: stream.toStream(signed) }),
|
||||||
|
verificationKeys: wrongPublicKey,
|
||||||
|
expectSigned: true
|
||||||
|
});
|
||||||
|
await expect(
|
||||||
|
stream.readToEnd(streamedData)
|
||||||
|
).to.be.eventually.rejectedWith(/Could not find signing key/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('verify should fail if the signature is re-used with a different message', async function () {
|
||||||
|
const privateKey = await openpgp.decryptKey({
|
||||||
|
privateKey: await openpgp.readKey({ armoredKey: priv_key }),
|
||||||
|
passphrase
|
||||||
|
});
|
||||||
|
|
||||||
|
const message = await openpgp.createMessage({ text: 'a message' });
|
||||||
|
const armoredSignature = await openpgp.sign({
|
||||||
|
message,
|
||||||
|
signingKeys: privateKey,
|
||||||
|
detached: true
|
||||||
|
});
|
||||||
|
const { signatures } = await openpgp.verify({
|
||||||
|
message,
|
||||||
|
signature: await openpgp.readSignature({ armoredSignature }),
|
||||||
|
verificationKeys: privateKey.toPublic()
|
||||||
|
});
|
||||||
|
expect(await signatures[0].verified).to.be.true;
|
||||||
|
// pass a different message
|
||||||
|
await expect(openpgp.verify({
|
||||||
|
message: await openpgp.createMessage({ text: 'a different message' }),
|
||||||
|
signature: await openpgp.readSignature({ armoredSignature }),
|
||||||
|
verificationKeys: privateKey.toPublic(),
|
||||||
|
expectSigned: true
|
||||||
|
})).to.be.rejectedWith(/digest did not match/);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('cleartext message', function() {
|
describe('cleartext message', function() {
|
||||||
|
@ -1324,67 +1503,17 @@ module.exports = () => describe('OpenPGP.js public api tests', function() {
|
||||||
expectSigned: true
|
expectSigned: true
|
||||||
})).to.be.eventually.rejectedWith(/Could not find signing key/);
|
})).to.be.eventually.rejectedWith(/Could not find signing key/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('verify should succeed with valid signature (expectSigned=true, with streaming)', async function () {
|
|
||||||
if (useCleartext) this.skip(); // eslint-disable-line no-invalid-this
|
|
||||||
|
|
||||||
const publicKey = await openpgp.readKey({ armoredKey: pub_key });
|
|
||||||
const privateKey = await openpgp.decryptKey({
|
|
||||||
privateKey: await openpgp.readKey({ armoredKey: priv_key }),
|
|
||||||
passphrase
|
|
||||||
});
|
|
||||||
|
|
||||||
const signed = await openpgp.sign({
|
|
||||||
message: await createMessage({ text }),
|
|
||||||
signingKeys: privateKey
|
|
||||||
});
|
|
||||||
const { data: streamedData, signatures } = await openpgp.verify({
|
|
||||||
message: await readMessage({ armoredMessage: stream.toStream(signed) }),
|
|
||||||
verificationKeys: publicKey,
|
|
||||||
expectSigned: true
|
|
||||||
});
|
|
||||||
const data = await stream.readToEnd(streamedData);
|
|
||||||
expect(data).to.equal(text);
|
|
||||||
expect(await signatures[0].verified).to.be.true;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('verify should throw on missing signature (expectSigned=true, with streaming)', async function () {
|
|
||||||
if (useCleartext) this.skip(); // eslint-disable-line no-invalid-this
|
|
||||||
|
|
||||||
const publicKey = await openpgp.readKey({ armoredKey: pub_key });
|
|
||||||
|
|
||||||
await expect(openpgp.verify({
|
|
||||||
message: await createMessage({ text: stream.toStream(text) }),
|
|
||||||
verificationKeys: publicKey,
|
|
||||||
expectSigned: true
|
|
||||||
})).to.be.eventually.rejectedWith(/Message is not signed/);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('verify should throw on invalid signature (expectSigned=true, with streaming)', async function () {
|
|
||||||
if (useCleartext) this.skip(); // eslint-disable-line no-invalid-this
|
|
||||||
|
|
||||||
const wrongPublicKey = (await openpgp.readKey({ armoredKey: priv_key_2000_2008 })).toPublic();
|
|
||||||
const privateKey = await openpgp.decryptKey({
|
|
||||||
privateKey: await openpgp.readKey({ armoredKey: priv_key }),
|
|
||||||
passphrase
|
|
||||||
});
|
|
||||||
|
|
||||||
const signed = await openpgp.sign({
|
|
||||||
message: await createMessage({ text }),
|
|
||||||
signingKeys: privateKey
|
|
||||||
});
|
|
||||||
const { data: streamedData } = await openpgp.verify({
|
|
||||||
message: await readMessage({ armoredMessage: stream.toStream(signed) }),
|
|
||||||
verificationKeys: wrongPublicKey,
|
|
||||||
expectSigned: true
|
|
||||||
});
|
|
||||||
await expect(
|
|
||||||
stream.readToEnd(streamedData)
|
|
||||||
).to.be.eventually.rejectedWith(/Could not find signing key/);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('sign - unit tests', function() {
|
||||||
|
it('Supports signing with GnuPG dummy key', async function() {
|
||||||
|
const dummyKey = await openpgp.readKey({ armoredKey: gnuDummyKeySigningSubkey });
|
||||||
|
const sig = await openpgp.sign({ message: await openpgp.createMessage({ text: 'test' }), privateKeys: dummyKey, date: new Date('2018-12-17T03:24:00') });
|
||||||
|
expect(sig).to.match(/-----END PGP MESSAGE-----\n$/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('encrypt, decrypt, sign, verify - integration tests', function() {
|
describe('encrypt, decrypt, sign, verify - integration tests', function() {
|
||||||
let privateKey_2000_2008;
|
let privateKey_2000_2008;
|
||||||
let publicKey_2000_2008;
|
let publicKey_2000_2008;
|
||||||
|
@ -3043,7 +3172,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() {
|
||||||
privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
|
privateKey: await openpgp.readKey({ armoredKey: priv_key_de }),
|
||||||
passphrase
|
passphrase
|
||||||
});
|
});
|
||||||
return privKeyDE.subKeys[0].revoke(privKeyDE.primaryKey).then(async function(revSubKey) {
|
return privKeyDE.subKeys[0].revoke(privKeyDE.keyPacket).then(async function(revSubKey) {
|
||||||
pubKeyDE.subKeys[0] = revSubKey;
|
pubKeyDE.subKeys[0] = revSubKey;
|
||||||
return openpgp.encrypt({
|
return openpgp.encrypt({
|
||||||
message: await openpgp.createMessage({ text: plaintext }),
|
message: await openpgp.createMessage({ text: plaintext }),
|
||||||
|
@ -3068,7 +3197,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() {
|
||||||
encryptionKeys: pubKeyDE,
|
encryptionKeys: pubKeyDE,
|
||||||
config: { rejectPublicKeyAlgorithms: new Set() }
|
config: { rejectPublicKeyAlgorithms: new Set() }
|
||||||
});
|
});
|
||||||
privKeyDE.subKeys[0] = await privKeyDE.subKeys[0].revoke(privKeyDE.primaryKey);
|
privKeyDE.subKeys[0] = await privKeyDE.subKeys[0].revoke(privKeyDE.keyPacket);
|
||||||
const decOpt = {
|
const decOpt = {
|
||||||
message: await openpgp.readMessage({ armoredMessage: encrypted }),
|
message: await openpgp.readMessage({ armoredMessage: encrypted }),
|
||||||
decryptionKeys: privKeyDE,
|
decryptionKeys: privKeyDE,
|
||||||
|
@ -3441,5 +3570,4 @@ bsZgJWVlAa5eil6J9ePX2xbo1vVAkLQdzE9+1jL+l7PRIZuVBQ==
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -706,15 +706,13 @@ module.exports = () => describe("Packet", function() {
|
||||||
it('Secret key reading with signature verification.', async function() {
|
it('Secret key reading with signature verification.', async function() {
|
||||||
const packets = await openpgp.PacketList.fromBinary((await openpgp.unarmor(armored_key)).data, allAllowedPackets);
|
const packets = await openpgp.PacketList.fromBinary((await openpgp.unarmor(armored_key)).data, allAllowedPackets);
|
||||||
const [keyPacket, userIDPacket, keySigPacket, subkeyPacket, subkeySigPacket] = packets;
|
const [keyPacket, userIDPacket, keySigPacket, subkeyPacket, subkeySigPacket] = packets;
|
||||||
expect(keySigPacket.verified).to.be.null;
|
|
||||||
expect(subkeySigPacket.verified).to.be.null;
|
|
||||||
|
|
||||||
await keySigPacket.verify(
|
await keySigPacket.verify(
|
||||||
keyPacket, openpgp.enums.signature.certGeneric, { userID: userIDPacket, key: keyPacket }
|
keyPacket, openpgp.enums.signature.certGeneric, { userID: userIDPacket, key: keyPacket }
|
||||||
).then(async () => expect(keySigPacket.verified).to.be.true);
|
);
|
||||||
await subkeySigPacket.verify(
|
await subkeySigPacket.verify(
|
||||||
keyPacket, openpgp.enums.signature.keyBinding, { key: keyPacket, bind: subkeyPacket }
|
keyPacket, openpgp.enums.signature.keyBinding, { key: keyPacket, bind: subkeyPacket }
|
||||||
).then(async () => expect(subkeySigPacket.verified).to.be.true);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Reading a signed, encrypted message.', async function() {
|
it('Reading a signed, encrypted message.', async function() {
|
||||||
|
|
|
@ -41,34 +41,6 @@ module.exports = () => describe("Signature", function() {
|
||||||
'=LSrW',
|
'=LSrW',
|
||||||
'-----END PGP PRIVATE KEY BLOCK-----'].join("\n");
|
'-----END PGP PRIVATE KEY BLOCK-----'].join("\n");
|
||||||
|
|
||||||
const priv_key_arm1_stripped =
|
|
||||||
['-----BEGIN PGP PRIVATE KEY BLOCK-----',
|
|
||||||
'Version: GnuPG v1.4.11 (GNU/Linux)',
|
|
||||||
'',
|
|
||||||
'lQGqBFERnrMRBADmM0hIfkI3yosjgbWo9v0Lnr3CCE+8KsMszgVS+hBu0XfGraKm',
|
|
||||||
'ivcA2aaJimHqVYOP7gEnwFAxHBBpeTJcu5wzCFyJwEYqVeS3nnaIhBPplSF14Duf',
|
|
||||||
'i6bB9RV7KxVAg6aunmM2tAutqC+a0y2rDaf7jkJoZ9gWJe2zI+vraD6fiwCgxvHo',
|
|
||||||
'3IgULB9RqIqpLoMgXfcjC+cD/1jeJlKRm+n71ryYwT/ECKsspFz7S36z6q3XyS8Q',
|
|
||||||
'QfrsUz2p1fbFicvJwIOJ8B20J/N2/nit4P0gBUTUxv3QEa7XCM/56/xrGkyBzscW',
|
|
||||||
'AzBoy/AK9K7GN6z13RozuAS60F1xO7MQc6Yi2VU3eASDQEKiyL/Ubf/s/rkZ+sGj',
|
|
||||||
'yJizBACtwCbQzA+z9XBZNUat5NPgcZz5Qeh1nwF9Nxnr6pyBv7tkrLh/3gxRGHqG',
|
|
||||||
'063dMbUk8pmUcJzBUyRsNiIPDoEUsLjY5zmZZmp/waAhpREsnK29WLCbqLdpUors',
|
|
||||||
'c1JJBsObkA1IM8TZY8YUmvsMEvBLCCanuKpclZZXqeRAeOHJ0v4DZQJHTlUBtBZU',
|
|
||||||
'ZXN0MiA8dGVzdDJAdGVzdC5jb20+iGIEExECACIFAlERnrMCGwMGCwkIBwMCBhUI',
|
|
||||||
'AgkKCwQWAgMBAh4BAheAAAoJEBEnlAPLFp74xc0AoLNZINHe0ytOsNtMCuLvc3Vd',
|
|
||||||
'vePUAJ9KX3L5IBqHarsa+aJHX7r796SokZ0BWARREZ6zEAQA2WkxmNbfeMzGUocN',
|
|
||||||
'3JEVe0o6rxGt5eGrTSmWisduDP3MURabhUXnf4T8oaeYcbJjkLLxMrJmNq55ln1e',
|
|
||||||
'4bSG5mDkh/ryKsV81m3F0DbqO/z/891nRSP5fondFVral4wsMOzBNgs4vVk7V/F2',
|
|
||||||
'0MPjR90CIhnVDKPAQbQA+3PjUR8AAwUEALn922AEE+0d7xSMMFpR7ic3Me5QEGnp',
|
|
||||||
'cT4ft6oc0UK5kAnvKoksZUc0hpBHjX1w3LTz847/5hRDuuDvwvGMWK8IfsjOF9T7',
|
|
||||||
'rK8QtJuBEyJxjoScA/YZP5vX4y0U1reUEa0EdwmVrnZzatMAe2FhlaR9PlHkOcm5',
|
|
||||||
'DZwkcExL0dbI/gMDArxZ+5N7kH4zYLtr9glJS/pJ7F0YJqJpNwCbqD8+8DqHD8Uv',
|
|
||||||
'MgQ/rtBxBJJOaF+1AjCd123hLgzIkkfdTh8loV9hDXMKeJgmiEkEGBECAAkFAlER',
|
|
||||||
'nrMCGwwACgkQESeUA8sWnvhBswCfdXjznvHCc73/6/MhWcv3dbeTT/wAoLyiZg8+',
|
|
||||||
'iY3UT9QkV9d0sMgyLkug',
|
|
||||||
'=GQsY',
|
|
||||||
'-----END PGP PRIVATE KEY BLOCK-----'].join("\n");
|
|
||||||
|
|
||||||
const pub_key_arm1 =
|
const pub_key_arm1 =
|
||||||
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
||||||
'Version: GnuPG v1.4.11 (GNU/Linux)',
|
'Version: GnuPG v1.4.11 (GNU/Linux)',
|
||||||
|
@ -254,61 +226,6 @@ module.exports = () => describe("Signature", function() {
|
||||||
'=ok+o',
|
'=ok+o',
|
||||||
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
|
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
|
||||||
|
|
||||||
const pub_expired =
|
|
||||||
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
|
||||||
'Comment: GPGTools - https://gpgtools.org',
|
|
||||||
'',
|
|
||||||
'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',
|
|
||||||
'2g==',
|
|
||||||
'=X7/F',
|
|
||||||
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
|
|
||||||
|
|
||||||
const pub_latin1_msg = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
const pub_latin1_msg = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
mQINBFS6eEEBEAC56tAm82tgg5BJE0dA4c5UNUDQ7SKLIsleh7TrwsKocEp1b34E
|
mQINBFS6eEEBEAC56tAm82tgg5BJE0dA4c5UNUDQ7SKLIsleh7TrwsKocEp1b34E
|
||||||
|
@ -663,171 +580,56 @@ Blk+CJ7ytHy6En8542bB/yC+Z9/zWbVuhg==
|
||||||
=jmT1
|
=jmT1
|
||||||
-----END PGP PUBLIC KEY BLOCK-----`;
|
-----END PGP PUBLIC KEY BLOCK-----`;
|
||||||
|
|
||||||
const msg_sig_expired = [
|
const keyExpiredBindingSig = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
'-----BEGIN PGP MESSAGE-----',
|
|
||||||
'Comment: GPGTools - https://gpgtools.org',
|
|
||||||
'',
|
|
||||||
'owEBWwKk/ZANAwAKAeyAexA3gWZ0AawUYgloZWxsby50eHRaX2WpaGVsbG+JAjME',
|
|
||||||
'AAEKAB0WIQTxcIn7ZRrhwd51IZbsgHsQN4FmdAUCWl9lqQAKCRDsgHsQN4FmdCln',
|
|
||||||
'D/44x1bcrOXg+DbRStSrC75wFa+cvPEmaTZyqN6d7qlQCMxOcPlq6lbZ74QWfEq7',
|
|
||||||
'i1ZYHp4AU8jALw0QqBQQE5FvABleQKpVfY22s83Bqy+P0DB9ntpD+t+oZrxGCLmL',
|
|
||||||
'MbZJNFnGro48gHt+/OQKLuftiVwE2opHfgogVKNL74FmYA0hMItdzpn4OPNFkP8t',
|
|
||||||
'Iq/m0hkXlTAKqBPITVLv1FN16v+Sm1iC317eP/HOTYqVZdJN3svVF8ZBfg29a8p6',
|
|
||||||
'6nl67fZhXgrt0OB6KSNIZEwMTWjFAqi365mtTssqAA0un94+cQ/WvAC5QcMM8g5S',
|
|
||||||
'i3G7vny9AsXor+GDU1z7UDWs3wBV4mVRdj7bBIS6PK+6oe012aNpRObcI2bU2BT/',
|
|
||||||
'H/7uHZWfwEmpfvH9RVZgoeETA3vSx7MDrNyDt3gwv2hxOHEd7nnVQ3EKG33173o1',
|
|
||||||
'/5/oEmn2USujKGhHJ2Zo3aWNRuUWZlvBaYw+PwB2R0UiuJbi0KofNYPssNdpw4sg',
|
|
||||||
'Qs7Nb2/Ilo1zn5bDh+WDrUrn6zHKAfBytBPpwPFWPZ8W10HUlC5vMZSKH5/UZhj5',
|
|
||||||
'kLlUC1zKjFPpRhO27ImTJuImil4lR2/CFjB1duG3JGJQaYIq8RFJOjvTVY29wl0i',
|
|
||||||
'pFy6y1Ofv2lLHB9K7N7dvvee2nvpUMkLEL52oFQ6Jc7sdg==',
|
|
||||||
'=Q4tk',
|
|
||||||
'-----END PGP MESSAGE-----'
|
|
||||||
].join('\n');
|
|
||||||
|
|
||||||
const flowcrypt_stripped_key = [
|
xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv
|
||||||
'-----BEGIN PGP PRIVATE KEY BLOCK-----',
|
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',
|
hW1Hj9AO8lzggBQ=
|
||||||
'rfSRYjuZpgO3cIJdDvGXv0vnuF2p9Z83pz3FS3dx33Weiss30pBt5pCvZKT8SAQp',
|
=Nt+N
|
||||||
'ityaxxYtDDb1t0fKmd59DByNfLaHl9pOPIs6adYL8ojFA2Qhd4walTl2+nkuWz9A',
|
-----END PGP PUBLIC KEY BLOCK-----`;
|
||||||
'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',
|
|
||||||
'=NbaL',
|
|
||||||
'-----END PGP PRIVATE KEY BLOCK-----'
|
|
||||||
].join("\n");
|
|
||||||
|
|
||||||
const signature_with_critical_notation = `-----BEGIN PGP MESSAGE-----
|
const signature_with_critical_notation = `-----BEGIN PGP MESSAGE-----
|
||||||
|
|
||||||
|
@ -848,6 +650,24 @@ hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw==
|
||||||
-----END PGP SIGNATURE-----
|
-----END PGP SIGNATURE-----
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
it('Throws when reading a signature missing the creation time', async function () {
|
||||||
|
const armoredSignature = `-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
|
wsDtBAABCAAXFiEE0aZuGiOxgsmYD3iM+/zIKgFeczAACgkQ+/zIKgFeczDjiwv+
|
||||||
|
LFUWJohCYtauaVDHBDHWF+tojls+ducY6uuU6iUTBb1969okh2sjUmvPwIjrVXuk
|
||||||
|
cfPl616xRqVWolEU9T5sG6MjRlAaG31Oo/FVAVFXZn30ldEtuDss12+/IhES3Wfx
|
||||||
|
M1jGdJZ1ZMZJpRsNBJXBegEBFKbPleEd66seuuFfvoIUbgsdj7IT/ZlEMlixelWW
|
||||||
|
igXPVY1C05oPbkC8oo0lVSxwdq6gDvm8a52k3GCtXJELrYGH29C+eDqmyLP1zJOt
|
||||||
|
NBoZBAqMd9XYVrJtuip436D9pdo5pbg4zCE6uPf2zzx4taK7jGkk6nn7LqVDxvQm
|
||||||
|
3dAXUnIxw4V9eL3V8SFAKwmouUmHPRbjfnQ70hxYQxDXUcIwu1aYn13QS1s/F/jf
|
||||||
|
DVRZWaAhNdL9BfHfhEsRVsmjMhe0zwRpaepvXnERbnA/lAUHEmEvgfPFz/2GsAo/
|
||||||
|
kCNcH9WI6idSzFjuYegECf+ZA1xOCjS9oLTGbSeT7jNfC8dH5+E92qlBLq4Ctt7k
|
||||||
|
=lMU7
|
||||||
|
-----END PGP SIGNATURE-----`;
|
||||||
|
|
||||||
|
await expect(openpgp.readSignature({ armoredSignature })).to.be.rejectedWith(/Missing signature creation time/);
|
||||||
|
});
|
||||||
|
|
||||||
it('Testing signature checking on CAST5-enciphered message', async function() {
|
it('Testing signature checking on CAST5-enciphered message', async function() {
|
||||||
const publicKey = await openpgp.readKey({ armoredKey: pub_key_arm1 });
|
const publicKey = await openpgp.readKey({ armoredKey: pub_key_arm1 });
|
||||||
const privateKey = await openpgp.decryptKey({
|
const privateKey = await openpgp.decryptKey({
|
||||||
|
@ -865,50 +685,180 @@ hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw==
|
||||||
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
|
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Supports decrypting with GnuPG dummy key', async function() {
|
it('Signing fails if primary key is expired', async function() {
|
||||||
const { rejectMessageHashAlgorithms } = openpgp.config;
|
const armoredExpiredKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
Object.assign(openpgp.config, { rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) });
|
|
||||||
try {
|
|
||||||
const passphrase = 'abcd';
|
|
||||||
// exercises the GnuPG s2k type 1001 extension:
|
|
||||||
// the secrets on the primary key have been stripped.
|
|
||||||
const dummyKey = await openpgp.readKey({ armoredKey: priv_key_arm1_stripped });
|
|
||||||
const publicKey = await openpgp.readKey({ armoredKey: pub_key_arm1 });
|
|
||||||
const message = await openpgp.readMessage({ armoredMessage: msg_arm1 });
|
|
||||||
const primaryKeyPacket = dummyKey.primaryKey.write();
|
|
||||||
expect(dummyKey.isDecrypted()).to.be.false;
|
|
||||||
const decryptedDummyKey = await openpgp.decryptKey({ privateKey: dummyKey, passphrase });
|
|
||||||
expect(decryptedDummyKey.isDecrypted()).to.be.true;
|
|
||||||
// decrypting with a secret subkey works
|
|
||||||
const msg = await openpgp.decrypt({ message, decryptionKeys: decryptedDummyKey, verificationKeys: publicKey, config: { rejectPublicKeyAlgorithms: new Set() } });
|
|
||||||
expect(msg.signatures).to.exist;
|
|
||||||
expect(msg.signatures).to.have.length(1);
|
|
||||||
expect(msg.signatures[0].valid).to.be.true;
|
|
||||||
expect(msg.signatures[0].signature.packets.length).to.equal(1);
|
|
||||||
// secret key operations involving the primary key should fail
|
|
||||||
await expect(openpgp.sign({
|
|
||||||
message: await openpgp.createMessage({ text: 'test' }), signingKeys: decryptedDummyKey, config: { rejectPublicKeyAlgorithms: new Set() }
|
|
||||||
})).to.eventually.be.rejectedWith(/Cannot sign with a gnu-dummy key/);
|
|
||||||
await expect(
|
|
||||||
openpgp.reformatKey({ userIDs: { name: 'test' }, privateKey: decryptedDummyKey })
|
|
||||||
).to.eventually.be.rejectedWith(/Cannot reformat a gnu-dummy primary key/);
|
|
||||||
|
|
||||||
const encryptedDummyKey = await openpgp.encryptKey({ privateKey: decryptedDummyKey, passphrase });
|
xVgEYKKPDRYJKwYBBAHaRw8BAQdAwJcSQMkHVnZPesPJP1JaB9ptV+wG8Io1
|
||||||
expect(encryptedDummyKey.isDecrypted()).to.be.false;
|
vxRKvXQe0wMAAP0fdn6gvpVwFUE4bIRcn9hx6eDxSxUu+tg/t959Oo+iahF1
|
||||||
const primaryKeyPacket2 = encryptedDummyKey.primaryKey.write();
|
zRB0ZXN0IDx0ZXN0QGEuaXQ+wpIEEBYKACMFAmCijw0FCQAAAAEECwkHCAMV
|
||||||
expect(primaryKeyPacket).to.deep.equal(primaryKeyPacket2);
|
CAoEFgACAQIZAQIbAwIeAQAhCRD16pevybCusRYhBHjm9svlAjmgVWL4wvXq
|
||||||
} finally {
|
l6/JsK6xGUQBAPzxKS2Qs+vWGpxPT2N2T+PLHIgCOxVJVngj4fzREFH1AP9t
|
||||||
Object.assign(openpgp.config, { rejectMessageHashAlgorithms });
|
wP+fn3eSsik+vFGy93REmlD1xdu7nW/sHuxY4roqBcddBGCijw0SCisGAQQB
|
||||||
}
|
l1UBBQEBB0Cl1lr+aHfy6V4ePmZUULK6VKTCTPTMaPpR2TzKNIJQBQMBCAcA
|
||||||
|
AP9DZWRqQLCIkF38Q0UC/YXLCDdBEQdnlwpHgA0W1bSWmA3uwn4EGBYIAA8F
|
||||||
|
AmCijw0FCQAAAAECGwwAIQkQ9eqXr8mwrrEWIQR45vbL5QI5oFVi+ML16pev
|
||||||
|
ybCusYE4AQCYbXw8ZWoMevbOM7lAttkwyrG3V/nTW6BVo7/M9Pr9swEA0mDI
|
||||||
|
DQmhI0SZoTKy4EGhS0bNJ+g2+dJ8Y22fKzLWXwo=
|
||||||
|
=qiIN
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----`;
|
||||||
|
const key = await openpgp.readKey({ armoredKey: armoredExpiredKey });
|
||||||
|
await expect(openpgp.sign({
|
||||||
|
signingKeys: key,
|
||||||
|
message: await openpgp.createMessage({ text: 'Hello World' })
|
||||||
|
})).to.be.rejectedWith(/key is expired/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Supports signing with GnuPG dummy key', async function() {
|
it('Signing fails if the signing date is before the key creation date', async function() {
|
||||||
const dummyKey = await openpgp.decryptKey({
|
const key = await openpgp.decryptKey({
|
||||||
privateKey: await openpgp.readKey({ armoredKey: flowcrypt_stripped_key }),
|
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
|
||||||
passphrase: 'FlowCrypt'
|
passphrase: 'hello world'
|
||||||
});
|
});
|
||||||
const sig = await openpgp.sign({ message: await openpgp.createMessage({ text: 'test' }), signingKeys: dummyKey, date: new Date('2018-12-17T03:24:00') });
|
await expect(openpgp.sign({
|
||||||
expect(sig).to.match(/-----END PGP MESSAGE-----\n$/);
|
signingKeys: key,
|
||||||
|
date: new Date(key.keyPacket.created - 3600),
|
||||||
|
message: await openpgp.createMessage({ text: 'Hello World' })
|
||||||
|
})).to.be.rejectedWith(/Signature creation time is in the future/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Verification fails if primary key binding signature is expired', async function() {
|
||||||
|
const armoredSignature = `-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
|
wsDzBAABCgAGBYJfLAFsACEJEHwvqk35PDeyFiEEHdzhXwkhfO4vOzdgfC+qTfk8
|
||||||
|
N7KiqwwAts4QGB7v9bABCC2qkTxJhmStC0wQMcHRcjL/qAiVnmasQWmvE9KVsdm3
|
||||||
|
AaXd8mIx4a37/RRvr9dYrY2eE4uw72cMqPxNja2tvVXkHQvk1oEUqfkvbXs4ypKI
|
||||||
|
NyeTWjXNOTZEbg0hbm3nMy+Wv7zgB1CEvAsEboLDJlhGqPcD+X8a6CJGrBGUBUrv
|
||||||
|
KVmZr3U6vEzClz3DBLpoddCQseJRhT4YM1nKmBlZ5quh2LFgTSpajv5OsZheqt9y
|
||||||
|
EZAPbqmLhDmWRQwGzkWHKceKS7nZ/ox2WK6OS7Ob8ZGZkM64iPo6/EGj5Yc19vQN
|
||||||
|
AGiIaPEGszBBWlOpHTPhNm0LB0nMWqqaT87oNYwP8CQuuxDb6rKJ2lffCmZH27Lb
|
||||||
|
UbQZcH8J+0UhpeaiadPZxH5ATJAcenmVtVVMLVOFnm+eIlxzov9ntpgGYt8hLdXB
|
||||||
|
ITEG9mMgp3TGS9ZzSifMZ8UGtHdp9QdBg8NEVPFzDOMGxpc/Bftav7RRRuPiAER+
|
||||||
|
7A5CBid5
|
||||||
|
=aQkm
|
||||||
|
-----END PGP SIGNATURE-----`;
|
||||||
|
const key = await openpgp.readKey({ armoredKey: keyExpiredBindingSig });
|
||||||
|
const signature = await openpgp.readSignature({ armoredSignature });
|
||||||
|
const { signatures: [sigInfo] } = await openpgp.verify({
|
||||||
|
verificationKeys: key,
|
||||||
|
message: await openpgp.createMessage({ text: 'Hello World :)' }),
|
||||||
|
signature
|
||||||
|
});
|
||||||
|
await expect(sigInfo.verified).to.be.rejectedWith(/Signature is expired/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Verification fails if signing key was already expired at the time of signing (one-pass signature, streamed)', async function() {
|
||||||
|
const armoredMessage = `-----BEGIN PGP MESSAGE-----
|
||||||
|
|
||||||
|
xA0DAQgB4IT3RGwgLJcByxR1AGCc8BxIZWxsbyBXb3JsZCA6KcKzBAEBCAAG
|
||||||
|
BQJgnPAcACEJEOCE90RsICyXFiEE19Gx3sbKlGcANEXF4IT3RGwgLJdssAP+
|
||||||
|
KpyVi30z5iMckULAQ3Q34IB29Gxa1/99ABSld6iIVGRCfmZZlS5UGcxJJGoL
|
||||||
|
vZ1RAL4YQx/GLV1dBcKWFwzb5G2/ip4coDMCDGTAwnwjcPwjHpfMQ9gX59mG
|
||||||
|
AkLaG/AkATpuH+DMkYDmKbDLGgD+N4yuxXBJmBfC2IBe4J1S2Gg=
|
||||||
|
=zvNP
|
||||||
|
-----END PGP MESSAGE-----`;
|
||||||
|
const key = await openpgp.decryptKey({
|
||||||
|
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
|
||||||
|
passphrase: 'hello world'
|
||||||
|
});
|
||||||
|
const { key: expiredKey } = await openpgp.reformatKey({
|
||||||
|
privateKey: key,
|
||||||
|
userIDs: key.users.map(user => user.userID),
|
||||||
|
keyExpirationTime: 1,
|
||||||
|
date: key.keyPacket.created
|
||||||
|
});
|
||||||
|
await stream.loadStreamsPonyfill();
|
||||||
|
const { signatures: [sigInfo] } = await openpgp.verify({
|
||||||
|
verificationKeys: expiredKey,
|
||||||
|
message: await openpgp.readMessage({ armoredMessage: stream.toStream(armoredMessage) }),
|
||||||
|
config: { minRSABits: 1024 }
|
||||||
|
|
||||||
|
});
|
||||||
|
await expect(sigInfo.verified).to.be.rejectedWith(/Primary key is expired/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Verification fails if signing key was already expired at the time of signing (standard signature)', async function() {
|
||||||
|
const armoredMessage = `-----BEGIN PGP MESSAGE-----
|
||||||
|
|
||||||
|
wrMEAQEIAAYFAmCc8BwAIQkQ4IT3RGwgLJcWIQTX0bHexsqUZwA0RcXghPdE
|
||||||
|
bCAsl2ywA/4qnJWLfTPmIxyRQsBDdDfggHb0bFrX/30AFKV3qIhUZEJ+ZlmV
|
||||||
|
LlQZzEkkagu9nVEAvhhDH8YtXV0FwpYXDNvkbb+KnhygMwIMZMDCfCNw/CMe
|
||||||
|
l8xD2Bfn2YYCQtob8CQBOm4f4MyRgOYpsMsaAP43jK7FcEmYF8LYgF7gnVLY
|
||||||
|
aMsUdQBgnPAcSGVsbG8gV29ybGQgOik=
|
||||||
|
=s9xh
|
||||||
|
-----END PGP MESSAGE-----`;
|
||||||
|
const key = await openpgp.decryptKey({
|
||||||
|
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
|
||||||
|
passphrase: 'hello world'
|
||||||
|
});
|
||||||
|
const { key: expiredKey } = await openpgp.reformatKey({
|
||||||
|
privateKey: key,
|
||||||
|
userIDs: key.users.map(user => user.userID),
|
||||||
|
keyExpirationTime: 1,
|
||||||
|
date: key.keyPacket.created
|
||||||
|
});
|
||||||
|
await stream.loadStreamsPonyfill();
|
||||||
|
const { signatures: [sigInfo] } = await openpgp.verify({
|
||||||
|
verificationKeys: expiredKey,
|
||||||
|
message: await openpgp.readMessage({ armoredMessage: stream.toStream(armoredMessage) }),
|
||||||
|
config: { minRSABits: 1024 }
|
||||||
|
});
|
||||||
|
await expect(sigInfo.verified).to.be.rejectedWith(/Primary key is expired/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Verification succeeds if an expired signing key was valid at the time of signing (with streaming)', async function() {
|
||||||
|
const armoredMessage = `-----BEGIN PGP MESSAGE-----
|
||||||
|
|
||||||
|
xA0DAQgB4IT3RGwgLJcByxF1AGCdJvJoZWxsbyB3b3JsZMKzBAEBCAAGBQJS
|
||||||
|
YS9OACEJEOCE90RsICyXFiEE19Gx3sbKlGcANEXF4IT3RGwgLJcPBQP/csZd
|
||||||
|
9AhZQ3+dPkwlqlsXqYMlVEagYDavlUDI2CEJ2cn1rqHBuMlRkmYs7UqODku4
|
||||||
|
FhJ6WvghiEKx8vqghDuaUXmcKuXhYe+PomD4XBmpbURBXCdPnojTINUj7GPK
|
||||||
|
eSvSZutLuKKbidSYMLhWROPlwKc2GU2ws6PrLZAyCAel/lU=
|
||||||
|
=xDib
|
||||||
|
-----END PGP MESSAGE-----`;
|
||||||
|
const key = await openpgp.decryptKey({
|
||||||
|
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
|
||||||
|
passphrase: 'hello world'
|
||||||
|
});
|
||||||
|
const { key: expiredKey } = await openpgp.reformatKey({
|
||||||
|
privateKey: key,
|
||||||
|
userIDs: key.users.map(user => user.userID),
|
||||||
|
keyExpirationTime: 1,
|
||||||
|
date: key.keyPacket.created
|
||||||
|
});
|
||||||
|
await stream.loadStreamsPonyfill();
|
||||||
|
const { signatures: [sigInfo] } = await openpgp.verify({
|
||||||
|
verificationKeys: expiredKey,
|
||||||
|
message: await openpgp.readMessage({ armoredMessage: stream.toStream(armoredMessage) }),
|
||||||
|
config: { minRSABits: 1024 }
|
||||||
|
});
|
||||||
|
expect(await sigInfo.verified).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Verification succeeds if an expired signing key was valid at the time of signing (without streaming)', async function() {
|
||||||
|
const armoredMessage = `-----BEGIN PGP MESSAGE-----
|
||||||
|
|
||||||
|
xA0DAQgB4IT3RGwgLJcByxF1AGCdJvJoZWxsbyB3b3JsZMKzBAEBCAAGBQJS
|
||||||
|
YS9OACEJEOCE90RsICyXFiEE19Gx3sbKlGcANEXF4IT3RGwgLJcPBQP/csZd
|
||||||
|
9AhZQ3+dPkwlqlsXqYMlVEagYDavlUDI2CEJ2cn1rqHBuMlRkmYs7UqODku4
|
||||||
|
FhJ6WvghiEKx8vqghDuaUXmcKuXhYe+PomD4XBmpbURBXCdPnojTINUj7GPK
|
||||||
|
eSvSZutLuKKbidSYMLhWROPlwKc2GU2ws6PrLZAyCAel/lU=
|
||||||
|
=xDib
|
||||||
|
-----END PGP MESSAGE-----`;
|
||||||
|
const key = await openpgp.decryptKey({
|
||||||
|
privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
|
||||||
|
passphrase: 'hello world'
|
||||||
|
});
|
||||||
|
const { key: expiredKey } = await openpgp.reformatKey({
|
||||||
|
privateKey: key,
|
||||||
|
userIDs: key.users.map(user => user.userID),
|
||||||
|
keyExpirationTime: 1,
|
||||||
|
date: key.keyPacket.created
|
||||||
|
});
|
||||||
|
const { signatures: [sigInfo] } = await openpgp.verify({
|
||||||
|
verificationKeys: expiredKey,
|
||||||
|
message: await openpgp.readMessage({ armoredMessage }),
|
||||||
|
config: { minRSABits: 1024 }
|
||||||
|
});
|
||||||
|
expect(await sigInfo.verified).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Supports non-human-readable notations', async function() {
|
it('Supports non-human-readable notations', async function() {
|
||||||
|
@ -1493,46 +1443,21 @@ hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Verify test with expired verification public key', async function() {
|
|
||||||
const pubKey = await openpgp.readKey({ armoredKey: pub_expired });
|
|
||||||
const message = await openpgp.readMessage({ armoredMessage: msg_sig_expired });
|
|
||||||
return openpgp.verify({ verificationKeys:[pubKey], message:message }).then(function(verified) {
|
|
||||||
expect(verified).to.exist;
|
|
||||||
expect(verified.signatures).to.have.length(1);
|
|
||||||
expect(verified.signatures[0].valid).to.be.true;
|
|
||||||
expect(verified.signatures[0].signature.packets.length).to.equal(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Verify test with expired verification public key and disable expiration checks using null date', async function() {
|
|
||||||
const pubKey = await openpgp.readKey({ armoredKey: pub_expired });
|
|
||||||
const message = await openpgp.readMessage({ armoredMessage: msg_sig_expired });
|
|
||||||
return openpgp.verify({ verificationKeys:[pubKey], message:message, date: null }).then(function(verified) {
|
|
||||||
expect(verified).to.exist;
|
|
||||||
expect(verified.signatures).to.have.length(1);
|
|
||||||
expect(verified.signatures[0].valid).to.be.true;
|
|
||||||
expect(verified.signatures[0].signature.packets.length).to.equal(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO add test with multiple revocation signatures
|
// TODO add test with multiple revocation signatures
|
||||||
it('Verify primary key revocation signatures', async function() {
|
it('Verify primary key revocation signatures', async function() {
|
||||||
const pubKey = await openpgp.readKey({ armoredKey: pub_revoked });
|
const pubKey = await openpgp.readKey({ armoredKey: pub_revoked });
|
||||||
const revSig = pubKey.revocationSignatures[0];
|
|
||||||
revSig.verified = null;
|
|
||||||
await pubKey.revocationSignatures[0].verify(
|
await pubKey.revocationSignatures[0].verify(
|
||||||
pubKey.primaryKey, openpgp.enums.signature.keyRevocation, { key: pubKey.primaryKey }
|
pubKey.keyPacket, openpgp.enums.signature.keyRevocation, { key: pubKey.keyPacket }
|
||||||
).then(() => expect(revSig.verified).to.be.true);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO add test with multiple revocation signatures
|
// TODO add test with multiple revocation signatures
|
||||||
it('Verify subkey revocation signatures', async function() {
|
it('Verify subkey revocation signatures', async function() {
|
||||||
const pubKey = await openpgp.readKey({ armoredKey: pub_revoked });
|
const pubKey = await openpgp.readKey({ armoredKey: pub_revoked });
|
||||||
const revSig = pubKey.subKeys[0].revocationSignatures[0];
|
const revSig = pubKey.subKeys[0].revocationSignatures[0];
|
||||||
revSig.verified = null;
|
|
||||||
await revSig.verify(
|
await revSig.verify(
|
||||||
pubKey.primaryKey, openpgp.enums.signature.subkeyRevocation, { key: pubKey.primaryKey, bind: pubKey.subKeys[0].keyPacket }
|
pubKey.keyPacket, openpgp.enums.signature.subkeyRevocation, { key: pubKey.keyPacket, bind: pubKey.subKeys[0].keyPacket }
|
||||||
).then(() => expect(revSig.verified).to.be.true);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Verify key expiration date', async function() {
|
it('Verify key expiration date', async function() {
|
||||||
|
@ -1698,7 +1623,7 @@ iTuGu4fEU1UligAXSrZmCdE=
|
||||||
|
|
||||||
const key = await openpgp.readKey({ armoredKey: armoredKeyWithPhoto });
|
const key = await openpgp.readKey({ armoredKey: armoredKeyWithPhoto });
|
||||||
await Promise.all(key.users.map(async user => {
|
await Promise.all(key.users.map(async user => {
|
||||||
await user.verify(key.primaryKey);
|
await user.verify(key.keyPacket);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -389,30 +389,27 @@ function omnibus() {
|
||||||
expect(firstKey.privateKeyArmored).to.exist;
|
expect(firstKey.privateKeyArmored).to.exist;
|
||||||
expect(firstKey.publicKeyArmored).to.exist;
|
expect(firstKey.publicKeyArmored).to.exist;
|
||||||
expect(firstKey.key).to.exist;
|
expect(firstKey.key).to.exist;
|
||||||
expect(firstKey.key.primaryKey).to.exist;
|
expect(firstKey.key.keyPacket).to.exist;
|
||||||
expect(firstKey.key.subKeys).to.have.length(1);
|
expect(firstKey.key.subKeys).to.have.length(1);
|
||||||
expect(firstKey.key.subKeys[0].keyPacket).to.exist;
|
expect(firstKey.key.subKeys[0].keyPacket).to.exist;
|
||||||
|
|
||||||
const hi = firstKey.key;
|
const hi = firstKey.key;
|
||||||
const primaryKey = hi.primaryKey;
|
const primaryKey = hi.keyPacket;
|
||||||
const subKey = hi.subKeys[0];
|
const subKey = hi.subKeys[0];
|
||||||
expect(hi.getAlgorithmInfo().curve).to.equal('ed25519');
|
expect(hi.getAlgorithmInfo().curve).to.equal('ed25519');
|
||||||
expect(hi.getAlgorithmInfo().algorithm).to.equal('eddsa');
|
expect(hi.getAlgorithmInfo().algorithm).to.equal('eddsa');
|
||||||
expect(subKey.getAlgorithmInfo().curve).to.equal('curve25519');
|
expect(subKey.getAlgorithmInfo().curve).to.equal('curve25519');
|
||||||
expect(subKey.getAlgorithmInfo().algorithm).to.equal('ecdh');
|
expect(subKey.getAlgorithmInfo().algorithm).to.equal('ecdh');
|
||||||
|
|
||||||
// Self Certificate is valid
|
// Verify that self Certificate is valid
|
||||||
const user = hi.users[0];
|
const user = hi.users[0];
|
||||||
const certificate = user.selfCertifications[0];
|
const certificate = user.selfCertifications[0];
|
||||||
certificate.verified = null;
|
|
||||||
await certificate.verify(
|
await certificate.verify(
|
||||||
primaryKey, openpgp.enums.signature.certGeneric, { userID: user.userID, key: primaryKey }
|
primaryKey, openpgp.enums.signature.certGeneric, { userID: user.userID, key: primaryKey }
|
||||||
).then(async () => expect(certificate.verified).to.be.true);
|
);
|
||||||
|
|
||||||
certificate.verified = null;
|
|
||||||
await user.verifyCertificate(
|
await user.verifyCertificate(
|
||||||
primaryKey, certificate, [hi.toPublic()], undefined, openpgp.config
|
primaryKey, certificate, [hi.toPublic()], undefined, openpgp.config
|
||||||
).then(async () => expect(certificate.verified).to.be.true);
|
);
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
userIDs: { name: "Bye", email: "bye@good.bye" },
|
userIDs: { name: "Bye", email: "bye@good.bye" },
|
||||||
|
@ -425,27 +422,23 @@ function omnibus() {
|
||||||
expect(bye.subKeys[0].getAlgorithmInfo().curve).to.equal('curve25519');
|
expect(bye.subKeys[0].getAlgorithmInfo().curve).to.equal('curve25519');
|
||||||
expect(bye.subKeys[0].getAlgorithmInfo().algorithm).to.equal('ecdh');
|
expect(bye.subKeys[0].getAlgorithmInfo().algorithm).to.equal('ecdh');
|
||||||
|
|
||||||
// Self Certificate is valid
|
// Verify that self Certificate is valid
|
||||||
const user = bye.users[0];
|
const user = bye.users[0];
|
||||||
const certificate = user.selfCertifications[0];
|
const certificate = user.selfCertifications[0];
|
||||||
certificate.verified = null;
|
|
||||||
await certificate.verify(
|
await certificate.verify(
|
||||||
bye.primaryKey, openpgp.enums.signature.certGeneric, { userID: user.userID, key: bye.primaryKey }
|
bye.keyPacket, openpgp.enums.signature.certGeneric, { userID: user.userID, key: bye.keyPacket }
|
||||||
).then(async () => expect(certificate.verified).to.be.true);
|
);
|
||||||
certificate.verified = null;
|
|
||||||
await user.verifyCertificate(
|
await user.verifyCertificate(
|
||||||
bye.primaryKey, user.selfCertifications[0], [bye.toPublic()], undefined, openpgp.config
|
bye.keyPacket, user.selfCertifications[0], [bye.toPublic()], undefined, openpgp.config
|
||||||
).then(async () => expect(certificate.verified).to.be.true);
|
);
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
// Hi trusts Bye!
|
// Hi trusts Bye!
|
||||||
bye.toPublic().signPrimaryUser([hi]).then(trustedBye => {
|
bye.toPublic().signPrimaryUser([hi]).then(trustedBye => {
|
||||||
const hiCertificate = trustedBye.users[0].otherCertifications[0];
|
const hiCertificate = trustedBye.users[0].otherCertifications[0];
|
||||||
expect(hiCertificate.verified).to.be.true;
|
|
||||||
hiCertificate.verified = null;
|
|
||||||
return hiCertificate.verify(
|
return hiCertificate.verify(
|
||||||
primaryKey, openpgp.enums.signature.certGeneric, { userID: user.userID, key: bye.toPublic().primaryKey }
|
primaryKey, openpgp.enums.signature.certGeneric, { userID: user.userID, key: bye.toPublic().keyPacket }
|
||||||
).then(async () => expect(hiCertificate.verified).to.be.true);
|
);
|
||||||
}),
|
}),
|
||||||
// Signing message
|
// Signing message
|
||||||
openpgp.sign(
|
openpgp.sign(
|
||||||
|
@ -466,22 +459,18 @@ function omnibus() {
|
||||||
]);
|
]);
|
||||||
}),
|
}),
|
||||||
// Encrypting and signing
|
// Encrypting and signing
|
||||||
openpgp.encrypt(
|
openpgp.encrypt({
|
||||||
{
|
message: await openpgp.createMessage({ text: 'Hi, Hi wrote this but only Bye can read it!' }),
|
||||||
message: await openpgp.createMessage({ text: 'Hi, Hi wrote this but only Bye can read it!' }),
|
encryptionKeys: [bye.toPublic()],
|
||||||
encryptionKeys: [bye.toPublic()],
|
signingKeys: [hi]
|
||||||
signingKeys: [hi]
|
}).then(async encrypted => {
|
||||||
}
|
|
||||||
).then(async encrypted => {
|
|
||||||
const msg = await openpgp.readMessage({ armoredMessage: encrypted });
|
const msg = await openpgp.readMessage({ armoredMessage: encrypted });
|
||||||
// Decrypting and verifying
|
// Decrypting and verifying
|
||||||
return openpgp.decrypt(
|
return openpgp.decrypt({
|
||||||
{
|
message: msg,
|
||||||
message: msg,
|
decryptionKeys: bye,
|
||||||
decryptionKeys: bye,
|
verificationKeys: [hi.toPublic()]
|
||||||
verificationKeys: [hi.toPublic()]
|
}).then(output => {
|
||||||
}
|
|
||||||
).then(output => {
|
|
||||||
expect(output.data).to.equal('Hi, Hi wrote this but only Bye can read it!');
|
expect(output.data).to.equal('Hi, Hi wrote this but only Bye can read it!');
|
||||||
expect(output.signatures[0].valid).to.be.true;
|
expect(output.signatures[0].valid).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user