lemon-tls 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,6 +8,76 @@ import * as crypto from 'crypto';
8
8
  let TLS_VERSION_TLS1_2 = 0x0303;
9
9
  let TLS_VERSION_TLS1_3 = 0x0304;
10
10
 
11
+ /**
12
+ * Default SignatureScheme offers, in preference order.
13
+ *
14
+ * SINGLE SOURCE OF TRUTH, for the same reason ecdh.js owns SUPPORTED_GROUPS:
15
+ * this list must never advertise something scheme_info/sign_with_scheme below
16
+ * cannot actually produce or verify, and it was previously hardcoded in three
17
+ * consumers (TLSSession client defaults, TLSSocket server defaults,
18
+ * DTLSSession) that could drift apart.
19
+ *
20
+ * MODERN covers schemes valid in both TLS 1.3 and 1.2 (RFC 8447 §3 / RFC 8422
21
+ * §5.1.3).
22
+ *
23
+ * LEGACY_PKCS1 needs care, because "offer" and "use" are NOT the same thing
24
+ * (RFC 8446 §4.2.3): signature_algorithms describes signatures we can verify,
25
+ * and that includes the signatures ON CERTIFICATES IN THE CHAIN — which are
26
+ * very commonly RSASSA-PKCS1-v1_5. So these MUST be advertised even in a
27
+ * TLS 1.3-only handshake. What §4.4.3 forbids is *using* them in
28
+ * CertificateVerify, and that is enforced separately in scheme_info (which
29
+ * returns null for them at 1.3) and pick_scheme (whose 1.3 RSA candidates are
30
+ * PSS only). Withholding them from the offer made peers with PKCS#1-signed
31
+ * chains unable to authenticate at all.
32
+ */
33
+ const SIG_SCHEMES_MODERN = [
34
+ 0x0807, 0x0808, // ed25519, ed448
35
+ 0x0403, 0x0503, 0x0603, // ecdsa_secp256r1/384r1/521r1
36
+ 0x0804, 0x0805, 0x0806, // rsa_pss_rsae_sha256/384/512
37
+ ];
38
+
39
+ const SIG_SCHEMES_LEGACY_PKCS1 = [
40
+ 0x0401, 0x0501, 0x0601, // rsa_pkcs1_sha256/384/512
41
+ ];
42
+
43
+ /**
44
+ * SHA-1 schemes. Deliberately last, and deliberately still offered.
45
+ *
46
+ * Same offer/use split as the PKCS#1 set above, taken one step further: SHA-1
47
+ * is unacceptable for a handshake signature, and scheme_info already refuses
48
+ * these at TLS 1.3 so they can never be selected for CertificateVerify. But
49
+ * signature_algorithms also describes the signatures we can verify ON
50
+ * CERTIFICATES (RFC 8446 §4.2.3), and SHA-1-signed intermediates still exist
51
+ * in real chains. Withholding them from the offer tells a peer we cannot
52
+ * verify such a chain at all, which is a different — and wrong — statement
53
+ * from "we will not sign with SHA-1".
54
+ */
55
+ const SIG_SCHEMES_LEGACY_SHA1 = [
56
+ 0x0201, // rsa_pkcs1_sha1
57
+ 0x0203, // ecdsa_sha1
58
+ ];
59
+
60
+ /**
61
+ * Default offer list — the same for every version range, deliberately taking
62
+ * no parameter.
63
+ *
64
+ * An earlier version accepted an "includeTls12" flag and then ignored it,
65
+ * which is worse than having no parameter: three call sites passed three
66
+ * different meaningful-looking values and a reader would reasonably conclude
67
+ * the version range changed the offer. It does not.
68
+ *
69
+ * PKCS#1 is always offered because signature_algorithms also covers the
70
+ * signatures ON CERTIFICATES in the chain (RFC 8446 §4.2.3). Where a scheme
71
+ * may be USED is enforced by scheme_info / scheme_matches_key, not by
72
+ * trimming this list.
73
+ */
74
+ function default_signature_schemes() {
75
+ return SIG_SCHEMES_MODERN
76
+ .concat(SIG_SCHEMES_LEGACY_PKCS1)
77
+ .concat(SIG_SCHEMES_LEGACY_SHA1);
78
+ }
79
+
80
+
11
81
  /**
12
82
  * Decode a SignatureScheme (u16) into hash/sig/isPSS info.
13
83
  * Returns null if the scheme is unsupported for the given TLS version.
@@ -15,30 +85,44 @@ let TLS_VERSION_TLS1_3 = 0x0304;
15
85
  function scheme_info(version, scheme) {
16
86
  let h = (scheme >>> 8) & 0xff, s = scheme & 0xff;
17
87
 
18
- // EdDSA (TLS 1.3 only)
88
+ // SignatureScheme codepoints are a single global registry, not per-version.
89
+ // RFC 8446 defined them, and RFC 8447 §3 / RFC 8422 §5.1.3 make the PSS and
90
+ // EdDSA entries usable in TLS 1.2 as well. Treating them as "TLS 1.3 only"
91
+ // is what made a modern peer unverifiable over TLS 1.2: OpenSSL 3.x signs
92
+ // ServerKeyExchange with rsa_pss_rsae_sha256 (0x0804) by default, and this
93
+ // returned null for it — the signature then failed to verify not because it
94
+ // was wrong but because we refused to describe it.
95
+ //
96
+ // Handled here, before the legacy (hash, sig) decoding below, so both
97
+ // versions agree on every modern codepoint. sign_with_scheme and
98
+ // verify_with_scheme share this function, so signing and verification can
99
+ // never drift apart.
100
+
101
+ // EdDSA
19
102
  if (scheme === 0x0807 || scheme === 0x0808) {
20
- if (version === TLS_VERSION_TLS1_2) return null;
21
103
  return { hash: null, sig: 'eddsa', isPSS: false };
22
104
  }
23
105
 
24
- // TLS 1.3: RSA means RSA-PSS
25
- if (version === TLS_VERSION_TLS1_3) {
26
- if (scheme === 0x0804) return { hash: 'sha256', sig: 'rsa', isPSS: true };
27
- if (scheme === 0x0805) return { hash: 'sha384', sig: 'rsa', isPSS: true };
28
- if (scheme === 0x0806) return { hash: 'sha512', sig: 'rsa', isPSS: true };
29
- if (scheme === 0x0403) return { hash: 'sha256', sig: 'ecdsa', isPSS: false };
30
- if (scheme === 0x0503) return { hash: 'sha384', sig: 'ecdsa', isPSS: false };
31
- if (scheme === 0x0603) return { hash: 'sha512', sig: 'ecdsa', isPSS: false };
32
- return null;
33
- }
106
+ // RSASSA-PSS (rsa_pss_rsae_* and rsa_pss_pss_*)
107
+ if (scheme === 0x0804 || scheme === 0x0809) return { hash: 'sha256', sig: 'rsa', isPSS: true };
108
+ if (scheme === 0x0805 || scheme === 0x080a) return { hash: 'sha384', sig: 'rsa', isPSS: true };
109
+ if (scheme === 0x0806 || scheme === 0x080b) return { hash: 'sha512', sig: 'rsa', isPSS: true };
110
+
111
+ // ECDSA with a fixed curve+hash pairing
112
+ if (scheme === 0x0403) return { hash: 'sha256', sig: 'ecdsa', isPSS: false };
113
+ if (scheme === 0x0503) return { hash: 'sha384', sig: 'ecdsa', isPSS: false };
114
+ if (scheme === 0x0603) return { hash: 'sha512', sig: 'ecdsa', isPSS: false };
115
+
116
+ // TLS 1.3 forbids the legacy PKCS#1 v1.5 codepoints in CertificateVerify
117
+ // (RFC 8446 §4.4.3); TLS 1.2 still uses them.
118
+ if (version === TLS_VERSION_TLS1_3) return null;
34
119
 
35
120
  // TLS 1.2: classic (hash, sig) interpretation
121
+ if (h === 0x02 && s === 0x01) return { hash: 'sha1', sig: 'rsa', isPSS: false };
36
122
  if (h === 0x04 && s === 0x01) return { hash: 'sha256', sig: 'rsa', isPSS: false };
37
123
  if (h === 0x05 && s === 0x01) return { hash: 'sha384', sig: 'rsa', isPSS: false };
38
124
  if (h === 0x06 && s === 0x01) return { hash: 'sha512', sig: 'rsa', isPSS: false };
39
- if (h === 0x04 && s === 0x03) return { hash: 'sha256', sig: 'ecdsa', isPSS: false };
40
- if (h === 0x05 && s === 0x03) return { hash: 'sha384', sig: 'ecdsa', isPSS: false };
41
- if (h === 0x06 && s === 0x03) return { hash: 'sha512', sig: 'ecdsa', isPSS: false };
125
+ if (h === 0x02 && s === 0x03) return { hash: 'sha1', sig: 'ecdsa', isPSS: false };
42
126
 
43
127
  return null;
44
128
  }
@@ -46,32 +130,37 @@ function scheme_info(version, scheme) {
46
130
  /**
47
131
  * Pick the best signature scheme given the server's key type and client's supported list.
48
132
  */
