From 5bfff907b444f5ca886e403b24b3bbaac1ac6926 Mon Sep 17 00:00:00 2001 From: larabr <7375870+larabr@users.noreply.github.com> Date: Thu, 2 May 2024 19:12:31 +0200 Subject: [PATCH] Move Brainpool curves implementation from noble-curves fork The main repo doesn't implement them --- .../elliptic/brainpool/brainpoolP256r1.ts | 23 +++++ .../elliptic/brainpool/brainpoolP384r1.ts | 23 +++++ .../elliptic/brainpool/brainpoolP512r1.ts | 23 +++++ .../public_key/elliptic/noble_curves.js | 6 +- test/crypto/brainpool_rfc7027.js | 83 +++++++++++++++++++ test/crypto/index.js | 2 + 6 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 src/crypto/public_key/elliptic/brainpool/brainpoolP256r1.ts create mode 100644 src/crypto/public_key/elliptic/brainpool/brainpoolP384r1.ts create mode 100644 src/crypto/public_key/elliptic/brainpool/brainpoolP512r1.ts create mode 100644 test/crypto/brainpool_rfc7027.js diff --git a/src/crypto/public_key/elliptic/brainpool/brainpoolP256r1.ts b/src/crypto/public_key/elliptic/brainpool/brainpoolP256r1.ts new file mode 100644 index 00000000..c5ae5395 --- /dev/null +++ b/src/crypto/public_key/elliptic/brainpool/brainpoolP256r1.ts @@ -0,0 +1,23 @@ +import { createCurve } from '@noble/curves/_shortw_utils'; +import { sha256 } from '@noble/hashes/sha256'; +import { Field } from '@noble/curves/abstract/modular'; + +// brainpoolP256r1: https://datatracker.ietf.org/doc/html/rfc5639#section-3.4 + +const Fp = Field(BigInt('0xa9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377')); +const CURVE_A = Fp.create(BigInt('0x7d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9')); +const CURVE_B = BigInt('0x26dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b6'); + +// prettier-ignore +export const brainpoolP256r1 = createCurve({ + a: CURVE_A, // Equation params: a, b + b: CURVE_B, + Fp, + // Curve order (q), total count of valid points in the field + n: BigInt('0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7'), + // Base (generator) point (x, y) + Gx: BigInt('0x8bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262'), + Gy: BigInt('0x547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997'), + h: BigInt(1), + lowS: false, +} as const, sha256); diff --git a/src/crypto/public_key/elliptic/brainpool/brainpoolP384r1.ts b/src/crypto/public_key/elliptic/brainpool/brainpoolP384r1.ts new file mode 100644 index 00000000..82f177df --- /dev/null +++ b/src/crypto/public_key/elliptic/brainpool/brainpoolP384r1.ts @@ -0,0 +1,23 @@ +import { createCurve } from '@noble/curves/_shortw_utils'; +import { sha384 } from '@noble/hashes/sha512'; +import { Field } from '@noble/curves/abstract/modular'; + +// brainpoolP384 r1: https://datatracker.ietf.org/doc/html/rfc5639#section-3.6 + +const Fp = Field(BigInt('0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec53')); +const CURVE_A = Fp.create(BigInt('0x7bc382c63d8c150c3c72080ace05afa0c2bea28e4fb22787139165efba91f90f8aa5814a503ad4eb04a8c7dd22ce2826')); +const CURVE_B = BigInt('0x04a8c7dd22ce28268b39b55416f0447c2fb77de107dcd2a62e880ea53eeb62d57cb4390295dbc9943ab78696fa504c11'); + +// prettier-ignore +export const brainpoolP384r1 = createCurve({ + a: CURVE_A, // Equation params: a, b + b: CURVE_B, + Fp, + // Curve order (q), total count of valid points in the field + n: BigInt('0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e9046565'), + // Base (generator) point (x, y) + Gx: BigInt('0x1d1c64f068cf45ffa2a63a81b7c13f6b8847a3e77ef14fe3db7fcafe0cbd10e8e826e03436d646aaef87b2e247d4af1e'), + Gy: BigInt('0x8abe1d7520f9c2a45cb1eb8e95cfd55262b70b29feec5864e19c054ff99129280e4646217791811142820341263c5315'), + h: BigInt(1), + lowS: false, +} as const, sha384); diff --git a/src/crypto/public_key/elliptic/brainpool/brainpoolP512r1.ts b/src/crypto/public_key/elliptic/brainpool/brainpoolP512r1.ts new file mode 100644 index 00000000..37ed3b3d --- /dev/null +++ b/src/crypto/public_key/elliptic/brainpool/brainpoolP512r1.ts @@ -0,0 +1,23 @@ +import { createCurve } from '@noble/curves/_shortw_utils'; +import { sha512 } from '@noble/hashes/sha512'; +import { Field } from '@noble/curves/abstract/modular'; + +// brainpoolP512r1: https://datatracker.ietf.org/doc/html/rfc5639#section-3.7 + +const Fp = Field(BigInt('0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3')); +const CURVE_A = Fp.create(BigInt('0x7830a3318b603b89e2327145ac234cc594cbdd8d3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94ca')); +const CURVE_B = BigInt('0x3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94cadc083e67984050b75ebae5dd2809bd638016f723'); + +// prettier-ignore +export const brainpoolP512r1 = createCurve({ + a: CURVE_A, // Equation params: a, b + b: CURVE_B, + Fp, + // Curve order (q), total count of valid points in the field + n: BigInt('0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90069'), + // Base (generator) point (x, y) + Gx: BigInt('0x81aee4bdd82ed9645a21322e9c4c6a9385ed9f70b5d916c1b43b62eef4d0098eff3b1f78e2d0d48d50d1687b93b97d5f7c6d5047406a5e688b352209bcb9f822'), + Gy: BigInt('0x7dde385d566332ecc0eabfa9cf7822fdf209f70024a57b1aa000c55b881f8111b2dcde494a5f485e5bca4bd88a2763aed1ca2b2fa8f0540678cd1e0f3ad80892'), + h: BigInt(1), + lowS: false, +} as const, sha512); diff --git a/src/crypto/public_key/elliptic/noble_curves.js b/src/crypto/public_key/elliptic/noble_curves.js index fce717dd..49af7169 100644 --- a/src/crypto/public_key/elliptic/noble_curves.js +++ b/src/crypto/public_key/elliptic/noble_curves.js @@ -4,12 +4,12 @@ * which share a lot of code anyway. */ -import { brainpoolP256r1 } from '@openpgp/noble-curves/brainpoolP256r1'; -import { brainpoolP384r1 } from '@openpgp/noble-curves/brainpoolP384r1'; -import { brainpoolP512r1 } from '@openpgp/noble-curves/brainpoolP512r1'; import { p256 as nistP256 } from '@noble/curves/p256'; import { p384 as nistP384 } from '@noble/curves/p384'; import { p521 as nistP521 } from '@noble/curves/p521'; +import { brainpoolP256r1 } from './brainpool/brainpoolP256r1'; +import { brainpoolP384r1 } from './brainpool/brainpoolP384r1'; +import { brainpoolP512r1 } from './brainpool/brainpoolP512r1'; import { x448, ed448 } from '@noble/curves/ed448'; import { secp256k1 } from '@noble/curves/secp256k1'; diff --git a/test/crypto/brainpool_rfc7027.js b/test/crypto/brainpool_rfc7027.js new file mode 100644 index 00000000..b28ff9cb --- /dev/null +++ b/test/crypto/brainpool_rfc7027.js @@ -0,0 +1,83 @@ +import { expect } from 'chai'; + +import { brainpoolP256r1 } from '../../src/crypto/public_key/elliptic/brainpool/brainpoolP256r1'; +import { brainpoolP384r1 } from '../../src/crypto/public_key/elliptic/brainpool/brainpoolP384r1'; +import { brainpoolP512r1 } from '../../src/crypto/public_key/elliptic/brainpool/brainpoolP512r1'; +import util from '../../src/util'; + +const rfc7027 = [ + { + 'curve': 'brainpoolP256r1', + 'dA': '81DB1EE100150FF2EA338D708271BE38300CB54241D79950F77B063039804F1D', + 'QAx': '44106E913F92BC02A1705D9953A8414DB95E1AAA49E81D9E85F929A8E3100BE5', + 'QAy': '8AB4846F11CACCB73CE49CBDD120F5A900A69FD32C272223F789EF10EB089BDC', + 'dB': '55E40BC41E37E3E2AD25C3C6654511FFA8474A91A0032087593852D3E7D76BD3', + 'QBx': '8D2D688C6CF93E1160AD04CC4429117DC2C41825E1E9FCA0ADDD34E6F1B39F7B', + 'QBy': '990C57520812BE512641E47034832106BC7D3E8DD0E4C7F1136D7006547CEC6A', + 'Zx': '89AFC39D41D3B327814B80940B042590F96556EC91E6AE7939BCE31F3A18BF2B', + 'Zy': '49C27868F4ECA2179BFD7D59B1E3BF34C1DBDE61AE12931648F43E59632504DE' + }, + { + 'curve': 'brainpoolP384r1', + 'dA': '1E20F5E048A5886F1F157C74E91BDE2B98C8B52D58E5003D57053FC4B0BD65D6F15EB5D1EE1610DF870795143627D042', + 'QAx': '68B665DD91C195800650CDD363C625F4E742E8134667B767B1B476793588F885AB698C852D4A6E77A252D6380FCAF068', + 'QAy': '55BC91A39C9EC01DEE36017B7D673A931236D2F1F5C83942D049E3FA20607493E0D038FF2FD30C2AB67D15C85F7FAA59', + 'dB': '032640BC6003C59260F7250C3DB58CE647F98E1260ACCE4ACDA3DD869F74E01F8BA5E0324309DB6A9831497ABAC96670', + 'QBx': '4D44326F269A597A5B58BBA565DA5556ED7FD9A8A9EB76C25F46DB69D19DC8CE6AD18E404B15738B2086DF37E71D1EB4', + 'QBy': '62D692136DE56CBE93BF5FA3188EF58BC8A3A0EC6C1E151A21038A42E9185329B5B275903D192F8D4E1F32FE9CC78C48', + 'Zx': '0BD9D3A7EA0B3D519D09D8E48D0785FB744A6B355E6304BC51C229FBBCE239BBADF6403715C35D4FB2A5444F575D4F42', + 'Zy': '0DF213417EBE4D8E40A5F76F66C56470C489A3478D146DECF6DF0D94BAE9E598157290F8756066975F1DB34B2324B7BD' + }, + { + 'curve': 'brainpoolP512r1', + 'dA': '16302FF0DBBB5A8D733DAB7141C1B45ACBC8715939677F6A56850A38BD87BD59B09E80279609FF333EB9D4C061231FB26F92EEB04982A5F1D1764CAD57665422', + 'QAx': '0A420517E406AAC0ACDCE90FCD71487718D3B953EFD7FBEC5F7F27E28C6149999397E91E029E06457DB2D3E640668B392C2A7E737A7F0BF04436D11640FD09FD', + 'QAy': '72E6882E8DB28AAD36237CD25D580DB23783961C8DC52DFA2EC138AD472A0FCEF3887CF62B623B2A87DE5C588301EA3E5FC269B373B60724F5E82A6AD147FDE7', + 'dB': '230E18E1BCC88A362FA54E4EA3902009292F7F8033624FD471B5D8ACE49D12CFABBC19963DAB8E2F1EBA00BFFB29E4D72D13F2224562F405CB80503666B25429', + 'QBx': '9D45F66DE5D67E2E6DB6E93A59CE0BB48106097FF78A081DE781CDB31FCE8CCBAAEA8DD4320C4119F1E9CD437A2EAB3731FA9668AB268D871DEDA55A5473199F', + 'QBy': '2FDC313095BCDD5FB3A91636F07A959C8E86B5636A1E930E8396049CB481961D365CC11453A06C719835475B12CB52FC3C383BCE35E27EF194512B71876285FA', + 'Zx': 'A7927098655F1F9976FA50A9D566865DC530331846381C87256BAF3226244B76D36403C024D7BBF0AA0803EAFF405D3D24F11A9B5C0BEF679FE1454B21C4CD1F', + 'Zy': '7DB71C3DEF63212841C463E881BDCF055523BD368240E6C3143BD8DEF8B3B3223B95E0F53082FF5E412F4222537A43DF1C6D25729DDB51620A832BE6A26680A2' + } +]; + +const hexToBigint = hex => BigInt(`0x${hex}`); + +// prettier-ignore +const BRAINPOOL = { + brainpoolP256r1, + brainpoolP384r1, + brainpoolP512r1 +}; + +export default () => describe('Brainpool curves (RFC7027)', () => { + it('Field orders', () => { + const vectors = { + brainpoolP256r1: BigInt('0xa9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377'), + brainpoolP384r1: BigInt('0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec53'), + brainpoolP512r1: BigInt('0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3') + }; + for (const n of Object.keys(vectors)) { expect(BRAINPOOL[n].CURVE.Fp.ORDER).to.deep.equal(vectors[n]); } + }); + + for (const v of rfc7027) { + it(v.curve, () => { + const curve = BRAINPOOL[v.curve]; + const secKeyA = util.hexToUint8Array(v.dA); + const pubKeyA = curve.getPublicKey(secKeyA); + const pubPointA = curve.ProjectivePoint.fromHex(pubKeyA); + expect(pubPointA.x).to.equal(hexToBigint(v.QAx)); + expect(pubPointA.y).to.equal(hexToBigint(v.QAy)); + const secKeyB = hexToBigint(v.dB); + const pubKeyB = curve.getPublicKey(secKeyB); + const pubPointB = curve.ProjectivePoint.fromHex(pubKeyB); + expect(pubPointB.x).to.equal(hexToBigint(v.QBx)); + expect(pubPointB.y).to.equal(hexToBigint(v.QBy)); + const shared = curve.getSharedSecret(secKeyA, pubKeyB); + const sharedPoint = curve.ProjectivePoint.fromHex(shared); + expect(sharedPoint.x).to.equal(hexToBigint(v.Zx)); + expect(sharedPoint.y).to.equal(hexToBigint(v.Zy)); + expect(shared).to.deep.equal(curve.getSharedSecret(secKeyB, pubKeyA)); + }); + } +}); diff --git a/test/crypto/index.js b/test/crypto/index.js index 205b9866..86f45445 100644 --- a/test/crypto/index.js +++ b/test/crypto/index.js @@ -2,6 +2,7 @@ import testCipher from './cipher'; import testHash from './hash'; import testCrypto from './crypto'; import testElliptic from './elliptic'; +import testBrainpoolRFC7027 from './brainpool_rfc7027'; import testECDH from './ecdh'; import testPKCS5 from './pkcs5'; import testAESKW from './aes_kw'; @@ -17,6 +18,7 @@ export default () => describe('Crypto', function () { testHash(); testCrypto(); testElliptic(); + testBrainpoolRFC7027(); testECDH(); testPKCS5(); testAESKW();