mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2026-03-08 01:51:34 +00:00
Add SHA-3 signature support (#1680)
To support parsing, signing and verifying SHA3 signatures over messages and keys.
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
import { sha1 } from '@openpgp/noble-hashes/sha1';
|
||||
import { sha224, sha256 } from '@openpgp/noble-hashes/sha256';
|
||||
import { sha384, sha512 } from '@openpgp/noble-hashes/sha512';
|
||||
import { sha3_256, sha3_512 } from '@openpgp/noble-hashes/sha3';
|
||||
import { ripemd160 } from '@openpgp/noble-hashes/ripemd160';
|
||||
import * as stream from '@openpgp/web-stream-tools';
|
||||
import md5 from './md5';
|
||||
@@ -55,7 +56,9 @@ const hashFunctions = {
|
||||
sha256: nodeHash('sha256') || nobleHash(sha256, 'SHA-256'),
|
||||
sha384: nodeHash('sha384') || nobleHash(sha384, 'SHA-384'),
|
||||
sha512: nodeHash('sha512') || nobleHash(sha512, 'SHA-512'),
|
||||
ripemd: nodeHash('ripemd160') || nobleHash(ripemd160)
|
||||
ripemd: nodeHash('ripemd160') || nobleHash(ripemd160),
|
||||
sha3_256: nodeHash('sha3-256') || nobleHash(sha3_256),
|
||||
sha3_512: nodeHash('sha3-512') || nobleHash(sha3_512)
|
||||
};
|
||||
|
||||
export default {
|
||||
@@ -68,6 +71,8 @@ export default {
|
||||
sha384: hashFunctions.sha384,
|
||||
sha512: hashFunctions.sha512,
|
||||
ripemd: hashFunctions.ripemd,
|
||||
sha3_256: hashFunctions.sha3_256,
|
||||
sha3_512: hashFunctions.sha3_512,
|
||||
|
||||
/**
|
||||
* Create a hash on the specified data using the specified algorithm
|
||||
@@ -91,6 +96,10 @@ export default {
|
||||
return this.sha512(data);
|
||||
case enums.hash.sha224:
|
||||
return this.sha224(data);
|
||||
case enums.hash.sha3_256:
|
||||
return this.sha3_256(data);
|
||||
case enums.hash.sha3_512:
|
||||
return this.sha3_512(data);
|
||||
default:
|
||||
throw new Error('Invalid hash function.');
|
||||
}
|
||||
@@ -116,6 +125,10 @@ export default {
|
||||
return 64;
|
||||
case enums.hash.sha224:
|
||||
return 28;
|
||||
case enums.hash.sha3_256:
|
||||
return 32;
|
||||
case enums.hash.sha3_512:
|
||||
return 64;
|
||||
default:
|
||||
throw new Error('Invalid hash algorithm.');
|
||||
}
|
||||
|
||||
@@ -175,7 +175,9 @@ export default {
|
||||
sha256: 8,
|
||||
sha384: 9,
|
||||
sha512: 10,
|
||||
sha224: 11
|
||||
sha224: 11,
|
||||
sha3_256: 12,
|
||||
sha3_512: 14
|
||||
},
|
||||
|
||||
/** A list of hash names as accepted by webCrypto functions.
|
||||
|
||||
@@ -833,6 +833,8 @@ export function saltLengthForHash(hashAlgorithm) {
|
||||
case enums.hash.sha384: return 24;
|
||||
case enums.hash.sha512: return 32;
|
||||
case enums.hash.sha224: return 16;
|
||||
case enums.hash.sha3_256: return 16;
|
||||
case enums.hash.sha3_512: return 32;
|
||||
default: throw new Error('Unsupported hash function for V6 signatures');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,8 @@ export default () => it('SHA* with test vectors from NIST FIPS 180-2', async fun
|
||||
expect(util.uint8ArrayToHex(await hash.sha384(util.stringToUint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')), 'hash.sha384("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b')).to.equal('3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b');
|
||||
expect(util.uint8ArrayToHex(await hash.sha512(util.stringToUint8Array('abc')), 'hash.sha512("abc") = ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f')).to.equal('ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f');
|
||||
expect(util.uint8ArrayToHex(await hash.sha512(util.stringToUint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')), 'hash.sha512("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445')).to.equal('204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445');
|
||||
expect(util.uint8ArrayToHex(await hash.sha3_256(util.stringToUint8Array('abc')), 'hash.sha3_256("abc") = 3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532')).to.equal('3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532');
|
||||
expect(util.uint8ArrayToHex(await hash.sha3_256(util.stringToUint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')), 'hash.sha3_256("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376')).to.equal('41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376');
|
||||
expect(util.uint8ArrayToHex(await hash.sha3_512(util.stringToUint8Array('abc')), 'hash.sha3_512("abc") = b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0')).to.equal('b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0');
|
||||
expect(util.uint8ArrayToHex(await hash.sha3_512(util.stringToUint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')), 'hash.sha3_512("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636dee691fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e')).to.equal('04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636dee691fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e');
|
||||
});
|
||||
|
||||
@@ -297,6 +297,25 @@ DECl1Qu4QyeXin29uEXWiekMpNlZVsEuc8icCw6ABhIZ
|
||||
=/7PI
|
||||
-----END PGP PRIVATE KEY BLOCK-----`;
|
||||
|
||||
const priv_key_sha3 = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
xUsGZN8edBsAAAAgdUMlFMFCVKNo7sdUd6FVBos6NNjpUpSdrodk6BfPb/kA+3bu
|
||||
A2+WY2LwyxlX5o07WR2VSn+wuegC3v28yO0tClHCtwYfGw4AAABIBYJk3x50BAsJ
|
||||
CAcHFQ4MCgkICwIWAAIXgAKbAwIeCSIhBpbSe0QWuaCNSSLaePhXEP3BxQ2VHX3W
|
||||
pW1U6svHvCUiBScJAgcCAAAAACMZIP8aHixoyC9wS3q/TNV/IfOQa81f+U5Ucz6H
|
||||
4I+c5bWRYUzH/piBB4n5FoYlld+/SViCQIBCQ+fynLmaj5wlf22+mISTt/9je1Zf
|
||||
YWlJ+WSJyi5gY5EH9DubfuIU3VaqCM0aQmVybmFkZXR0ZSA8YkBleGFtcGxlLm9y
|
||||
Zz7CugYTGw4AAABLBYJk3x50BAsJCAcHFQ4MCgkICwIWAAIXgAIZAQKbAwIeCSIh
|
||||
BpbSe0QWuaCNSSLaePhXEP3BxQ2VHX3WpW1U6svHvCUiBScJAgcCAAAAAMMGIJEi
|
||||
9+yqkFKsNwX1H5II0riPudFpwBx2ypVjNk4aNb7Exl56Aac4tXEhz4fH41q0dAzF
|
||||
ww2erZaiUqmohQ4AFSw1jN/WOiDfb1DkjT/HJ8vXMGpwWdgFPoqsWzTNhd5VCcdL
|
||||
BmTfHnQZAAAAIAGMcsqVCXLclRhVamWciSxmnYF1FFs80W7dNUH07HUOAHh/S601
|
||||
If+/eZKDIj3jq7oOe2PzHSYEK+mpQD1hBpF2wpsGGBsOAAAALAWCZN8edAKbDCIh
|
||||
BpbSe0QWuaCNSSLaePhXEP3BxQ2VHX3WpW1U6svHvCUiAAAAANj3IBknZTPsMpWA
|
||||
we0Jl5gw/Dj4lWAGoJfWfk+6s3Q86Hag3Hu8VBsapzmul+vzy0KJa+ZRcZz2n8aj
|
||||
0vTl4sOZ0EcCdFDfkh/tR//gKkT6BiSBG86WoFq3f6U/RC+z0Ym7Dw==
|
||||
-----END PGP PRIVATE KEY BLOCK-----`;
|
||||
|
||||
const passphrase = 'hello world';
|
||||
const plaintext = input.createSomeMessage();
|
||||
const password1 = 'I am a password';
|
||||
@@ -1821,6 +1840,23 @@ aOU=
|
||||
})).to.be.rejectedWith(/No signing keys provided/);
|
||||
});
|
||||
|
||||
it('Signing with key which uses sha3 should generate a valid sha3 signature', async function() {
|
||||
const privKey = await openpgp.readKey({ armoredKey: priv_key_sha3 });
|
||||
const pubKey = privKey.toPublic();
|
||||
const text = 'Hello, world.';
|
||||
const message = await openpgp.createCleartextMessage({ text });
|
||||
|
||||
const cleartextMessage = await openpgp.sign({ message, signingKeys: privKey, format: 'armored' });
|
||||
const parsedArmored = await openpgp.readCleartextMessage({ cleartextMessage });
|
||||
expect(parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)).to.have.length(1);
|
||||
expect(
|
||||
parsedArmored.signature.packets.filterByTag(openpgp.enums.packet.signature)[0].hashAlgorithm
|
||||
).to.equal(openpgp.enums.hash.sha3_512);
|
||||
|
||||
const verified = await openpgp.verify({ message: parsedArmored, verificationKeys: pubKey, expectSigned: true });
|
||||
expect(verified.data).to.equal(text);
|
||||
});
|
||||
|
||||
it('should output cleartext message of expected format', async function() {
|
||||
const text = 'test';
|
||||
const message = await openpgp.createCleartextMessage({ text });
|
||||
|
||||
Reference in New Issue
Block a user