Skip to content

Commit 67587a2

Browse files
authored
fix: update P-521 bit length in Web Crypto (#2710)
ECDH over P-521 returns 66 bytes, i.e. 528 bits. Requesting 521 bits causes the last 7 bits of the derived value to be set to 0, which seems unintended (the Node.js version doesn't do this). So, request 66 * 8 bits instead. Also, remove the superfluous `namedCurve` property in the algorithm identifier passed to `deriveBits` (it's only necessary when importing or generating keys).
1 parent c628c44 commit 67587a2

File tree

3 files changed

+72
-46
lines changed

3 files changed

+72
-46
lines changed

packages/crypto/src/keys/ecdh/index.browser.ts

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ import webcrypto from '../../webcrypto/index.js'
77
import type { Curve } from './index.js'
88
import type { ECDHKey, ECDHKeyPair, JWKEncodedPrivateKey, JWKEncodedPublicKey } from '../interface.js'
99

10-
const bits = {
11-
'P-256': 256,
12-
'P-384': 384,
13-
'P-521': 521
10+
const curveLengths = {
11+
'P-256': 32,
12+
'P-384': 48,
13+
'P-521': 66
1414
}
1515

16-
const curveTypes = Object.keys(bits)
16+
const curveTypes = Object.keys(curveLengths)
1717
const names = curveTypes.join(' / ')
1818

1919
export async function generateEphemeralKeyPair (curve: Curve): Promise<ECDHKey> {
@@ -63,12 +63,10 @@ export async function generateEphemeralKeyPair (curve: Curve): Promise<ECDHKey>
6363
const buffer = await webcrypto.get().subtle.deriveBits(
6464
{
6565
name: 'ECDH',
66-
// @ts-expect-error namedCurve is missing from the types
67-
namedCurve: curve,
6866
public: key
6967
},
7068
privateKey,
71-
bits[curve]
69+
curveLengths[curve] * 8
7270
)
7371

7472
return new Uint8Array(buffer, 0, buffer.byteLength)
@@ -84,12 +82,6 @@ export async function generateEphemeralKeyPair (curve: Curve): Promise<ECDHKey>
8482
return ecdhKey
8583
}
8684

87-
const curveLengths = {
88-
'P-256': 32,
89-
'P-384': 48,
90-
'P-521': 66
91-
}
92-
9385
// Marshal converts a jwk encoded ECDH public key into the
9486
// form specified in section 4.3.6 of ANSI X9.62. (This is the format
9587
// go-ipfs uses)
Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,53 @@
11
import type { Curve } from '../../src/keys/ecdh/index.js'
22

3-
export interface GoEllipticKey {
4-
curve: Curve
5-
bob: {
6-
private: Uint8Array
7-
public: Uint8Array
3+
type GoEllipticKeys = {
4+
[key in Curve]: {
5+
alice: {
6+
private: Uint8Array
7+
public: Uint8Array
8+
}
9+
bob: {
10+
private: Uint8Array
11+
public: Uint8Array
12+
}
13+
shared: Uint8Array
814
}
915
}
1016

11-
export default {
12-
curve: 'P-256',
13-
bob: {
14-
private: Uint8Array.from([
15-
181, 217, 162, 151, 225, 36, 53, 253, 107, 66, 27, 27, 232, 72, 0, 0, 103, 167, 84, 62, 203, 91, 97, 137, 131, 193, 230, 126, 98, 242, 216, 170
16-
]),
17-
public: Uint8Array.from([
18-
4, 53, 59, 128, 56, 162, 250, 72, 141, 206, 117, 232, 57, 96, 39, 39, 247, 7, 27, 57, 251, 232, 120, 186, 21, 239, 176, 139, 195, 129, 125, 85, 11, 188, 191, 32, 227, 0, 6, 163, 101, 68, 208, 1, 43, 131, 124, 112, 102, 91, 104, 79, 16, 119, 152, 208, 4, 147, 155, 83, 20, 146, 104, 55, 90
19-
])
17+
const fixtures: GoEllipticKeys = {
18+
'P-256': {
19+
alice: {
20+
private: Uint8Array.from([37, 140, 166, 161, 212, 132, 254, 140, 126, 65, 100, 6, 148, 131, 215, 92, 208, 103, 221, 6, 128, 23, 135, 79, 255, 73, 11, 225, 82, 212, 107, 80]),
21+
public: Uint8Array.from([4, 122, 24, 120, 242, 177, 155, 244, 37, 171, 235, 22, 163, 121, 156, 182, 143, 217, 16, 233, 66, 127, 128, 60, 26, 57, 152, 210, 225, 251, 222, 8, 23, 85, 128, 21, 98, 28, 210, 66, 226, 90, 84, 107, 116, 233, 254, 6, 104, 58, 145, 7, 111, 60, 176, 86, 42, 191, 157, 27, 102, 77, 35, 66, 57])
22+
},
23+
bob: {
24+
private: Uint8Array.from([250, 49, 115, 112, 140, 2, 12, 1, 189, 35, 30, 96, 177, 1, 181, 249, 110, 31, 240, 17, 27, 30, 19, 58, 209, 1, 63, 147, 184, 139, 208, 171]),
25+
public: Uint8Array.from([4, 177, 148, 249, 217, 112, 201, 61, 113, 88, 206, 195, 25, 181, 241, 5, 244, 233, 252, 46, 233, 204, 48, 252, 250, 247, 66, 217, 130, 209, 62, 155, 242, 232, 26, 29, 150, 122, 131, 101, 138, 74, 137, 33, 136, 156, 46, 63, 213, 158, 51, 163, 91, 203, 21, 150, 227, 190, 218, 117, 235, 254, 148, 117, 199])
26+
},
27+
shared: Uint8Array.from([157, 145, 9, 131, 239, 13, 39, 207, 4, 151, 238, 38, 98, 169, 181, 194, 83, 36, 88, 238, 172, 133, 32, 251, 118, 90, 233, 47, 197, 40, 231, 182])
28+
},
29+
'P-384': {
30+
alice: {
31+
private: Uint8Array.from([220, 34, 222, 224, 82, 178, 23, 121, 165, 184, 228, 178, 77, 76, 66, 55, 35, 122, 195, 222, 229, 66, 41, 75, 221, 48, 32, 99, 117, 118, 26, 96, 156, 131, 97, 121, 17, 251, 157, 202, 50, 241, 238, 195, 126, 211, 223, 250]),
32+
public: Uint8Array.from([4, 91, 188, 238, 169, 244, 3, 234, 174, 151, 59, 224, 217, 125, 111, 92, 118, 37, 26, 14, 102, 223, 122, 181, 164, 142, 48, 97, 59, 33, 33, 99, 122, 51, 111, 125, 199, 218, 171, 248, 117, 45, 39, 9, 243, 31, 62, 156, 221, 95, 110, 148, 158, 232, 50, 83, 98, 91, 68, 162, 173, 125, 87, 56, 101, 118, 243, 47, 203, 135, 246, 194, 249, 78, 42, 70, 205, 229, 100, 59, 17, 153, 198, 185, 202, 146, 227, 242, 136, 36, 18, 169, 213, 227, 90, 77, 163])
33+
},
34+
bob: {
35+
private: Uint8Array.from([234, 194, 98, 206, 63, 134, 230, 192, 54, 44, 167, 95, 131, 97, 64, 109, 241, 143, 69, 216, 170, 102, 70, 53, 209, 24, 145, 207, 102, 240, 123, 165, 12, 4, 28, 47, 159, 143, 131, 96, 114, 43, 144, 227, 114, 166, 174, 232]),
36+
public: Uint8Array.from([4, 118, 187, 146, 219, 61, 124, 142, 240, 183, 239, 180, 86, 241, 181, 238, 4, 135, 205, 109, 253, 130, 91, 183, 151, 161, 208, 10, 92, 167, 37, 71, 1, 68, 174, 30, 49, 30, 161, 22, 23, 249, 82, 148, 58, 22, 238, 95, 213, 13, 63, 15, 219, 167, 210, 74, 101, 19, 70, 53, 199, 197, 103, 73, 110, 90, 195, 208, 28, 195, 96, 196, 11, 187, 26, 2, 233, 54, 75, 177, 213, 55, 196, 171, 164, 233, 218, 161, 221, 61, 92, 19, 153, 74, 149, 131, 50])
37+
},
38+
shared: Uint8Array.from([23, 182, 212, 185, 218, 192, 252, 17, 210, 198, 144, 134, 45, 210, 69, 232, 121, 210, 97, 36, 188, 115, 104, 137, 69, 79, 249, 125, 158, 151, 247, 142, 87, 137, 229, 225, 222, 244, 203, 22, 211, 85, 57, 130, 178, 12, 81, 43])
39+
},
40+
'P-521': {
41+
alice: {
42+
private: Uint8Array.from([1, 159, 121, 134, 240, 103, 228, 193, 28, 197, 193, 159, 230, 12, 193, 254, 160, 159, 230, 96, 254, 230, 241, 164, 158, 239, 164, 238, 165, 55, 119, 33, 89, 229, 193, 83, 97, 58, 245, 11, 5, 143, 253, 142, 176, 36, 225, 253, 133, 46, 148, 176, 144, 24, 157, 205, 50, 126, 110, 85, 238, 52, 207, 23, 64, 203]),
43+
public: Uint8Array.from([4, 1, 75, 171, 78, 143, 67, 235, 246, 10, 144, 92, 51, 186, 8, 32, 61, 204, 148, 130, 91, 86, 108, 157, 163, 134, 193, 160, 187, 177, 143, 112, 132, 227, 207, 178, 141, 199, 51, 249, 3, 245, 51, 37, 192, 0, 188, 239, 216, 244, 243, 168, 232, 49, 158, 164, 140, 87, 90, 220, 18, 26, 101, 235, 71, 206, 142, 1, 124, 224, 170, 139, 25, 6, 0, 17, 153, 45, 0, 41, 102, 106, 211, 255, 116, 191, 219, 66, 16, 72, 161, 235, 106, 41, 19, 132, 38, 64, 5, 90, 154, 22, 71, 178, 85, 52, 225, 195, 39, 112, 124, 136, 133, 42, 220, 110, 51, 237, 85, 55, 90, 105, 212, 252, 50, 113, 8, 205, 92, 246, 154, 49, 124])
44+
},
45+
bob: {
46+
private: Uint8Array.from([0, 48, 128, 144, 205, 13, 19, 62, 68, 13, 80, 138, 138, 166, 70, 7, 26, 180, 229, 237, 141, 10, 70, 72, 58, 100, 140, 3, 62, 14, 182, 241, 79, 229, 170, 205, 15, 50, 24, 238, 46, 194, 18, 179, 225, 238, 221, 25, 42, 49, 110, 133, 201, 21, 139, 155, 184, 170, 129, 214, 189, 202, 36, 19, 224, 86]),
47+
public: Uint8Array.from([4, 1, 222, 110, 155, 186, 20, 231, 105, 66, 36, 75, 141, 111, 195, 120, 250, 27, 80, 251, 156, 245, 226, 181, 242, 40, 218, 227, 78, 217, 162, 221, 54, 29, 67, 216, 82, 160, 196, 57, 241, 33, 54, 52, 23, 109, 156, 247, 253, 89, 199, 141, 27, 105, 232, 230, 97, 159, 156, 215, 224, 72, 147, 124, 16, 43, 69, 0, 174, 197, 20, 217, 121, 68, 66, 130, 205, 40, 209, 16, 46, 78, 244, 51, 162, 27, 141, 30, 146, 245, 211, 149, 81, 195, 174, 233, 42, 177, 16, 141, 217, 63, 193, 149, 68, 204, 216, 153, 167, 139, 194, 182, 96, 209, 249, 151, 172, 153, 24, 243, 4, 143, 120, 178, 228, 155, 14, 16, 222, 196, 96, 10, 154])
48+
},
49+
shared: Uint8Array.from([1, 135, 156, 15, 251, 68, 247, 200, 153, 189, 60, 183, 63, 215, 139, 185, 242, 96, 23, 86, 39, 175, 173, 170, 7, 189, 168, 209, 241, 1, 212, 136, 241, 212, 136, 90, 145, 48, 133, 46, 41, 142, 218, 133, 198, 127, 56, 9, 70, 35, 16, 156, 217, 58, 74, 72, 69, 188, 53, 11, 130, 164, 57, 160, 113, 76])
2050
}
21-
} satisfies GoEllipticKey
51+
}
52+
53+
export default fixtures

packages/crypto/test/keys/ephemeral-keys.spec.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { generateEphemeralKeyPair } from '../../src/keys/index.js'
55
import fixtures from '../fixtures/go-elliptic-key.js'
66
import type { Curve } from '../../src/keys/ecdh/index.js'
77

8-
const curves: Curve[] = ['P-256', 'P-384'] // 'P-521' fails in tests :( no clue why
8+
const curves: Curve[] = ['P-256', 'P-384', 'P-521']
99
const lengths: Record<string, number> = {
1010
'P-256': 65,
1111
'P-384': 97,
@@ -35,25 +35,27 @@ describe('generateEphemeralKeyPair', () => {
3535
})
3636

3737
describe('go interop', () => {
38-
it('generates a shared secret', async () => {
39-
const curve = fixtures.curve
38+
curves.forEach((curve) => {
39+
it(`generates a shared secret ${curve}`, async () => {
40+
const keys = await Promise.all([
41+
generateEphemeralKeyPair(curve),
42+
generateEphemeralKeyPair(curve)
43+
])
4044

41-
const keys = await Promise.all([
42-
generateEphemeralKeyPair(curve),
43-
generateEphemeralKeyPair(curve)
44-
])
45-
46-
const alice = keys[0]
47-
const bob = keys[1]
48-
bob.key = fixtures.bob.public
45+
const alice = keys[0]
46+
const bob = keys[1]
47+
alice.key = fixtures[curve].alice.public
48+
bob.key = fixtures[curve].bob.public
4949

50-
const secrets = await Promise.all([
51-
alice.genSharedKey(bob.key),
52-
bob.genSharedKey(alice.key, fixtures.bob)
53-
])
50+
const secrets = await Promise.all([
51+
alice.genSharedKey(bob.key, fixtures[curve].alice),
52+
bob.genSharedKey(alice.key, fixtures[curve].bob)
53+
])
5454

55-
expect(secrets[0]).to.eql(secrets[1])
56-
expect(secrets[0]).to.have.length(32)
55+
expect(secrets[0]).to.eql(secrets[1])
56+
expect(secrets[0]).to.eql(fixtures[curve].shared)
57+
expect(secrets[0]).to.have.length(secretLengths[curve])
58+
})
5759
})
5860
})
5961

0 commit comments

Comments
 (0)