mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-10-14 00:59:29 +00:00
Add support for v6 one-pass signature packets
Introduces v6 one-pass signature packets required for v6 signatures. Includes the changes from !305 of the crypto refresh: https://gitlab.com/openpgp-wg/rfc4880bis/-/merge_requests/305 Also, introduce `OnePassSignaturePacket.fromSignaturePacket` to simplify OPS generation.
This commit is contained in:
parent
0b8501427b
commit
af96628855
@ -24,7 +24,7 @@ import crypto from './crypto';
|
|||||||
import enums from './enums';
|
import enums from './enums';
|
||||||
import util from './util';
|
import util from './util';
|
||||||
import { Signature } from './signature';
|
import { Signature } from './signature';
|
||||||
import { getPreferredHashAlgo, getPreferredCipherSuite, createSignaturePacket } from './key';
|
import { getPreferredCipherSuite, createSignaturePacket } from './key';
|
||||||
import {
|
import {
|
||||||
PacketList,
|
PacketList,
|
||||||
LiteralDataPacket,
|
LiteralDataPacket,
|
||||||
@ -514,49 +514,14 @@ export class Message {
|
|||||||
throw new Error('No literal data packet to sign.');
|
throw new Error('No literal data packet to sign.');
|
||||||
}
|
}
|
||||||
|
|
||||||
let i;
|
const signaturePackets = await createSignaturePackets(literalDataPacket, signingKeys, signature, signingKeyIDs, date, userIDs, notations, false, config); // this returns the existing signature packets as well
|
||||||
let existingSigPacketlist;
|
const onePassSignaturePackets = signaturePackets.map(
|
||||||
// If data packet was created from Uint8Array, use binary, otherwise use text
|
(signaturePacket, i) => OnePassSignaturePacket.fromSignaturePacket(signaturePacket, i === 0))
|
||||||
const signatureType = literalDataPacket.text === null ?
|
.reverse(); // innermost OPS refers to the first signature packet
|
||||||
enums.signature.binary : enums.signature.text;
|
|
||||||
|
|
||||||
if (signature) {
|
|
||||||
existingSigPacketlist = signature.packets.filterByTag(enums.packet.signature);
|
|
||||||
for (i = existingSigPacketlist.length - 1; i >= 0; i--) {
|
|
||||||
const signaturePacket = existingSigPacketlist[i];
|
|
||||||
const onePassSig = new OnePassSignaturePacket();
|
|
||||||
onePassSig.signatureType = signaturePacket.signatureType;
|
|
||||||
onePassSig.hashAlgorithm = signaturePacket.hashAlgorithm;
|
|
||||||
onePassSig.publicKeyAlgorithm = signaturePacket.publicKeyAlgorithm;
|
|
||||||
onePassSig.issuerKeyID = signaturePacket.issuerKeyID;
|
|
||||||
if (!signingKeys.length && i === 0) {
|
|
||||||
onePassSig.flags = 1;
|
|
||||||
}
|
|
||||||
packetlist.push(onePassSig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(Array.from(signingKeys).reverse().map(async function (primaryKey, i) {
|
|
||||||
if (!primaryKey.isPrivate()) {
|
|
||||||
throw new Error('Need private key for signing');
|
|
||||||
}
|
|
||||||
const signingKeyID = signingKeyIDs[signingKeys.length - 1 - i];
|
|
||||||
const signingKey = await primaryKey.getSigningKey(signingKeyID, date, userIDs, config);
|
|
||||||
const onePassSig = new OnePassSignaturePacket();
|
|
||||||
onePassSig.signatureType = signatureType;
|
|
||||||
onePassSig.hashAlgorithm = await getPreferredHashAlgo(primaryKey, signingKey.keyPacket, date, userIDs, config);
|
|
||||||
onePassSig.publicKeyAlgorithm = signingKey.keyPacket.algorithm;
|
|
||||||
onePassSig.issuerKeyID = signingKey.getKeyID();
|
|
||||||
if (i === signingKeys.length - 1) {
|
|
||||||
onePassSig.flags = 1;
|
|
||||||
}
|
|
||||||
return onePassSig;
|
|
||||||
})).then(onePassSignatureList => {
|
|
||||||
onePassSignatureList.forEach(onePassSig => packetlist.push(onePassSig));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
packetlist.push(...onePassSignaturePackets);
|
||||||
packetlist.push(literalDataPacket);
|
packetlist.push(literalDataPacket);
|
||||||
packetlist.push(...(await createSignaturePackets(literalDataPacket, signingKeys, signature, signingKeyIDs, date, userIDs, notations, false, config)));
|
packetlist.push(...signaturePackets);
|
||||||
|
|
||||||
return new Message(packetlist);
|
return new Message(packetlist);
|
||||||
}
|
}
|
||||||
@ -733,6 +698,7 @@ export class Message {
|
|||||||
* @param {Date} [date] - Override the creationtime of the signature
|
* @param {Date} [date] - Override the creationtime of the signature
|
||||||
* @param {Array} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
|
* @param {Array} [userIDs] - User IDs to sign with, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }]
|
||||||
* @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }]
|
* @param {Array} [notations] - Notation Data to add to the signatures, e.g. [{ name: 'test@example.org', value: new TextEncoder().encode('test'), humanReadable: true, critical: false }]
|
||||||
|
* @param {Array} [signatureSalts] - A list of signature salts matching the number of signingKeys that should be used for v6 signatures
|
||||||
* @param {Boolean} [detached] - Whether to create detached signature packets
|
* @param {Boolean} [detached] - Whether to create detached signature packets
|
||||||
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
* @param {Object} [config] - Full configuration, defaults to openpgp.config
|
||||||
* @returns {Promise<PacketList>} List of signature packets.
|
* @returns {Promise<PacketList>} List of signature packets.
|
||||||
|
@ -16,14 +16,12 @@
|
|||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
import * as stream from '@openpgp/web-stream-tools';
|
import * as stream from '@openpgp/web-stream-tools';
|
||||||
import SignaturePacket from './signature';
|
import SignaturePacket, { saltLengthForHash } from './signature';
|
||||||
import KeyID from '../type/keyid';
|
import KeyID from '../type/keyid';
|
||||||
import enums from '../enums';
|
import enums from '../enums';
|
||||||
import util from '../util';
|
import util from '../util';
|
||||||
import { UnsupportedError } from './packet';
|
import { UnsupportedError } from './packet';
|
||||||
|
|
||||||
const VERSION = 3;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the One-Pass Signature Packets (Tag 4)
|
* Implementation of the One-Pass Signature Packets (Tag 4)
|
||||||
*
|
*
|
||||||
@ -39,8 +37,22 @@ class OnePassSignaturePacket {
|
|||||||
return enums.packet.onePassSignature;
|
return enums.packet.onePassSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static fromSignaturePacket(signaturePacket, isLast) {
|
||||||
|
const onePassSig = new OnePassSignaturePacket();
|
||||||
|
onePassSig.version = signaturePacket.version === 6 ? 6 : 3;
|
||||||
|
onePassSig.signatureType = signaturePacket.signatureType;
|
||||||
|
onePassSig.hashAlgorithm = signaturePacket.hashAlgorithm;
|
||||||
|
onePassSig.publicKeyAlgorithm = signaturePacket.publicKeyAlgorithm;
|
||||||
|
onePassSig.issuerKeyID = signaturePacket.issuerKeyID;
|
||||||
|
onePassSig.salt = signaturePacket.salt; // v6 only
|
||||||
|
onePassSig.issuerFingerprint = signaturePacket.issuerFingerprint; // v6 only
|
||||||
|
|
||||||
|
onePassSig.flags = isLast ? 1 : 0;
|
||||||
|
return onePassSig;
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
/** A one-octet version number. The current version is 3. */
|
/** A one-octet version number. The current versions are 3 and 6. */
|
||||||
this.version = null;
|
this.version = null;
|
||||||
/**
|
/**
|
||||||
* A one-octet signature type.
|
* A one-octet signature type.
|
||||||
@ -62,8 +74,12 @@ class OnePassSignaturePacket {
|
|||||||
* @type {enums.publicKey}
|
* @type {enums.publicKey}
|
||||||
*/
|
*/
|
||||||
this.publicKeyAlgorithm = null;
|
this.publicKeyAlgorithm = null;
|
||||||
/** An eight-octet number holding the Key ID of the signing key. */
|
/** Only for v6, a variable-length field containing the salt. */
|
||||||
|
this.salt = null;
|
||||||
|
/** Only for v3 packets, an eight-octet number holding the Key ID of the signing key. */
|
||||||
this.issuerKeyID = null;
|
this.issuerKeyID = null;
|
||||||
|
/** Only for v6 packets, 32 octets of the fingerprint of the signing key. */
|
||||||
|
this.issuerFingerprint = null;
|
||||||
/**
|
/**
|
||||||
* A one-octet number holding a flag showing whether the signature is nested.
|
* A one-octet number holding a flag showing whether the signature is nested.
|
||||||
* A zero value indicates that the next packet is another One-Pass Signature packet
|
* A zero value indicates that the next packet is another One-Pass Signature packet
|
||||||
@ -79,9 +95,9 @@ class OnePassSignaturePacket {
|
|||||||
*/
|
*/
|
||||||
read(bytes) {
|
read(bytes) {
|
||||||
let mypos = 0;
|
let mypos = 0;
|
||||||
// A one-octet version number. The current version is 3.
|
// A one-octet version number. The current versions are 3 or 6.
|
||||||
this.version = bytes[mypos++];
|
this.version = bytes[mypos++];
|
||||||
if (this.version !== VERSION) {
|
if (this.version !== 3 && this.version !== 6) {
|
||||||
throw new UnsupportedError(`Version ${this.version} of the one-pass signature packet is unsupported.`);
|
throw new UnsupportedError(`Version ${this.version} of the one-pass signature packet is unsupported.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,10 +111,32 @@ class OnePassSignaturePacket {
|
|||||||
// A one-octet number describing the public-key algorithm used.
|
// A one-octet number describing the public-key algorithm used.
|
||||||
this.publicKeyAlgorithm = bytes[mypos++];
|
this.publicKeyAlgorithm = bytes[mypos++];
|
||||||
|
|
||||||
// An eight-octet number holding the Key ID of the signing key.
|
if (this.version === 6) {
|
||||||
this.issuerKeyID = new KeyID();
|
// Only for v6 signatures, a variable-length field containing:
|
||||||
this.issuerKeyID.read(bytes.subarray(mypos, mypos + 8));
|
|
||||||
mypos += 8;
|
// A one-octet salt size. The value MUST match the value defined
|
||||||
|
// for the hash algorithm as specified in Table 23 (Hash algorithm registry).
|
||||||
|
const saltLength = bytes[mypos++];
|
||||||
|
if (saltLength !== saltLengthForHash(this.hashAlgorithm)) {
|
||||||
|
throw new Error('Unexpected salt size for the hash algorithm');
|
||||||
|
}
|
||||||
|
|
||||||
|
// The salt; a random value value of the specified size.
|
||||||
|
this.salt = bytes.subarray(mypos, mypos + saltLength);
|
||||||
|
mypos += saltLength;
|
||||||
|
|
||||||
|
// Only for v6 packets, 32 octets of the fingerprint of the signing key.
|
||||||
|
this.issuerFingerprint = bytes.subarray(mypos, mypos + 32);
|
||||||
|
mypos += 32;
|
||||||
|
this.issuerKeyID = new KeyID();
|
||||||
|
// For v6 the Key ID is the high-order 64 bits of the fingerprint.
|
||||||
|
this.issuerKeyID.read(this.issuerFingerprint);
|
||||||
|
} else {
|
||||||
|
// Only for v3 packets, an eight-octet number holding the Key ID of the signing key.
|
||||||
|
this.issuerKeyID = new KeyID();
|
||||||
|
this.issuerKeyID.read(bytes.subarray(mypos, mypos + 8));
|
||||||
|
mypos += 8;
|
||||||
|
}
|
||||||
|
|
||||||
// A one-octet number holding a flag showing whether the signature
|
// A one-octet number holding a flag showing whether the signature
|
||||||
// is nested. A zero value indicates that the next packet is
|
// is nested. A zero value indicates that the next packet is
|
||||||
@ -113,11 +151,23 @@ class OnePassSignaturePacket {
|
|||||||
* @returns {Uint8Array} A Uint8Array representation of a one-pass signature packet.
|
* @returns {Uint8Array} A Uint8Array representation of a one-pass signature packet.
|
||||||
*/
|
*/
|
||||||
write() {
|
write() {
|
||||||
const start = new Uint8Array([VERSION, this.signatureType, this.hashAlgorithm, this.publicKeyAlgorithm]);
|
const arr = [new Uint8Array([
|
||||||
|
this.version,
|
||||||
const end = new Uint8Array([this.flags]);
|
this.signatureType,
|
||||||
|
this.hashAlgorithm,
|
||||||
return util.concatUint8Array([start, this.issuerKeyID.write(), end]);
|
this.publicKeyAlgorithm
|
||||||
|
])];
|
||||||
|
if (this.version === 6) {
|
||||||
|
arr.push(
|
||||||
|
new Uint8Array([this.salt.length]),
|
||||||
|
this.salt,
|
||||||
|
this.issuerFingerprint
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
arr.push(this.issuerKeyID.write());
|
||||||
|
}
|
||||||
|
arr.push(new Uint8Array([this.flags]));
|
||||||
|
return util.concatUint8Array(arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateTrailer(...args) {
|
calculateTrailer(...args) {
|
||||||
@ -133,7 +183,11 @@ class OnePassSignaturePacket {
|
|||||||
correspondingSig.signatureType !== this.signatureType ||
|
correspondingSig.signatureType !== this.signatureType ||
|
||||||
correspondingSig.hashAlgorithm !== this.hashAlgorithm ||
|
correspondingSig.hashAlgorithm !== this.hashAlgorithm ||
|
||||||
correspondingSig.publicKeyAlgorithm !== this.publicKeyAlgorithm ||
|
correspondingSig.publicKeyAlgorithm !== this.publicKeyAlgorithm ||
|
||||||
!correspondingSig.issuerKeyID.equals(this.issuerKeyID)
|
!correspondingSig.issuerKeyID.equals(this.issuerKeyID) ||
|
||||||
|
(this.version === 3 && correspondingSig.version === 6) ||
|
||||||
|
(this.version === 6 && correspondingSig.version !== 6) ||
|
||||||
|
(this.version === 6 && !util.equalsUint8Array(correspondingSig.issuerFingerprint, this.issuerFingerprint)) ||
|
||||||
|
(this.version === 6 && !util.equalsUint8Array(correspondingSig.salt, this.salt))
|
||||||
) {
|
) {
|
||||||
throw new Error('Corresponding signature packet does not match one-pass signature packet');
|
throw new Error('Corresponding signature packet does not match one-pass signature packet');
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,11 @@ class SignaturePacket {
|
|||||||
|
|
||||||
if (this.version === 6) {
|
if (this.version === 6) {
|
||||||
const saltLength = saltLengthForHash(this.hashAlgorithm);
|
const saltLength = saltLengthForHash(this.hashAlgorithm);
|
||||||
this.salt = await crypto.random.getRandomBytes(saltLength);
|
if (this.salt === null) {
|
||||||
|
this.salt = crypto.random.getRandomBytes(saltLength);
|
||||||
|
} else if (saltLength !== this.salt.length) {
|
||||||
|
throw new Error('Provided salt does not have the required length');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const toHash = this.toHash(this.signatureType, data, detached);
|
const toHash = this.toHash(this.signatureType, data, detached);
|
||||||
const hash = await this.hash(this.signatureType, data, toHash, detached);
|
const hash = await this.hash(this.signatureType, data, toHash, detached);
|
||||||
@ -823,7 +827,7 @@ function writeSubPacket(type, critical, data) {
|
|||||||
* @returns {Integer} Salt length.
|
* @returns {Integer} Salt length.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function saltLengthForHash(hashAlgorithm) {
|
export function saltLengthForHash(hashAlgorithm) {
|
||||||
switch (hashAlgorithm) {
|
switch (hashAlgorithm) {
|
||||||
case enums.hash.sha256: return 16;
|
case enums.hash.sha256: return 16;
|
||||||
case enums.hash.sha384: return 24;
|
case enums.hash.sha384: return 24;
|
||||||
|
@ -710,6 +710,68 @@ hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw==
|
|||||||
expect(signature.getSigningKeyIDs().map(x => x.toHex())).to.include(publicKey.getKeyID().toHex());
|
expect(signature.getSigningKeyIDs().map(x => x.toHex())).to.include(publicKey.getKeyID().toHex());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Generates valid one-pass signature packets (v6 keys)', async function () {
|
||||||
|
const v6PrivateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
|
||||||
|
xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
|
||||||
|
exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
|
||||||
|
BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
|
||||||
|
2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
|
||||||
|
RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
|
||||||
|
7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
|
||||||
|
LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
|
||||||
|
GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
|
||||||
|
2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
|
||||||
|
M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
|
||||||
|
k0mXubZvyl4GBg==
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----` });
|
||||||
|
const v4PrivateKey = await openpgp.readKey({
|
||||||
|
armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
|
||||||
|
xVgEZOjwQBYJKwYBBAHaRw8BAQdAIzsYHb7T7NhSFmkWKSk5ItaBYv2HET7u
|
||||||
|
IFXhGdvOpogAAQDMk8SQlysNkLe7VRwXedX63St1a2V6/dzDG926oPyW3hJ6
|
||||||
|
zQ48dGVzdEB0ZXN0Lml0PsKMBBAWCgA+BYJk6PBABAsJBwgJkFMSwQwprjrH
|
||||||
|
AxUICgQWAAIBAhkBApsDAh4BFiEEwKKS1V/wldBNfwpjUxLBDCmuOscAAONc
|
||||||
|
AQDU1gi9moPtQzh6rrpKPWZ8nMSiZzLb3LYmpqz9Tn9YFAEAmEC2uHET/Oj6
|
||||||
|
VVxvMwvGytr5y0nuc6ZV5D0I2qoMcAHHXQRk6PBAEgorBgEEAZdVAQUBAQdA
|
||||||
|
xeBFSxzKOtLEQ6mVO2mnxvDiiMKmq7NbOezoWkbeSjgDAQgHAAD/YMvH+eDP
|
||||||
|
h1RplHOiaAMwhpTRGSGOaC6+pu6AMWiRLWAQ88J4BBgWCAAqBYJk6PBACZBT
|
||||||
|
EsEMKa46xwKbDBYhBMCiktVf8JXQTX8KY1MSwQwprjrHAAD4igEAzoExR6VX
|
||||||
|
EY9xxFKtAVdtYOT61d0FZibs0TD0VjbqzdkBAK60jT9jKefpnZ9sGv7bhSRX
|
||||||
|
2hrIPyCF2f0R5Js3LCML
|
||||||
|
=xyQ6
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----`
|
||||||
|
});
|
||||||
|
const armoredSignedMessage = await openpgp.sign({
|
||||||
|
message: await openpgp.createMessage({ text: 'test' }),
|
||||||
|
signingKeys: [v6PrivateKey, v4PrivateKey, v6PrivateKey] // get multiple signature packets
|
||||||
|
});
|
||||||
|
const signedMessage = await openpgp.readMessage({ armoredMessage: armoredSignedMessage });
|
||||||
|
// read signature packet stream
|
||||||
|
signedMessage.packets.push(...await stream.readToEnd(signedMessage.packets.stream, _ => _));
|
||||||
|
const signature1 = signedMessage.packets[4];
|
||||||
|
const signature2 = signedMessage.packets[5]; // v4 sig
|
||||||
|
const signature3 = signedMessage.packets[6];
|
||||||
|
const opsSignature1 = signedMessage.packets[2];
|
||||||
|
const opsSignature2 = signedMessage.packets[1];
|
||||||
|
const opsSignature3 = signedMessage.packets[0];
|
||||||
|
expect(opsSignature1).to.be.instanceOf(openpgp.OnePassSignaturePacket);
|
||||||
|
expect(signature1).to.be.instanceOf(openpgp.SignaturePacket);
|
||||||
|
expect(opsSignature2).to.be.instanceOf(openpgp.OnePassSignaturePacket);
|
||||||
|
expect(signature2).to.be.instanceOf(openpgp.SignaturePacket);
|
||||||
|
expect(opsSignature3).to.be.instanceOf(openpgp.OnePassSignaturePacket);
|
||||||
|
expect(signature3).to.be.instanceOf(openpgp.SignaturePacket);
|
||||||
|
expect(opsSignature1.version).to.equal(6);
|
||||||
|
expect(opsSignature2.version).to.equal(3);
|
||||||
|
expect(opsSignature3.version).to.equal(6);
|
||||||
|
expect(util.uint8ArrayToHex(opsSignature1.issuerFingerprint)).to.equal(v6PrivateKey.getFingerprint());
|
||||||
|
expect(util.uint8ArrayToHex(opsSignature3.issuerFingerprint)).to.equal(v6PrivateKey.getFingerprint());
|
||||||
|
expect(opsSignature1.salt).to.deep.equal(signature1.salt);
|
||||||
|
expect(opsSignature2.salt).to.be.null;
|
||||||
|
expect(opsSignature3.salt).to.deep.equal(signature3.salt);
|
||||||
|
expect(opsSignature1.salt).to.not.deep.equal(opsSignature3.salt); // sanity check
|
||||||
|
});
|
||||||
|
|
||||||
it('Throws when reading a signature missing the creation time', async function () {
|
it('Throws when reading a signature missing the creation time', async function () {
|
||||||
const armoredSignature = `-----BEGIN PGP SIGNATURE-----
|
const armoredSignature = `-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
@ -1109,7 +1171,92 @@ Fk7EflUZzngwY4lBzYAfnNBjEjc30xD/ddo+rwE=
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Verify signed message with two one pass signatures', async function() {
|
|
||||||
|
it('Verify signed message with a v6 one pass signature', async function() {
|
||||||
|
// test vector from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#appendix-A.7
|
||||||
|
const message = await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE-----
|
||||||
|
|
||||||
|
xEYGAQobIHZJX1AhiJD39eLuPBgiUU9wUA9VHYblySHkBONKU/usyxhsTwYJppfk
|
||||||
|
1S36bHIrDB8eJ8GKVnCPZSXsJ7rZrMkBy0p1AAAAAABXaGF0IHdlIG5lZWQgZnJv
|
||||||
|
bSB0aGUgZ3JvY2VyeSBzdG9yZToKCi0gdG9mdQotIHZlZ2V0YWJsZXMKLSBub29k
|
||||||
|
bGVzCsKYBgEbCgAAACkFgmOYo2MiIQbLGGxPBgmml+TVLfpscisMHx4nwYpWcI9l
|
||||||
|
JewnutmsyQAAAABpNiB2SV9QIYiQ9/Xi7jwYIlFPcFAPVR2G5ckh5ATjSlP7rCfQ
|
||||||
|
b7gKqPxbyxbhljGygHQPnqau1eBzrQD5QVplPEDnemrnfmkrpx0GmhCfokxYz9jj
|
||||||
|
FtCgazStmsuOXF9SFQE=
|
||||||
|
-----END PGP MESSAGE-----` });
|
||||||
|
const key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf
|
||||||
|
GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy
|
||||||
|
KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw
|
||||||
|
gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE
|
||||||
|
QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn
|
||||||
|
+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh
|
||||||
|
BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8
|
||||||
|
j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805
|
||||||
|
I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg==
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----` });
|
||||||
|
|
||||||
|
const signingKeyIDs = message.getSigningKeyIDs();
|
||||||
|
expect(key.getKeys(signingKeyIDs[0])).to.not.be.empty;
|
||||||
|
|
||||||
|
const { data, signatures } = await openpgp.verify({ message, verificationKeys: key });
|
||||||
|
expect(signatures).to.have.length(1);
|
||||||
|
expect(await signatures[0].verified).to.be.true;
|
||||||
|
expect((await signatures[0].signature).packets.length).to.equal(1);
|
||||||
|
expect(data.includes('What we need from the grocery store')).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Verify signed message with two one pass signatures (v3 and v6)', async function() {
|
||||||
|
// test vector from gopenpgp
|
||||||
|
const message = await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE-----
|
||||||
|
|
||||||
|
xEYGAAobIBEtnAy3EeX8aDFd2bf/A1Xfi7C/D3mLIkGEwVmD0fecyxhsTwYJppfk
|
||||||
|
1S36bHIrDB8eJ8GKVnCPZSXsJ7rZrMkAxA0DAAoW8jFVDE9H444ByxRiAAAAAABI
|
||||||
|
ZWxsbyBXb3JsZCA6KcJ1BAAWCgAnBQJk52D2CRDyMVUMT0fjjhYhBOuFu1+jOnXh
|
||||||
|
XpROY/IxVQxPR+OOAABOSwEA2nLYxa9ELDxwuPskKCUVs8noyT4fVEScWlWZAKqP
|
||||||
|
ykIBAJEhqqNHFP4Gok1Ss9mvf6fE25tJkdJKD+7PIH0mCJgAwpgGABsKAAAAKQUC
|
||||||
|
ZOdg9iKhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAA2MIBEt
|
||||||
|
nAy3EeX8aDFd2bf/A1Xfi7C/D3mLIkGEwVmD0fecr0oTPTcMNupiH7SCY4THe3ue
|
||||||
|
7PlRvF2WoeWKkNZT1uwcb+ilW5h9eBpp0I4Xj98C0hMA5+rHsTsME2EZM8HADA==
|
||||||
|
-----END PGP MESSAGE-----` });
|
||||||
|
const v6Key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf
|
||||||
|
GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy
|
||||||
|
KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw
|
||||||
|
gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE
|
||||||
|
QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn
|
||||||
|
+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh
|
||||||
|
BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8
|
||||||
|
j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805
|
||||||
|
I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg==
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----` });
|
||||||
|
const v4Key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Comment: Alice's OpenPGP certificate
|
||||||
|
|
||||||
|
mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U
|
||||||
|
b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE
|
||||||
|
ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy
|
||||||
|
MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO
|
||||||
|
dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4
|
||||||
|
OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s
|
||||||
|
E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb
|
||||||
|
DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn
|
||||||
|
0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE=
|
||||||
|
=iIGO
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----` });
|
||||||
|
|
||||||
|
const { data, signatures } = await openpgp.verify({ message, verificationKeys: [v4Key, v6Key] });
|
||||||
|
expect(signatures).to.have.length(2);
|
||||||
|
expect(await signatures[0].verified).to.be.true;
|
||||||
|
expect((await signatures[0].signature).packets.length).to.equal(1);
|
||||||
|
expect(await signatures[1].verified).to.be.true;
|
||||||
|
expect((await signatures[1].signature).packets.length).to.equal(1);
|
||||||
|
expect(data).to.equal('Hello World :)');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Verify signed message with two one pass signatures (both v3)', async function() {
|
||||||
const msg_armor =
|
const msg_armor =
|
||||||
['-----BEGIN PGP MESSAGE-----',
|
['-----BEGIN PGP MESSAGE-----',
|
||||||
'Version: GnuPG v2.0.19 (GNU/Linux)',
|
'Version: GnuPG v2.0.19 (GNU/Linux)',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user