GeezerVison is showing github.com — crypto: reject small-order EdDSA points during verify · nodejs/node@0908d76
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: src/crypto/crypto_sig.cc +143-1Lines changed: 143 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -78,6 +78,147 @@ bool ApplyRSAOptions(const EVPKeyPointer& pkey, | 78 | 78 | return true; | 79 | 79 | } | 80 | 80 |
| | 81 | +constexpr size_t kEd25519PointSize = 32; | | 82 | +constexpr size_t kEd448PointSize = 57; | | 83 | + | | 84 | +// Ed25519 has cofactor 8, so the first eight entries are the full | | 85 | +// canonical small-order subgroup: identity, one point of order 2, | | 86 | +// two points of order 4, and four points of order 8. | | 87 | +constexpr unsigned char kEd25519SmallOrderPoints[][kEd25519PointSize] = { | | 88 | + // Identity. | | 89 | + {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 90 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 91 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, | | 92 | + // Order 2. | | 93 | + {0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 94 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 95 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}, | | 96 | + // Order 4. | | 97 | + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 98 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 99 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}, | | 100 | + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 101 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 102 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, | | 103 | + // Order 8. | | 104 | + {0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, | | 105 | + 0x76, 0x0d, 0x10, 0x67, 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, | | 106 | + 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a}, | | 107 | + {0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, | | 108 | + 0x76, 0x0d, 0x10, 0x67, 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, | | 109 | + 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0xfa}, | | 110 | + {0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, | | 111 | + 0x89, 0xf2, 0xef, 0x98, 0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, | | 112 | + 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05}, | | 113 | + {0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, | | 114 | + 0x89, 0xf2, 0xef, 0x98, 0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, | | 115 | + 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x85}, | | 116 | + // Non-canonical encodings of the same small-order points. | | 117 | + {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 118 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 119 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}, | | 120 | + {0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 121 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 122 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, | | 123 | + {0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 124 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 125 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}, | | 126 | + {0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 127 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 128 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, | | 129 | + {0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 130 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 131 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, | | 132 | + {0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 133 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 134 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}, | | 135 | +}; | | 136 | + | | 137 | +// Ed448 has cofactor 4, so these four entries are the full canonical | | 138 | +// small-order subgroup: identity, one point of order 2, and two points | | 139 | +// of order 4. | | 140 | +constexpr unsigned char kEd448SmallOrderPoints[][kEd448PointSize] = { | | 141 | + // Identity. | | 142 | + {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 143 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 144 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 145 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 146 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, | | 147 | + // Order 2. | | 148 | + {0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 149 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 150 | + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 151 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | | 152 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, | | 153 | + // Order 4. | | 154 | + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 155 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 156 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 157 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 158 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, | | 159 | + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 160 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 161 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 162 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | | 163 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}, | | 164 | +}; | | 165 | + | | 166 | +template <size_t PointSize, size_t Count> | | 167 | +bool ContainsPoint(const unsigned char* candidate, | | 168 | + const unsigned char (&points)[Count][PointSize]) { | | 169 | + for (const auto& point : points) { | | 170 | + if (memcmp(candidate, point, PointSize) == 0) return true; | | 171 | + } | | 172 | + return false; | | 173 | +} | | 174 | + | | 175 | +bool IsSmallOrderEdDsaPoint(int id, | | 176 | + const unsigned char* candidate, | | 177 | + size_t size) { | | 178 | + switch (id) { | | 179 | + case EVP_PKEY_ED25519: | | 180 | + return size == kEd25519PointSize && | | 181 | + ContainsPoint(candidate, kEd25519SmallOrderPoints); | | 182 | + case EVP_PKEY_ED448: | | 183 | + return size == kEd448PointSize && | | 184 | + ContainsPoint(candidate, kEd448SmallOrderPoints); | | 185 | + default: | | 186 | + return false; | | 187 | + } | | 188 | +} | | 189 | + | | 190 | +bool HasSmallOrderEdDsaPoint(const EVPKeyPointer& key, | | 191 | + const ByteSource& signature) { | | 192 | + const int id = key.id(); | | 193 | + size_t point_size; | | 194 | + | | 195 | + switch (id) { | | 196 | + case EVP_PKEY_ED25519: | | 197 | + point_size = kEd25519PointSize; | | 198 | + break; | | 199 | + case EVP_PKEY_ED448: | | 200 | + point_size = kEd448PointSize; | | 201 | + break; | | 202 | + default: | | 203 | + return false; | | 204 | + } | | 205 | + | | 206 | + if (signature.size() != point_size * 2) return false; | | 207 | + | | 208 | + if (IsSmallOrderEdDsaPoint(id, signature.data<unsigned char>(), point_size)) { | | 209 | + return true; | | 210 | + } | | 211 | + | | 212 | + unsigned char raw_public_key[kEd448PointSize]; | | 213 | + size_t raw_public_key_size = point_size; | | 214 | + if (EVP_PKEY_get_raw_public_key( | | 215 | + key.get(), raw_public_key, &raw_public_key_size) != 1) { | | 216 | + return false; | | 217 | + } | | 218 | + | | 219 | + return IsSmallOrderEdDsaPoint(id, raw_public_key, raw_public_key_size); | | 220 | +} | | 221 | + | 81 | 222 | std::unique_ptr<BackingStore> Node_SignFinal(Environment* env, | 82 | 223 | EVPMDCtxPointer&& mdctx, | 83 | 224 | const EVPKeyPointer& pkey, | @@ -754,7 +895,8 @@ bool SignTraits::DeriveBits(Environment* env, | 754 | 895 | case SignConfiguration::Mode::Verify: { | 755 | 896 | auto buf = DataPointer::Alloc(1); | 756 | 897 | static_cast<char*>(buf.get())[0] = 0; | 757 | | - if (context.verify(params.data, params.signature)) { | | 898 | + if (context.verify(params.data, params.signature) && | | 899 | + !HasSmallOrderEdDsaPoint(key, params.signature)) { | 758 | 900 | static_cast<char*>(buf.get())[0] = 1; | 759 | 901 | } | 760 | 902 | *out = ByteSource::Allocated(buf.release()); | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-webcrypto-sign-verify-eddsa.js +41Lines changed: 41 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -15,6 +15,44 @@ const vectors = require('../fixtures/crypto/eddsa')(); | 15 | 15 |
| 16 | 16 | const supportsContext = hasOpenSSL(3, 2); | 17 | 17 |
| | 18 | +const smallOrderVerifyVectors = [ | | 19 | + { | | 20 | + name: 'Ed25519', | | 21 | + publicKey: Buffer.from( | | 22 | + 'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa', | | 23 | + 'hex'), | | 24 | + signature: Buffer.from( | | 25 | + 'c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a' + | | 26 | + '0000000000000000000000000000000000000000000000000000000000000000', | | 27 | + 'hex'), | | 28 | + data: Buffer.from( | | 29 | + '8c93255d71dcab10e8f379c26200f3c7bd5f09d9bc3068d3ef4edeb4853022b6', | | 30 | + 'hex'), | | 31 | + }, | | 32 | +]; | | 33 | + | | 34 | +if (!process.features.openssl_is_boringssl) { | | 35 | + smallOrderVerifyVectors.push({ | | 36 | + name: 'Ed448', | | 37 | + publicKey: Buffer.concat([Buffer.from([1]), Buffer.alloc(56)]), | | 38 | + signature: Buffer.concat([Buffer.from([1]), Buffer.alloc(113)]), | | 39 | + data: Buffer.from([1, 2, 3]), | | 40 | + }); | | 41 | +} | | 42 | + | | 43 | +async function testSmallOrderVerify({ name, publicKey, signature, data }) { | | 44 | + const key = await subtle.importKey( | | 45 | + 'raw', | | 46 | + publicKey, | | 47 | + { name }, | | 48 | + false, | | 49 | + ['verify']); | | 50 | + | | 51 | + assert.strictEqual( | | 52 | + await subtle.verify({ name }, key, signature, data), | | 53 | + false); | | 54 | +} | | 55 | + | 18 | 56 | async function testVerify({ name, | 19 | 57 | context, | 20 | 58 | publicKeyBuffer, | @@ -260,6 +298,9 @@ async function testSign({ name, | 260 | 298 | variations.push(testVerify(vector)); | 261 | 299 | variations.push(testSign(vector)); | 262 | 300 | }); | | 301 | + smallOrderVerifyVectors.forEach((vector) => { | | 302 | + variations.push(testSmallOrderVerify(vector)); | | 303 | + }); | 263 | 304 |
| 264 | 305 | await Promise.all(variations); | 265 | 306 | })().then(common.mustCall()); | |
Collapse file Copy file name to clipboardExpand all lines: test/wpt/status/WebCryptoAPI.cjs -19Lines changed: 0 additions & 19 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | 'use strict'; | 2 | 2 |
| 3 | | -const os = require('node:os'); | 4 | | - | 5 | 3 | const { hasOpenSSL } = require('../../common/crypto.js'); | 6 | 4 |
| 7 | | -const s390x = os.arch() === 's390x'; | 8 | | - | 9 | 5 | const conditionalFileSkips = {}; | 10 | 6 | const conditionalSubtestSkips = {}; | 11 | 7 |
| @@ -115,19 +111,4 @@ module.exports = { | 115 | 111 | 'historical.any.js': { | 116 | 112 | 'skip': 'Not relevant in Node.js context', | 117 | 113 | }, | 118 | | - 'sign_verify/eddsa_small_order_points.https.any.js': { | 119 | | - 'fail': { | 120 | | - 'note': 'see https://github.com/nodejs/node/issues/54572', | 121 | | - 'expected': [ | 122 | | - 'Ed25519 Verification checks with small-order key of order - Test 1', | 123 | | - 'Ed25519 Verification checks with small-order key of order - Test 2', | 124 | | - 'Ed25519 Verification checks with small-order key of order - Test 12', | 125 | | - 'Ed25519 Verification checks with small-order key of order - Test 13', | 126 | | - ...(s390x ? [] : [ | 127 | | - 'Ed25519 Verification checks with small-order key of order - Test 0', | 128 | | - 'Ed25519 Verification checks with small-order key of order - Test 11', | 129 | | - ]), | 130 | | - ], | 131 | | - }, | 132 | | - }, | 133 | 114 | }; |
|