lemon-tls 0.1.0 → 0.2.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.
- package/README.md +432 -122
- package/index.cjs +28 -0
- package/index.d.ts +283 -0
- package/index.js +70 -15
- package/lemontls.svg +1 -1
- package/package.json +120 -62
- package/src/compat.js +235 -0
- package/src/crypto.js +580 -0
- package/src/record.js +232 -0
- package/{secure_context.js → src/secure_context.js} +196 -196
- package/src/session/ecdh.js +61 -0
- package/src/session/message.js +203 -0
- package/src/session/signing.js +129 -0
- package/src/tls_session.js +2204 -0
- package/src/tls_socket.js +877 -0
- package/{utils.js → src/utils.js} +100 -87
- package/{wire.js → src/wire.js} +1499 -1672
- package/crypto.js +0 -383
- package/tls_server.js +0 -0
- package/tls_session.js +0 -1441
- package/tls_socket.js +0 -456
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ECDH key generation and shared secret helpers using node:crypto.
|
|
3
|
+
* Supports X25519 and P-256 (secp256r1).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as crypto from 'crypto';
|
|
7
|
+
|
|
8
|
+
// DER prefixes for raw X25519 key import/export
|
|
9
|
+
let X25519_PKCS8_PREFIX = Buffer.from('302e020100300506032b656e04220420', 'hex');
|
|
10
|
+
let X25519_SPKI_PREFIX = Buffer.from('302a300506032b656e032100', 'hex');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Derive the X25519 public key from a 32-byte private key.
|
|
14
|
+
*/
|
|
15
|
+
function x25519_get_public_key(privateKeyRaw) {
|
|
16
|
+
let der = Buffer.concat([X25519_PKCS8_PREFIX, Buffer.from(privateKeyRaw)]);
|
|
17
|
+
let privObj = crypto.createPrivateKey({ key: der, format: 'der', type: 'pkcs8' });
|
|
18
|
+
let pubObj = crypto.createPublicKey(privObj);
|
|
19
|
+
let spki = pubObj.export({ type: 'spki', format: 'der' });
|
|
20
|
+
return new Uint8Array(spki.subarray(X25519_SPKI_PREFIX.length));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Compute X25519 shared secret from local private key and remote public key (both raw 32 bytes).
|
|
25
|
+
*/
|
|
26
|
+
function x25519_get_shared_secret(localPrivateRaw, remotePublicRaw) {
|
|
27
|
+
let privDer = Buffer.concat([X25519_PKCS8_PREFIX, Buffer.from(localPrivateRaw)]);
|
|
28
|
+
let pubDer = Buffer.concat([X25519_SPKI_PREFIX, Buffer.from(remotePublicRaw)]);
|
|
29
|
+
let privObj = crypto.createPrivateKey({ key: privDer, format: 'der', type: 'pkcs8' });
|
|
30
|
+
let pubObj = crypto.createPublicKey({ key: pubDer, format: 'der', type: 'spki' });
|
|
31
|
+
return new Uint8Array(crypto.diffieHellman({ privateKey: privObj, publicKey: pubObj }));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Generate a P-256 keypair. Returns { private_key: Uint8Array, public_key: Uint8Array(65) }.
|
|
36
|
+
* Public key is uncompressed format (0x04 || x || y).
|
|
37
|
+
*/
|
|
38
|
+
function p256_generate_keypair() {
|
|
39
|
+
let ecdh = crypto.createECDH('prime256v1');
|
|
40
|
+
ecdh.generateKeys();
|
|
41
|
+
return {
|
|
42
|
+
private_key: new Uint8Array(ecdh.getPrivateKey()),
|
|
43
|
+
public_key: new Uint8Array(ecdh.getPublicKey(null, 'uncompressed'))
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Compute P-256 shared secret (raw x-coordinate, 32 bytes).
|
|
49
|
+
*/
|
|
50
|
+
function p256_get_shared_secret(localPrivateRaw, remotePublicRaw) {
|
|
51
|
+
let ecdh = crypto.createECDH('prime256v1');
|
|
52
|
+
ecdh.setPrivateKey(Buffer.from(localPrivateRaw));
|
|
53
|
+
return new Uint8Array(ecdh.computeSecret(Buffer.from(remotePublicRaw)));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export {
|
|
57
|
+
x25519_get_public_key,
|
|
58
|
+
x25519_get_shared_secret,
|
|
59
|
+
p256_generate_keypair,
|
|
60
|
+
p256_get_shared_secret
|
|
61
|
+
};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TLS handshake message building, parsing, and hello normalization.
|
|
3
|
+
* Wraps wire.js functions with higher-level type-driven dispatch.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as wire from '../wire.js';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Normalize a parsed hello into a flat object with named extension fields.
|
|
11
|
+
*/
|
|
12
|
+
function normalize_hello(hello) {
|
|
13
|
+
let out = {};
|
|
14
|
+
|
|
15
|
+
for (let key in hello) {
|
|
16
|
+
out[key] = hello[key];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if ('extensions' in hello && Array.isArray(hello.extensions)) {
|
|
20
|
+
for (let i = 0; i < hello.extensions.length; i++) {
|
|
21
|
+
let e = hello.extensions[i];
|
|
22
|
+
let name = e.name;
|
|
23
|
+
let value = e.value;
|
|
24
|
+
|
|
25
|
+
if (name === 'SERVER_NAME') {
|
|
26
|
+
out.sni = value;
|
|
27
|
+
} else if (name === 'ALPN') {
|
|
28
|
+
out.alpn = value;
|
|
29
|
+
} else if (name === 'KEY_SHARE') {
|
|
30
|
+
if (!('key_groups' in out)) out.key_groups = [];
|
|
31
|
+
if (!('supported_groups' in out)) out.supported_groups = [];
|
|
32
|
+
for (let i2 in value) {
|
|
33
|
+
if (out.supported_groups.indexOf(value[i2].group) < 0) {
|
|
34
|
+
out.supported_groups.push(value[i2].group);
|
|
35
|
+
}
|
|
36
|
+
out.key_groups.push({
|
|
37
|
+
group: value[i2].group,
|
|
38
|
+
public_key: value[i2].key_exchange
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
} else if (name === 'SUPPORTED_VERSIONS') {
|
|
42
|
+
out.supported_versions = value;
|
|
43
|
+
} else if (name === 'SIGNATURE_ALGORITHMS') {
|
|
44
|
+
out.signature_algorithms = value;
|
|
45
|
+
} else if (name === 'SIGNATURE_ALGORITHMS_CERT') {
|
|
46
|
+
out.signature_algorithms_cert = value;
|
|
47
|
+
} else if (name === 'SUPPORTED_GROUPS') {
|
|
48
|
+
out.supported_groups = value;
|
|
49
|
+
} else if (name === 'COOKIE') {
|
|
50
|
+
out.cookie = value;
|
|
51
|
+
} else if (name === 'EARLY_DATA') {
|
|
52
|
+
out.early_data = value;
|
|
53
|
+
} else if (name === 'PSK_KEY_EXCHANGE_MODES') {
|
|
54
|
+
out.psk_modes = value;
|
|
55
|
+
} else if (name === 'PRE_SHARED_KEY') {
|
|
56
|
+
out.pre_shared_key = value;
|
|
57
|
+
} else if (name === 'RENEGOTIATION_INFO') {
|
|
58
|
+
out.renegotiation_info = value;
|
|
59
|
+
} else if (name === 'STATUS_REQUEST') {
|
|
60
|
+
out.status_request = value;
|
|
61
|
+
} else if (name === 'MAX_FRAGMENT_LENGTH') {
|
|
62
|
+
out.max_fragment_length = value;
|
|
63
|
+
} else if (name === 'CERTIFICATE_AUTHORITIES') {
|
|
64
|
+
out.certificate_authorities = value;
|
|
65
|
+
} else if (name === 'SCT') {
|
|
66
|
+
out.sct = value;
|
|
67
|
+
} else if (name === 'HEARTBEAT') {
|
|
68
|
+
out.heartbeat = value;
|
|
69
|
+
} else if (name === 'USE_SRTP') {
|
|
70
|
+
out.use_srtp = value;
|
|
71
|
+
} else {
|
|
72
|
+
if (!('unknown' in out)) out.unknown = [];
|
|
73
|
+
out.unknown.push(e);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!('supported_versions' in out)) {
|
|
79
|
+
out.supported_versions = [];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (out.supported_versions.indexOf(out.version) < 0) {
|
|
83
|
+
out.supported_versions.push(out.version);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Build a TLS handshake message from a high-level params object.
|
|
92
|
+
* Returns a Uint8Array with handshake header + body.
|
|
93
|
+
*/
|
|
94
|
+
function build_tls_message(params) {
|
|
95
|
+
let type = 0;
|
|
96
|
+
let body = null;
|
|
97
|
+
|
|
98
|
+
if (params.type == 'server_hello') {
|
|
99
|
+
type = wire.TLS_MESSAGE_TYPE.SERVER_HELLO;
|
|
100
|
+
body = wire.build_hello('server', params);
|
|
101
|
+
} else if (params.type == 'client_hello') {
|
|
102
|
+
type = wire.TLS_MESSAGE_TYPE.CLIENT_HELLO;
|
|
103
|
+
body = wire.build_hello('client', params);
|
|
104
|
+
} else if (params.type == 'server_key_exchange') {
|
|
105
|
+
type = wire.TLS_MESSAGE_TYPE.SERVER_KEY_EXCHANGE;
|
|
106
|
+
body = wire.build_server_key_exchange_ecdhe(params);
|
|
107
|
+
} else if (params.type == 'client_key_exchange') {
|
|
108
|
+
type = wire.TLS_MESSAGE_TYPE.CLIENT_KEY_EXCHANGE;
|
|
109
|
+
body = wire.build_client_key_exchange_ecdhe(params.public_key);
|
|
110
|
+
} else if (params.type == 'server_hello_done') {
|
|
111
|
+
type = wire.TLS_MESSAGE_TYPE.SERVER_HELLO_DONE;
|
|
112
|
+
body = new Uint8Array(0);
|
|
113
|
+
} else if (params.type == 'encrypted_extensions') {
|
|
114
|
+
type = wire.TLS_MESSAGE_TYPE.ENCRYPTED_EXTENSIONS;
|
|
115
|
+
body = wire.build_extensions(params.extensions);
|
|
116
|
+
} else if (params.type == 'certificate') {
|
|
117
|
+
type = wire.TLS_MESSAGE_TYPE.CERTIFICATE;
|
|
118
|
+
body = wire.build_certificate(params);
|
|
119
|
+
} else if (params.type == 'certificate_verify') {
|
|
120
|
+
type = wire.TLS_MESSAGE_TYPE.CERTIFICATE_VERIFY;
|
|
121
|
+
body = wire.build_certificate_verify(params.scheme, params.signature);
|
|
122
|
+
} else if (params.type == 'finished') {
|
|
123
|
+
type = wire.TLS_MESSAGE_TYPE.FINISHED;
|
|
124
|
+
body = params.data;
|
|
125
|
+
} else if (params.type == 'key_update') {
|
|
126
|
+
type = wire.TLS_MESSAGE_TYPE.KEY_UPDATE;
|
|
127
|
+
body = wire.build_key_update(params.request_update);
|
|
128
|
+
} else if (params.type == 'certificate_request') {
|
|
129
|
+
type = wire.TLS_MESSAGE_TYPE.CERTIFICATE_REQUEST;
|
|
130
|
+
body = wire.build_certificate_request(params);
|
|
131
|
+
} else if (params.type == 'hello_retry_request') {
|
|
132
|
+
type = wire.TLS_MESSAGE_TYPE.SERVER_HELLO; // HRR uses ServerHello type with magic random
|
|
133
|
+
body = wire.build_hello_retry_request(params);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return wire.build_message(type, body);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Parse a raw TLS handshake message into a typed object.
|
|
142
|
+
*/
|
|
143
|
+
function parse_tls_message(data) {
|
|
144
|
+
let out = {};
|
|
145
|
+
let message = wire.parse_message(data);
|
|
146
|
+
|
|
147
|
+
if (message.type == wire.TLS_MESSAGE_TYPE.CLIENT_HELLO || message.type == wire.TLS_MESSAGE_TYPE.SERVER_HELLO) {
|
|
148
|
+
let hello = wire.parse_hello(message.type, message.body);
|
|
149
|
+
out = normalize_hello(hello);
|
|
150
|
+
|
|
151
|
+
} else if (message.type == wire.TLS_MESSAGE_TYPE.SERVER_KEY_EXCHANGE) {
|
|
152
|
+
out = wire.parse_server_key_exchange(message.body);
|
|
153
|
+
out.type = 'server_key_exchange';
|
|
154
|
+
|
|
155
|
+
} else if (message.type == wire.TLS_MESSAGE_TYPE.CLIENT_KEY_EXCHANGE) {
|
|
156
|
+
out.body = message.body;
|
|
157
|
+
out.public_key = message.body.slice(1);
|
|
158
|
+
out.type = 'client_key_exchange';
|
|
159
|
+
|
|
160
|
+
} else if (message.type == wire.TLS_MESSAGE_TYPE.SERVER_HELLO_DONE) {
|
|
161
|
+
out.body = message.body;
|
|
162
|
+
out.type = 'server_hello_done';
|
|
163
|
+
|
|
164
|
+
} else if (message.type == wire.TLS_MESSAGE_TYPE.ENCRYPTED_EXTENSIONS) {
|
|
165
|
+
out = normalize_hello({
|
|
166
|
+
extensions: wire.parse_extensions(message.body)
|
|
167
|
+
});
|
|
168
|
+
out.type = 'encrypted_extensions';
|
|
169
|
+
|
|
170
|
+
} else if (message.type == wire.TLS_MESSAGE_TYPE.CERTIFICATE) {
|
|
171
|
+
out = wire.parse_certificate(message.body);
|
|
172
|
+
out.type = 'certificate';
|
|
173
|
+
|
|
174
|
+
} else if (message.type == wire.TLS_MESSAGE_TYPE.CERTIFICATE_VERIFY) {
|
|
175
|
+
out.type = 'certificate_verify';
|
|
176
|
+
out.body = message.body;
|
|
177
|
+
|
|
178
|
+
} else if (message.type == wire.TLS_MESSAGE_TYPE.FINISHED) {
|
|
179
|
+
out.type = 'finished';
|
|
180
|
+
out.body = message.body;
|
|
181
|
+
|
|
182
|
+
} else if (message.type == wire.TLS_MESSAGE_TYPE.NEW_SESSION_TICKET) {
|
|
183
|
+
out = wire.parse_new_session_ticket(message.body);
|
|
184
|
+
out.type = 'new_session_ticket';
|
|
185
|
+
|
|
186
|
+
} else if (message.type == wire.TLS_MESSAGE_TYPE.KEY_UPDATE) {
|
|
187
|
+
out = wire.parse_key_update(message.body);
|
|
188
|
+
out.type = 'key_update';
|
|
189
|
+
|
|
190
|
+
} else if (message.type == wire.TLS_MESSAGE_TYPE.CERTIFICATE_REQUEST) {
|
|
191
|
+
out = wire.parse_certificate_request(message.body);
|
|
192
|
+
out.type = 'certificate_request';
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
export {
|
|
200
|
+
normalize_hello,
|
|
201
|
+
build_tls_message,
|
|
202
|
+
parse_tls_message
|
|
203
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TLS signature scheme helpers.
|
|
3
|
+
* Handles scheme negotiation and signing for both TLS 1.2 and 1.3.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as crypto from 'crypto';
|
|
7
|
+
|
|
8
|
+
let TLS_VERSION_TLS1_2 = 0x0303;
|
|
9
|
+
let TLS_VERSION_TLS1_3 = 0x0304;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Decode a SignatureScheme (u16) into hash/sig/isPSS info.
|
|
13
|
+
* Returns null if the scheme is unsupported for the given TLS version.
|
|
14
|
+
*/
|
|
15
|
+
function scheme_info(version, scheme) {
|
|
16
|
+
let h = (scheme >>> 8) & 0xff, s = scheme & 0xff;
|
|
17
|
+
|
|
18
|
+
// EdDSA (TLS 1.3 only)
|
|
19
|
+
if (scheme === 0x0807 || scheme === 0x0808) {
|
|
20
|
+
if (version === TLS_VERSION_TLS1_2) return null;
|
|
21
|
+
return { hash: null, sig: 'eddsa', isPSS: false };
|
|
22
|
+
}
|
|
23
|
+
|
|
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
|
+
}
|
|
34
|
+
|
|
35
|
+
// TLS 1.2: classic (hash, sig) interpretation
|
|
36
|
+
if (h === 0x04 && s === 0x01) return { hash: 'sha256', sig: 'rsa', isPSS: false };
|
|
37
|
+
if (h === 0x05 && s === 0x01) return { hash: 'sha384', sig: 'rsa', isPSS: false };
|
|
38
|
+
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 };
|
|
42
|
+
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Pick the best signature scheme given the server's key type and client's supported list.
|
|
48
|
+
*/
|
|
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);
|
|
63
|
+
}
|
|
64
|
+
|
|
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
|
+
|
|
69
|
+
for (let s of pref) {
|
|
70
|
+
if (cands.includes(s) && clientSupported.includes(s)) return s;
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Sign data using the given scheme. Works for both TLS 1.2 and 1.3.
|
|
77
|
+
*/
|
|
78
|
+
function sign_with_scheme(version, scheme, tbs, certKeyObj) {
|
|
79
|
+
let info = scheme_info(version, scheme);
|
|
80
|
+
if (!info) return null;
|
|
81
|
+
|
|
82
|
+
if (info.sig === 'eddsa') {
|
|
83
|
+
return new Uint8Array(crypto.sign(null, tbs, certKeyObj));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (info.sig === 'ecdsa') {
|
|
87
|
+
return new Uint8Array(crypto.sign(info.hash, tbs, certKeyObj));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (info.sig === 'rsa') {
|
|
91
|
+
if (info.isPSS) {
|
|
92
|
+
let saltLen = (info.hash === 'sha256') ? 32 : (info.hash === 'sha384') ? 48 : 64;
|
|
93
|
+
return new Uint8Array(crypto.sign(info.hash, tbs, {
|
|
94
|
+
key: certKeyObj,
|
|
95
|
+
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
|
|
96
|
+
saltLength: saltLen
|
|
97
|
+
}));
|
|
98
|
+
} else {
|
|
99
|
+
return new Uint8Array(crypto.sign(info.hash, tbs, {
|
|
100
|
+
key: certKeyObj,
|
|
101
|
+
padding: crypto.constants.RSA_PKCS1_PADDING
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
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.
|
|
111
|
+
*/
|
|
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;
|
|
117
|
+
}
|
|
118
|
+
} else if (group === 0x001d) { // X25519
|
|
119
|
+
if (p.length === 33 && p[0] === 0x04) p = p.slice(1);
|
|
120
|
+
}
|
|
121
|
+
return p;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export {
|
|
125
|
+
scheme_info,
|
|
126
|
+
pick_scheme,
|
|
127
|
+
sign_with_scheme,
|
|
128
|
+
normalizeServerPubKeyForTls12
|
|
129
|
+
};
|