49
- function pick_scheme(version, certKeyObj, clientSupported) {
50
- let cands = [];
51
- if (certKeyObj.asymmetricKeyType === 'rsa') {
52
- if (version === TLS_VERSION_TLS1_3) cands.push(0x0804, 0x0805, 0x0806);
53
- else cands.push(0x0401, 0x0501, 0x0601);
54
- } else if (certKeyObj.asymmetricKeyType === 'ec') {
55
- let c = (certKeyObj.asymmetricKeyDetails && certKeyObj.asymmetricKeyDetails.namedCurve) || '';
56
- if (c === 'prime256v1') cands.push(0x0403);
57
- if (c === 'secp384r1') cands.push(0x0503);
58
- if (c === 'secp521r1') cands.push(0x0603);
59
- } else if (certKeyObj.asymmetricKeyType === 'ed25519' && version === TLS_VERSION_TLS1_3) {
60
- cands.push(0x0807);
61
- } else if (certKeyObj.asymmetricKeyType === 'ed448' && version === TLS_VERSION_TLS1_3) {
62
- cands.push(0x0808);
133
+ function pick_scheme(version, certKeyObj, peerSupported) {
134
+ // Iterate the PEER's list, in the peer's own order.
135
+ //
136
+ // RFC 8446 §4.2.3: signature_algorithms is "in descending order of
137
+ // preference" — it is the sender telling us what it wants, not merely what
138
+ // it tolerates. Walking OUR order instead and taking the first entry the
139
+ // peer happened to include inverts that: a peer asking for
140
+ // rsa_pss_rsae_sha384 first was answered with sha256 purely because sha256
141
+ // sits earlier in our own list. Our preference only decides among schemes
142
+ // the peer did not rank, which is nothing — so it has no say here at all.
143
+ //
144
+ // scheme_matches_key does the filtering (key type, curve, and version — it
145
+ // rejects rsa_pkcs1_* and SHA-1 at TLS 1.3 because scheme_info does), so a
146
+ // peer cannot steer us into a scheme that is unusable with our certificate
147
+ // or forbidden at this version just by listing it first.
148
+ if (!Array.isArray(peerSupported) || peerSupported.length === 0) return null;
149
+
150
+ let picked = null;
151
+ for (let i = 0; i < peerSupported.length; i++) {
152
+ let s2 = peerSupported[i];
153
+ if (typeof s2 !== 'number') continue;
154
+ if (!scheme_matches_key(version, s2, certKeyObj)) continue;
155
+ picked = s2;
156
+ break;
63
157
  }
64
158
 
65
- let pref = (version === TLS_VERSION_TLS1_3)
66
- ? [0x0807, 0x0808, 0x0403, 0x0503, 0x0603, 0x0804, 0x0805, 0x0806]
67
- : [0x0403, 0x0503, 0x0603, 0x0401, 0x0501, 0x0601];
68
159
 
69
- for (let s of pref) {
70
- if (cands.includes(s) && clientSupported.includes(s)) return s;
71
- }
72
- return null;
160
+ return picked;
73
161
  }
74
162
 
163
+
75
164
  /**
76
165
  * Sign data using the given scheme. Works for both TLS 1.2 and 1.3.
77
166
  */
@@ -106,24 +195,128 @@ function sign_with_scheme(version, scheme, tbs, certKeyObj) {
106
195
  }
107
196
 
108
197
  /**
109
- * Normalize a public key from the wire for TLS 1.2 ECDHE.
110
- * P-256: add 0x04 prefix if missing. X25519: strip 0x04 prefix if present.
198
+ * Does `scheme` legitimately go with `keyObj`?
199
+ *
200
+ * SINGLE SOURCE OF TRUTH for the scheme<->key relationship, used in BOTH
201
+ * directions: pick_scheme asks "which scheme may I use with my key" and
202
+ * verify_with_scheme asks "may the peer's scheme be checked against its key".
203
+ * Those are the same question, and they were previously answered by two
204
+ * separate hand-rolled tables that had already drifted (pick_scheme still
205
+ * restricted EdDSA to TLS 1.3 after scheme_info was corrected to allow it at
206
+ * 1.2 per RFC 8422).
207
+ *
208
+ * A scheme names an algorithm AND the kind of key it may be used with
209
+ * (RFC 8446 §4.2.3):
210
+ * rsa_pss_rsae_* → plain rsaEncryption key
211
+ * rsa_pss_pss_* → RSASSA-PSS key
212
+ * rsa_pkcs1_* → plain RSA, and never valid in a TLS 1.3
213
+ * CertificateVerify (scheme_info returns null there)
214
+ * ecdsa_* → EC key on exactly the curve the scheme names
215
+ * ed25519/ed448 → the matching Edwards key
216
+ * Returns false for any scheme unusable at this version, so callers get
217
+ * version filtering for free.
111
218
  */
112
- function normalizeServerPubKeyForTls12(group, pub) {
113
- let p = pub;
114
- if (group === 0x0017) { // P-256
115
- if (p.length === 64) {
116
- const tmp = new Uint8Array(65); tmp[0] = 0x04; tmp.set(p, 1); p = tmp;
219
+ function scheme_matches_key(version, scheme, keyObj) {
220
+ let info = scheme_info(version, scheme);
221
+ if (!info || !keyObj) return false;
222
+
223
+ let kt = keyObj.asymmetricKeyType;
224
+
225
+ if (info.sig === 'rsa') {
226
+ if (info.isPSS) {
227
+ // 0x0809-0x080b are the *_pss_pss_* family; 0x0804-0x0806 are *_pss_rsae_*.
228
+ let wantsPssKey = (scheme === 0x0809 || scheme === 0x080a || scheme === 0x080b);
229
+ if (wantsPssKey) return kt === 'rsa-pss';
230
+ return kt === 'rsa';
117
231
  }
118
- } else if (group === 0x001d) { // X25519
119
- if (p.length === 33 && p[0] === 0x04) p = p.slice(1);
232
+ return kt === 'rsa';
233
+ }
234
+
235
+ if (info.sig === 'ecdsa') {
236
+ if (kt !== 'ec') return false;
237
+ // The curve is part of the scheme, not a free choice: signing P-256 data
238
+ // with a P-384 key (or vice versa) is a type confusion the RFC forbids.
239
+ let curve = null;
240
+ try {
241
+ curve = keyObj.asymmetricKeyDetails && keyObj.asymmetricKeyDetails.namedCurve;
242
+ } catch (e) { curve = null; }
243
+ if (!curve) return true; // unknown curve metadata — let the crypto layer decide
244
+ let want = (scheme === 0x0403) ? 'prime256v1'
245
+ : (scheme === 0x0503) ? 'secp384r1'
246
+ : (scheme === 0x0603) ? 'secp521r1' : null;
247
+ return want === null || curve === want;
248
+ }
249
+
250
+ if (info.sig === 'eddsa') {
251
+ if (scheme === 0x0807) return kt === 'ed25519';
252
+ if (scheme === 0x0808) return kt === 'ed448';
253
+ return false;
120
254
  }
121
- return p;
255
+
256
+ return false;
257
+ }
258
+
259
+ /**
260
+ * Verify a signature made with the given scheme. The exact mirror of
261
+ * sign_with_scheme — same scheme decoding, same padding/salt rules — so the
262
+ * two can never drift apart.
263
+ *
264
+ * @param version negotiated TLS version (0x0303 / 0x0304)
265
+ * @param scheme SignatureScheme from the wire (NOT a locally chosen one)
266
+ * @param tbs the exact bytes that were signed
267
+ * @param publicKey crypto.KeyObject of the peer's certificate public key
268
+ * @param signature signature bytes from the wire
269
+ * @returns {boolean} true only if the signature verifies
270
+ */
271
+ function verify_with_scheme(version, scheme, tbs, publicKey, signature) {
272
+ let info = scheme_info(version, scheme);
273
+ if (!info) return false;
274
+
275
+ // Scheme<->key binding is one named concept (see scheme_matches_key),
276
+ // shared with pick_scheme so the two can never disagree.
277
+ if (!scheme_matches_key(version, scheme, publicKey)) return false;
278
+
279
+ try {
280
+ if (info.sig === 'eddsa') {
281
+ return crypto.verify(null, tbs, publicKey, signature);
282
+ }
283
+
284
+ if (info.sig === 'ecdsa') {
285
+ // node's default DER encoding is what TLS uses for ECDSA signatures.
286
+ return crypto.verify(info.hash, tbs, publicKey, signature);
287
+ }
288
+
289
+ if (info.sig === 'rsa') {
290
+ if (info.isPSS) {
291
+ let saltLen = (info.hash === 'sha256') ? 32 : (info.hash === 'sha384') ? 48 : 64;
292
+ return crypto.verify(info.hash, tbs, {
293
+ key: publicKey,
294
+ padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
295
+ saltLength: saltLen
296
+ }, signature);
297
+ }
298
+ return crypto.verify(info.hash, tbs, {
299
+ key: publicKey,
300
+ padding: crypto.constants.RSA_PKCS1_PADDING
301
+ }, signature);
302
+ }
303
+ } catch (e) {
304
+ // Malformed signature encoding (bad DER, wrong size, ...) — node throws
305
+ // rather than returning false. A throw here is still a verification
306
+ // failure, never an acceptance.
307
+ return false;
308
+ }
309
+
310
+ return false;
122
311
  }
123
312
 
124
313
  export {
125
314
  scheme_info,
126
315
  pick_scheme,
127
316
  sign_with_scheme,
128
- normalizeServerPubKeyForTls12
317
+ verify_with_scheme,
318
+ SIG_SCHEMES_MODERN,
319
+ SIG_SCHEMES_LEGACY_PKCS1,
320
+ SIG_SCHEMES_LEGACY_SHA1,
321
+ default_signature_schemes
129
322
  };