mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-03-30 15:08:32 +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 util from './util';
|
||||
import { Signature } from './signature';
|
||||
import { getPreferredHashAlgo, getPreferredCipherSuite, createSignaturePacket } from './key';
|
||||
import { getPreferredCipherSuite, createSignaturePacket } from './key';
|
||||
import {
|
||||
PacketList,
|
||||
LiteralDataPacket,
|
||||
@ -514,49 +514,14 @@ export class Message {
|
||||
throw new Error('No literal data packet to sign.');
|
||||
}
|
||||
|
||||
let i;
|
||||
let existingSigPacketlist;
|
||||
// If data packet was created from Uint8Array, use binary, otherwise use text
|
||||
const signatureType = literalDataPacket.text === null ?
|
||||
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));
|
||||
});
|
||||
const signaturePackets = await createSignaturePackets(literalDataPacket, signingKeys, signature, signingKeyIDs, date, userIDs, notations, false, config); // this returns the existing signature packets as well
|
||||
const onePassSignaturePackets = signaturePackets.map(
|
||||
(signaturePacket, i) => OnePassSignaturePacket.fromSignaturePacket(signaturePacket, i === 0))
|
||||
.reverse(); // innermost OPS refers to the first signature packet
|
||||
|
||||
packetlist.push(...onePassSignaturePackets);
|
||||
packetlist.push(literalDataPacket);
|
||||
packetlist.push(...(await createSignaturePackets(literalDataPacket, signingKeys, signature, signingKeyIDs, date, userIDs, notations, false, config)));
|
||||
packetlist.push(...signaturePackets);
|
||||
|
||||
return new Message(packetlist);
|
||||
}
|
||||
@ -733,6 +698,7 @@ export class Message {
|
||||
* @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} [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 {Object} [config] - Full configuration, defaults to openpgp.config
|
||||
* @returns {Promise<PacketList>} List of signature packets.
|
||||
|
@ -16,14 +16,12 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import * as stream from '@openpgp/web-stream-tools';
|
||||
import SignaturePacket from './signature';
|
||||
import SignaturePacket, { saltLengthForHash } from './signature';
|
||||
import KeyID from '../type/keyid';
|
||||
import enums from '../enums';
|
||||
import util from '../util';
|
||||
import { UnsupportedError } from './packet';
|
||||
|
||||
const VERSION = 3;
|
||||
|
||||
/**
|
||||
* Implementation of the One-Pass Signature Packets (Tag 4)
|
||||
*
|
||||
@ -39,8 +37,22 @@ class OnePassSignaturePacket {
|
||||
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() {
|
||||
/** 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;
|
||||
/**
|
||||
* A one-octet signature type.
|
||||
@ -62,8 +74,12 @@ class OnePassSignaturePacket {
|
||||
* @type {enums.publicKey}
|
||||
*/
|
||||
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;
|
||||
/** 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 zero value indicates that the next packet is another One-Pass Signature packet
|
||||
@ -79,9 +95,9 @@ class OnePassSignaturePacket {
|
||||
*/
|
||||
read(bytes) {
|
||||
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++];
|
||||
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.`);
|
||||
}
|
||||
|
||||
@ -95,10 +111,32 @@ class OnePassSignaturePacket {
|
||||
// A one-octet number describing the public-key algorithm used.
|
||||
this.publicKeyAlgorithm = bytes[mypos++];
|
||||
|
||||
// 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;
|
||||
if (this.version === 6) {
|
||||
// Only for v6 signatures, a variable-length field containing:
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
*/
|
||||
write() {
|
||||
const start = new Uint8Array([VERSION, this.signatureType, this.hashAlgorithm, this.publicKeyAlgorithm]);
|
||||
|
||||
const end = new Uint8Array([this.flags]);
|
||||
|
||||
return util.concatUint8Array([start, this.issuerKeyID.write(), end]);
|
||||
const arr = [new Uint8Array([
|
||||
this.version,
|
||||
this.signatureType,
|
||||
this.hashAlgorithm,
|
||||
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) {
|
||||
@ -133,7 +183,11 @@ class OnePassSignaturePacket {
|
||||
correspondingSig.signatureType !== this.signatureType ||
|
||||
correspondingSig.hashAlgorithm !== this.hashAlgorithm ||
|
||||
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');
|
||||
}
|
||||
|
@ -214,7 +214,11 @@ class SignaturePacket {
|
||||
|
||||
if (this.version === 6) {
|
||||
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 hash = await this.hash(this.signatureType, data, toHash, detached);
|
||||
@ -823,7 +827,7 @@ function writeSubPacket(type, critical, data) {
|
||||
* @returns {Integer} Salt length.
|
||||
* @private
|
||||
*/
|
||||
function saltLengthForHash(hashAlgorithm) {
|
||||
export function saltLengthForHash(hashAlgorithm) {
|
||||
switch (hashAlgorithm) {
|
||||
case enums.hash.sha256: return 16;
|
||||
case enums.hash.sha384: return 24;
|
||||
|
@ -710,6 +710,68 @@ hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw==
|
||||
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 () {
|
||||
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 =
|
||||
['-----BEGIN PGP MESSAGE-----',
|
||||
'Version: GnuPG v2.0.19 (GNU/Linux)',
|
||||
|
Loading…
x
Reference in New Issue
Block a user