@joclaim/tls 0.1.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 +221 -0
- package/lib/crypto/common.d.ts +3 -0
- package/lib/crypto/common.js +26 -0
- package/lib/crypto/index.d.ts +3 -0
- package/lib/crypto/index.js +4 -0
- package/lib/crypto/insecure-rand.d.ts +1 -0
- package/lib/crypto/insecure-rand.js +9 -0
- package/lib/crypto/pure-js.d.ts +2 -0
- package/lib/crypto/pure-js.js +144 -0
- package/lib/crypto/webcrypto.d.ts +3 -0
- package/lib/crypto/webcrypto.js +310 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +4 -0
- package/lib/make-tls-client.d.ts +74 -0
- package/lib/make-tls-client.js +657 -0
- package/lib/scripts/build-jsc.d.ts +1 -0
- package/lib/scripts/build-jsc.js +20 -0
- package/lib/scripts/ca-template.d.ts +5 -0
- package/lib/scripts/ca-template.js +6 -0
- package/lib/scripts/fallbacks/crypto.d.ts +4 -0
- package/lib/scripts/fallbacks/crypto.js +2 -0
- package/lib/scripts/handshake.d.ts +1 -0
- package/lib/scripts/handshake.js +61 -0
- package/lib/scripts/jsc.d.ts +28 -0
- package/lib/scripts/jsc.js +92 -0
- package/lib/scripts/update-ca-certs.d.ts +1 -0
- package/lib/scripts/update-ca-certs.js +29 -0
- package/lib/types/crypto.d.ts +62 -0
- package/lib/types/crypto.js +1 -0
- package/lib/types/index.d.ts +15 -0
- package/lib/types/index.js +4 -0
- package/lib/types/logger.d.ts +6 -0
- package/lib/types/logger.js +1 -0
- package/lib/types/tls.d.ts +141 -0
- package/lib/types/tls.js +1 -0
- package/lib/types/x509.d.ts +32 -0
- package/lib/types/x509.js +1 -0
- package/lib/utils/additional-root-cas.d.ts +1 -0
- package/lib/utils/additional-root-cas.js +197 -0
- package/lib/utils/client-hello.d.ts +23 -0
- package/lib/utils/client-hello.js +167 -0
- package/lib/utils/constants.d.ts +239 -0
- package/lib/utils/constants.js +244 -0
- package/lib/utils/decryption-utils.d.ts +64 -0
- package/lib/utils/decryption-utils.js +166 -0
- package/lib/utils/finish-messages.d.ts +11 -0
- package/lib/utils/finish-messages.js +49 -0
- package/lib/utils/generics.d.ts +35 -0
- package/lib/utils/generics.js +146 -0
- package/lib/utils/index.d.ts +18 -0
- package/lib/utils/index.js +18 -0
- package/lib/utils/key-share.d.ts +13 -0
- package/lib/utils/key-share.js +72 -0
- package/lib/utils/key-update.d.ts +2 -0
- package/lib/utils/key-update.js +14 -0
- package/lib/utils/logger.d.ts +2 -0
- package/lib/utils/logger.js +15 -0
- package/lib/utils/make-queue.d.ts +3 -0
- package/lib/utils/make-queue.js +22 -0
- package/lib/utils/mozilla-root-cas.d.ts +5 -0
- package/lib/utils/mozilla-root-cas.js +4459 -0
- package/lib/utils/packets.d.ts +51 -0
- package/lib/utils/packets.js +148 -0
- package/lib/utils/parse-alert.d.ts +7 -0
- package/lib/utils/parse-alert.js +28 -0
- package/lib/utils/parse-certificate.d.ts +29 -0
- package/lib/utils/parse-certificate.js +188 -0
- package/lib/utils/parse-client-hello.d.ts +11 -0
- package/lib/utils/parse-client-hello.js +39 -0
- package/lib/utils/parse-extensions.d.ts +11 -0
- package/lib/utils/parse-extensions.js +74 -0
- package/lib/utils/parse-server-hello.d.ts +10 -0
- package/lib/utils/parse-server-hello.js +52 -0
- package/lib/utils/session-ticket.d.ts +17 -0
- package/lib/utils/session-ticket.js +51 -0
- package/lib/utils/wrapped-record.d.ts +25 -0
- package/lib/utils/wrapped-record.js +191 -0
- package/lib/utils/x509.d.ts +5 -0
- package/lib/utils/x509.js +124 -0
- package/package.json +82 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/** Max size of an encrypted packet */
|
|
2
|
+
export const MAX_ENC_PACKET_SIZE = 16380;
|
|
3
|
+
export const TLS_PROTOCOL_VERSION_MAP = {
|
|
4
|
+
'TLS1_3': new Uint8Array([0x03, 0x04]),
|
|
5
|
+
'TLS1_2': new Uint8Array([0x03, 0x03]),
|
|
6
|
+
};
|
|
7
|
+
export const SUPPORTED_NAMED_CURVE_MAP = {
|
|
8
|
+
SECP256R1: {
|
|
9
|
+
identifier: new Uint8Array([0x00, 0x17]),
|
|
10
|
+
algorithm: 'P-256'
|
|
11
|
+
},
|
|
12
|
+
SECP384R1: {
|
|
13
|
+
identifier: new Uint8Array([0x00, 0x18]),
|
|
14
|
+
algorithm: 'P-384'
|
|
15
|
+
},
|
|
16
|
+
X25519: {
|
|
17
|
+
identifier: new Uint8Array([0x00, 0x1d]),
|
|
18
|
+
algorithm: 'X25519'
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
export const SUPPORTED_RECORD_TYPE_MAP = {
|
|
22
|
+
CLIENT_HELLO: 0x01,
|
|
23
|
+
SERVER_HELLO: 0x02,
|
|
24
|
+
HELLO_RETRY_REQUEST: 0x03,
|
|
25
|
+
SESSION_TICKET: 0x04,
|
|
26
|
+
ENCRYPTED_EXTENSIONS: 0x08,
|
|
27
|
+
CERTIFICATE: 0x0b,
|
|
28
|
+
SERVER_KEY_SHARE: 0x0c,
|
|
29
|
+
CERTIFICATE_REQUEST: 0x0d,
|
|
30
|
+
SERVER_HELLO_DONE: 0x0e,
|
|
31
|
+
CERTIFICATE_VERIFY: 0x0f,
|
|
32
|
+
CLIENT_KEY_SHARE: 0x10,
|
|
33
|
+
FINISHED: 0x14,
|
|
34
|
+
KEY_UPDATE: 0x18
|
|
35
|
+
};
|
|
36
|
+
export const CONTENT_TYPE_MAP = {
|
|
37
|
+
CHANGE_CIPHER_SPEC: 0x14,
|
|
38
|
+
ALERT: 0x15,
|
|
39
|
+
HANDSHAKE: 0x16,
|
|
40
|
+
APPLICATION_DATA: 0x17,
|
|
41
|
+
};
|
|
42
|
+
// The length of AEAD auth tag, appended after encrypted data in wrapped records
|
|
43
|
+
export const AUTH_TAG_BYTE_LENGTH = 16;
|
|
44
|
+
export const SUPPORTED_NAMED_CURVES = Object.keys(SUPPORTED_NAMED_CURVE_MAP);
|
|
45
|
+
/**
|
|
46
|
+
* Supported cipher suites.
|
|
47
|
+
* In a client hello, these are sent in order of preference
|
|
48
|
+
* as listed below
|
|
49
|
+
*/
|
|
50
|
+
export const SUPPORTED_CIPHER_SUITE_MAP = {
|
|
51
|
+
// TLS 1.3 --------------------
|
|
52
|
+
TLS_CHACHA20_POLY1305_SHA256: {
|
|
53
|
+
identifier: new Uint8Array([0x13, 0x03]),
|
|
54
|
+
keyLength: 32,
|
|
55
|
+
hashLength: 32,
|
|
56
|
+
ivLength: 12,
|
|
57
|
+
hashAlgorithm: 'SHA-256',
|
|
58
|
+
cipher: 'CHACHA20-POLY1305'
|
|
59
|
+
},
|
|
60
|
+
TLS_AES_256_GCM_SHA384: {
|
|
61
|
+
identifier: new Uint8Array([0x13, 0x02]),
|
|
62
|
+
keyLength: 32,
|
|
63
|
+
hashLength: 48,
|
|
64
|
+
ivLength: 12,
|
|
65
|
+
hashAlgorithm: 'SHA-384',
|
|
66
|
+
cipher: 'AES-256-GCM',
|
|
67
|
+
},
|
|
68
|
+
TLS_AES_128_GCM_SHA256: {
|
|
69
|
+
identifier: new Uint8Array([0x13, 0x01]),
|
|
70
|
+
keyLength: 16,
|
|
71
|
+
hashLength: 32,
|
|
72
|
+
ivLength: 12,
|
|
73
|
+
hashAlgorithm: 'SHA-256',
|
|
74
|
+
cipher: 'AES-128-GCM',
|
|
75
|
+
},
|
|
76
|
+
// TLS 1.2 -------------------
|
|
77
|
+
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: {
|
|
78
|
+
identifier: new Uint8Array([0xcc, 0xa8]),
|
|
79
|
+
keyLength: 32,
|
|
80
|
+
hashLength: 32,
|
|
81
|
+
ivLength: 12,
|
|
82
|
+
hashAlgorithm: 'SHA-256',
|
|
83
|
+
cipher: 'CHACHA20-POLY1305',
|
|
84
|
+
},
|
|
85
|
+
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: {
|
|
86
|
+
identifier: new Uint8Array([0xcc, 0xa9]),
|
|
87
|
+
keyLength: 32,
|
|
88
|
+
hashLength: 32,
|
|
89
|
+
ivLength: 12,
|
|
90
|
+
hashAlgorithm: 'SHA-256',
|
|
91
|
+
cipher: 'CHACHA20-POLY1305',
|
|
92
|
+
},
|
|
93
|
+
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: {
|
|
94
|
+
identifier: new Uint8Array([0xc0, 0x2f]),
|
|
95
|
+
keyLength: 16,
|
|
96
|
+
hashLength: 32,
|
|
97
|
+
ivLength: 4,
|
|
98
|
+
hashAlgorithm: 'SHA-256',
|
|
99
|
+
cipher: 'AES-128-GCM',
|
|
100
|
+
},
|
|
101
|
+
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: {
|
|
102
|
+
identifier: new Uint8Array([0xc0, 0x2b]),
|
|
103
|
+
keyLength: 16,
|
|
104
|
+
hashLength: 32,
|
|
105
|
+
ivLength: 4,
|
|
106
|
+
hashAlgorithm: 'SHA-256',
|
|
107
|
+
cipher: 'AES-128-GCM',
|
|
108
|
+
},
|
|
109
|
+
TLS_RSA_WITH_AES_128_GCM_SHA256: {
|
|
110
|
+
identifier: new Uint8Array([0x00, 0x9c]),
|
|
111
|
+
keyLength: 16,
|
|
112
|
+
hashLength: 32,
|
|
113
|
+
ivLength: 4,
|
|
114
|
+
hashAlgorithm: 'SHA-256',
|
|
115
|
+
cipher: 'AES-128-GCM',
|
|
116
|
+
isRsaEcdh: true, // RSA key exchange
|
|
117
|
+
},
|
|
118
|
+
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: {
|
|
119
|
+
identifier: new Uint8Array([0xc0, 0x30]),
|
|
120
|
+
keyLength: 32,
|
|
121
|
+
hashLength: 48,
|
|
122
|
+
ivLength: 4,
|
|
123
|
+
hashAlgorithm: 'SHA-384',
|
|
124
|
+
cipher: 'AES-256-GCM',
|
|
125
|
+
},
|
|
126
|
+
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: {
|
|
127
|
+
identifier: new Uint8Array([0xc0, 0x2c]),
|
|
128
|
+
keyLength: 32,
|
|
129
|
+
hashLength: 48,
|
|
130
|
+
ivLength: 4,
|
|
131
|
+
hashAlgorithm: 'SHA-384',
|
|
132
|
+
cipher: 'AES-256-GCM',
|
|
133
|
+
},
|
|
134
|
+
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: {
|
|
135
|
+
identifier: new Uint8Array([0xc0, 0x13]),
|
|
136
|
+
keyLength: 16,
|
|
137
|
+
hashLength: 20,
|
|
138
|
+
ivLength: 16,
|
|
139
|
+
hashAlgorithm: 'SHA-1',
|
|
140
|
+
prfHashAlgorithm: 'SHA-256',
|
|
141
|
+
cipher: 'AES-128-CBC',
|
|
142
|
+
},
|
|
143
|
+
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: {
|
|
144
|
+
identifier: new Uint8Array([0xc0, 0x09]),
|
|
145
|
+
keyLength: 16,
|
|
146
|
+
hashLength: 20,
|
|
147
|
+
ivLength: 16,
|
|
148
|
+
hashAlgorithm: 'SHA-1',
|
|
149
|
+
prfHashAlgorithm: 'SHA-256',
|
|
150
|
+
cipher: 'AES-128-CBC',
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
export const ALERT_LEVEL = {
|
|
154
|
+
WARNING: 1,
|
|
155
|
+
FATAL: 2,
|
|
156
|
+
};
|
|
157
|
+
export const ALERT_DESCRIPTION = {
|
|
158
|
+
CLOSE_NOTIFY: 0,
|
|
159
|
+
UNEXPECTED_MESSAGE: 10,
|
|
160
|
+
BAD_RECORD_MAC: 20,
|
|
161
|
+
RECORD_OVERFLOW: 22,
|
|
162
|
+
HANDSHAKE_FAILURE: 40,
|
|
163
|
+
BAD_CERTIFICATE: 42,
|
|
164
|
+
UNSUPPORTED_CERTIFICATE: 43,
|
|
165
|
+
CERTIFICATE_REVOKED: 44,
|
|
166
|
+
CERTIFICATE_EXPIRED: 45,
|
|
167
|
+
CERTIFICATE_UNKNOWN: 46,
|
|
168
|
+
ILLEGAL_PARAMETER: 47,
|
|
169
|
+
UNKNOWN_CA: 48,
|
|
170
|
+
ACCESS_DENIED: 49,
|
|
171
|
+
DECODE_ERROR: 50,
|
|
172
|
+
DECRYPT_ERROR: 51,
|
|
173
|
+
PROTOCOL_VERSION: 70,
|
|
174
|
+
INSUFFICIENT_SECURITY: 71,
|
|
175
|
+
INTERNAL_ERROR: 80,
|
|
176
|
+
INAPPROPRIATE_FALLBACK: 86,
|
|
177
|
+
USER_CANCELED: 90,
|
|
178
|
+
MISSING_EXTENSION: 109,
|
|
179
|
+
UNSUPPORTED_EXTENSION: 110,
|
|
180
|
+
UNRECOGNIZED_NAME: 112,
|
|
181
|
+
BAD_CERTIFICATE_STATUS_RESPONSE: 113,
|
|
182
|
+
UNKNOWN_PSK_IDENTITY: 115,
|
|
183
|
+
CERTIFICATE_REQUIRED: 116,
|
|
184
|
+
NO_APPLICATION_PROTOCOL: 120,
|
|
185
|
+
// TLS1.2
|
|
186
|
+
DECRYPTION_FAILED_RESERVED: 21,
|
|
187
|
+
DECOMPRESSION_FAILURE: 30,
|
|
188
|
+
NO_CERTIFICATE_RESERVED: 41,
|
|
189
|
+
EXPORT_RESTRICTION_RESERVED: 60,
|
|
190
|
+
NO_RENEGOTIATION: 100,
|
|
191
|
+
};
|
|
192
|
+
export const SUPPORTED_CIPHER_SUITES = Object.keys(SUPPORTED_CIPHER_SUITE_MAP);
|
|
193
|
+
export const SUPPORTED_SIGNATURE_ALGS_MAP = {
|
|
194
|
+
ECDSA_SECP256R1_SHA256: {
|
|
195
|
+
identifier: new Uint8Array([0x04, 0x03]),
|
|
196
|
+
algorithm: 'ECDSA-SECP256R1-SHA256'
|
|
197
|
+
},
|
|
198
|
+
ECDSA_SECP384R1_SHA256: {
|
|
199
|
+
identifier: new Uint8Array([0x05, 0x03]),
|
|
200
|
+
algorithm: 'ECDSA-SECP384R1-SHA384'
|
|
201
|
+
},
|
|
202
|
+
RSA_PSS_RSAE_SHA256: {
|
|
203
|
+
identifier: new Uint8Array([0x08, 0x04]),
|
|
204
|
+
algorithm: 'RSA-PSS-SHA256',
|
|
205
|
+
},
|
|
206
|
+
RSA_PKCS1_SHA256: {
|
|
207
|
+
identifier: new Uint8Array([0x04, 0x01]),
|
|
208
|
+
algorithm: 'RSA-PKCS1-SHA256',
|
|
209
|
+
},
|
|
210
|
+
RSA_PKCS1_SHA384: {
|
|
211
|
+
identifier: new Uint8Array([0x05, 0x01]),
|
|
212
|
+
algorithm: 'RSA-PKCS1-SHA384',
|
|
213
|
+
},
|
|
214
|
+
RSA_PKCS1_SHA512: {
|
|
215
|
+
identifier: new Uint8Array([0x06, 0x01]),
|
|
216
|
+
algorithm: 'RSA-PKCS1-SHA512'
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
export const SUPPORTED_SIGNATURE_ALGS = Object.keys(SUPPORTED_SIGNATURE_ALGS_MAP);
|
|
220
|
+
export const SUPPORTED_EXTENSION_MAP = {
|
|
221
|
+
SERVER_NAME: 0x00,
|
|
222
|
+
MAX_FRAGMENT_LENGTH: 0x01,
|
|
223
|
+
KEY_SHARE: 0x33,
|
|
224
|
+
SUPPORTED_GROUPS: 0x0a,
|
|
225
|
+
SIGNATURE_ALGS: 0x0d,
|
|
226
|
+
SUPPORTED_VERSIONS: 0x2b,
|
|
227
|
+
SESSION_TICKET: 0x23,
|
|
228
|
+
EARLY_DATA: 0x2a,
|
|
229
|
+
PRE_SHARED_KEY: 0x29,
|
|
230
|
+
PRE_SHARED_KEY_MODE: 0x2d,
|
|
231
|
+
// application layer protocol negotiation
|
|
232
|
+
ALPN: 0x10,
|
|
233
|
+
};
|
|
234
|
+
export const SUPPORTED_EXTENSIONS = Object.keys(SUPPORTED_EXTENSION_MAP);
|
|
235
|
+
export const PACKET_TYPE = {
|
|
236
|
+
HELLO: 0x16,
|
|
237
|
+
WRAPPED_RECORD: 0x17,
|
|
238
|
+
CHANGE_CIPHER_SPEC: 0x14,
|
|
239
|
+
ALERT: 0x15,
|
|
240
|
+
};
|
|
241
|
+
export const KEY_UPDATE_TYPE_MAP = {
|
|
242
|
+
UPDATE_NOT_REQUESTED: 0,
|
|
243
|
+
UPDATE_REQUESTED: 1
|
|
244
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { CipherSuite, HashAlgorithm } from '../types/index.ts';
|
|
2
|
+
type DeriveTrafficKeysOptions = {
|
|
3
|
+
masterSecret: Uint8Array;
|
|
4
|
+
/** used to derive keys when resuming session */
|
|
5
|
+
earlySecret?: Uint8Array;
|
|
6
|
+
cipherSuite: CipherSuite;
|
|
7
|
+
/** list of handshake message to hash; or the hash itself */
|
|
8
|
+
hellos: Uint8Array[] | Uint8Array;
|
|
9
|
+
/** type of secret; handshake or provider-data */
|
|
10
|
+
secretType: 'hs' | 'ap';
|
|
11
|
+
};
|
|
12
|
+
type DeriveTrafficKeysOptionsTls12 = {
|
|
13
|
+
preMasterSecret: Uint8Array;
|
|
14
|
+
clientRandom: Uint8Array;
|
|
15
|
+
serverRandom: Uint8Array;
|
|
16
|
+
cipherSuite: CipherSuite;
|
|
17
|
+
};
|
|
18
|
+
export type SharedKeyData = Awaited<ReturnType<typeof computeSharedKeys>> | Awaited<ReturnType<typeof computeSharedKeysTls12>>;
|
|
19
|
+
export declare function computeSharedKeysTls12(opts: DeriveTrafficKeysOptionsTls12): Promise<{
|
|
20
|
+
type: "TLS1_2";
|
|
21
|
+
masterSecret: Uint8Array<ArrayBuffer>;
|
|
22
|
+
clientMacKey: unknown;
|
|
23
|
+
serverMacKey: unknown;
|
|
24
|
+
clientEncKey: unknown;
|
|
25
|
+
serverEncKey: unknown;
|
|
26
|
+
clientIv: Uint8Array<ArrayBuffer>;
|
|
27
|
+
serverIv: Uint8Array<ArrayBuffer>;
|
|
28
|
+
serverSecret: Uint8Array<ArrayBuffer>;
|
|
29
|
+
clientSecret: Uint8Array<ArrayBuffer>;
|
|
30
|
+
}>;
|
|
31
|
+
export declare function computeUpdatedTrafficMasterSecret(masterSecret: Uint8Array, cipherSuite: CipherSuite): Promise<Uint8Array<ArrayBuffer>>;
|
|
32
|
+
export declare function computeSharedKeys({ hellos, masterSecret: masterKey, cipherSuite, secretType, earlySecret }: DeriveTrafficKeysOptions): Promise<{
|
|
33
|
+
type: "TLS1_3";
|
|
34
|
+
masterSecret: Uint8Array<ArrayBufferLike>;
|
|
35
|
+
clientSecret: Uint8Array<ArrayBuffer>;
|
|
36
|
+
serverSecret: Uint8Array<ArrayBuffer>;
|
|
37
|
+
clientEncKey: unknown;
|
|
38
|
+
serverEncKey: unknown;
|
|
39
|
+
clientIv: Uint8Array<ArrayBuffer>;
|
|
40
|
+
serverIv: Uint8Array<ArrayBuffer>;
|
|
41
|
+
}>;
|
|
42
|
+
export declare function deriveTrafficKeys({ masterSecret, cipherSuite, hellos, secretType, }: DeriveTrafficKeysOptions): Promise<{
|
|
43
|
+
type: "TLS1_3";
|
|
44
|
+
masterSecret: Uint8Array<ArrayBufferLike>;
|
|
45
|
+
clientSecret: Uint8Array<ArrayBuffer>;
|
|
46
|
+
serverSecret: Uint8Array<ArrayBuffer>;
|
|
47
|
+
clientEncKey: unknown;
|
|
48
|
+
serverEncKey: unknown;
|
|
49
|
+
clientIv: Uint8Array<ArrayBuffer>;
|
|
50
|
+
serverIv: Uint8Array<ArrayBuffer>;
|
|
51
|
+
}>;
|
|
52
|
+
export declare function deriveTrafficKeysForSide(masterSecret: Uint8Array, cipherSuite: CipherSuite): Promise<{
|
|
53
|
+
masterSecret: Uint8Array<ArrayBufferLike>;
|
|
54
|
+
encKey: unknown;
|
|
55
|
+
iv: Uint8Array<ArrayBuffer>;
|
|
56
|
+
}>;
|
|
57
|
+
export declare function hkdfExtractAndExpandLabel(algorithm: HashAlgorithm, secret: Uint8Array, label: string, context: Uint8Array, length: number): Promise<Uint8Array<ArrayBuffer>>;
|
|
58
|
+
export declare function getHash(msgs: Uint8Array[] | Uint8Array, cipherSuite: CipherSuite): Promise<Uint8Array<ArrayBufferLike>>;
|
|
59
|
+
/**
|
|
60
|
+
* Get the PRF algorithm for the given cipher suite
|
|
61
|
+
* Relevant for TLS 1.2
|
|
62
|
+
*/
|
|
63
|
+
export declare function getPrfHashAlgorithm(cipherSuite: CipherSuite): "SHA-256" | "SHA-384";
|
|
64
|
+
export {};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { crypto } from "../crypto/index.js";
|
|
2
|
+
import { SUPPORTED_CIPHER_SUITE_MAP } from "./constants.js";
|
|
3
|
+
import { asciiToUint8Array, concatenateUint8Arrays, hkdfExpand, isSymmetricCipher, uint8ArrayToDataView } from "./generics.js";
|
|
4
|
+
import { packWithLength } from "./packets.js";
|
|
5
|
+
const TLS1_2_BASE_SEED = asciiToUint8Array('master secret');
|
|
6
|
+
const TLS1_2_KEY_EXPANSION_SEED = asciiToUint8Array('key expansion');
|
|
7
|
+
export async function computeSharedKeysTls12(opts) {
|
|
8
|
+
const { clientRandom, serverRandom, cipherSuite, } = opts;
|
|
9
|
+
const masterSecret = await generateMasterSecret(opts);
|
|
10
|
+
// all key derivation in TLS 1.2 uses SHA-256
|
|
11
|
+
const hashAlgorithm = getPrfHashAlgorithm(cipherSuite);
|
|
12
|
+
const { keyLength, cipher, hashLength, hashAlgorithm: cipherHashAlg, ivLength } = SUPPORTED_CIPHER_SUITE_MAP[cipherSuite];
|
|
13
|
+
const masterKey = await crypto
|
|
14
|
+
.importKey(hashAlgorithm, masterSecret);
|
|
15
|
+
const seed = concatenateUint8Arrays([
|
|
16
|
+
TLS1_2_KEY_EXPANSION_SEED,
|
|
17
|
+
serverRandom,
|
|
18
|
+
clientRandom,
|
|
19
|
+
]);
|
|
20
|
+
const expandedSecretArr = [];
|
|
21
|
+
let lastSeed = seed;
|
|
22
|
+
for (let i = 0; i < 4; i++) {
|
|
23
|
+
lastSeed = await crypto.hmac(hashAlgorithm, masterKey, lastSeed);
|
|
24
|
+
const expandedSecret = await crypto.hmac(hashAlgorithm, masterKey, concatenateUint8Arrays([
|
|
25
|
+
lastSeed,
|
|
26
|
+
seed
|
|
27
|
+
]));
|
|
28
|
+
expandedSecretArr.push(expandedSecret);
|
|
29
|
+
}
|
|
30
|
+
let expandedSecret = concatenateUint8Arrays(expandedSecretArr);
|
|
31
|
+
const needsMac = isSymmetricCipher(cipher);
|
|
32
|
+
const clientMacKey = needsMac ? await crypto.importKey(cipherHashAlg, readExpandedSecret(hashLength)) : undefined;
|
|
33
|
+
const serverMacKey = needsMac ? await crypto.importKey(cipherHashAlg, readExpandedSecret(hashLength)) : undefined;
|
|
34
|
+
const clientEncKey = await crypto.importKey(cipher, readExpandedSecret(keyLength));
|
|
35
|
+
const serverEncKey = await crypto.importKey(cipher, readExpandedSecret(keyLength));
|
|
36
|
+
const clientIv = readExpandedSecret(ivLength);
|
|
37
|
+
const serverIv = readExpandedSecret(ivLength);
|
|
38
|
+
return {
|
|
39
|
+
type: 'TLS1_2',
|
|
40
|
+
masterSecret,
|
|
41
|
+
clientMacKey,
|
|
42
|
+
serverMacKey,
|
|
43
|
+
clientEncKey,
|
|
44
|
+
serverEncKey,
|
|
45
|
+
clientIv,
|
|
46
|
+
serverIv,
|
|
47
|
+
serverSecret: masterSecret,
|
|
48
|
+
clientSecret: masterSecret,
|
|
49
|
+
};
|
|
50
|
+
function readExpandedSecret(len) {
|
|
51
|
+
const returnVal = expandedSecret
|
|
52
|
+
.slice(0, len);
|
|
53
|
+
expandedSecret = expandedSecret
|
|
54
|
+
.slice(len);
|
|
55
|
+
return returnVal;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function generateMasterSecret({ preMasterSecret, clientRandom, serverRandom, cipherSuite }) {
|
|
59
|
+
// all key derivation in TLS 1.2 uses SHA-256
|
|
60
|
+
const hashAlgorithm = getPrfHashAlgorithm(cipherSuite);
|
|
61
|
+
const preMasterKey = await crypto
|
|
62
|
+
.importKey(hashAlgorithm, preMasterSecret);
|
|
63
|
+
const seed = concatenateUint8Arrays([
|
|
64
|
+
TLS1_2_BASE_SEED,
|
|
65
|
+
clientRandom,
|
|
66
|
+
serverRandom
|
|
67
|
+
]);
|
|
68
|
+
const a1 = await crypto.hmac(hashAlgorithm, preMasterKey, seed);
|
|
69
|
+
const a2 = await crypto.hmac(hashAlgorithm, preMasterKey, a1);
|
|
70
|
+
const p1 = await crypto.hmac(hashAlgorithm, preMasterKey, concatenateUint8Arrays([
|
|
71
|
+
a1,
|
|
72
|
+
seed
|
|
73
|
+
]));
|
|
74
|
+
const p2 = await crypto.hmac(hashAlgorithm, preMasterKey, concatenateUint8Arrays([
|
|
75
|
+
a2,
|
|
76
|
+
seed
|
|
77
|
+
]));
|
|
78
|
+
return concatenateUint8Arrays([p1, p2])
|
|
79
|
+
.slice(0, 48);
|
|
80
|
+
}
|
|
81
|
+
export function computeUpdatedTrafficMasterSecret(masterSecret, cipherSuite) {
|
|
82
|
+
const { hashAlgorithm, hashLength } = SUPPORTED_CIPHER_SUITE_MAP[cipherSuite];
|
|
83
|
+
return hkdfExtractAndExpandLabel(hashAlgorithm, masterSecret, 'traffic upd', new Uint8Array(), hashLength);
|
|
84
|
+
}
|
|
85
|
+
export async function computeSharedKeys({ hellos, masterSecret: masterKey, cipherSuite, secretType, earlySecret }) {
|
|
86
|
+
const { hashAlgorithm, hashLength } = SUPPORTED_CIPHER_SUITE_MAP[cipherSuite];
|
|
87
|
+
const emptyHash = await crypto.hash(hashAlgorithm, new Uint8Array());
|
|
88
|
+
const zeros = new Uint8Array(hashLength);
|
|
89
|
+
let handshakeTrafficSecret;
|
|
90
|
+
if (secretType === 'hs') {
|
|
91
|
+
// some hashes
|
|
92
|
+
earlySecret = earlySecret
|
|
93
|
+
|| await crypto.extract(hashAlgorithm, hashLength, zeros, '');
|
|
94
|
+
const derivedSecret = await hkdfExtractAndExpandLabel(hashAlgorithm, earlySecret, 'derived', emptyHash, hashLength);
|
|
95
|
+
handshakeTrafficSecret = await crypto.extract(hashAlgorithm, hashLength, masterKey, derivedSecret);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
const derivedSecret = await hkdfExtractAndExpandLabel(hashAlgorithm, masterKey, 'derived', emptyHash, hashLength);
|
|
99
|
+
handshakeTrafficSecret = await crypto.extract(hashAlgorithm, hashLength, zeros, derivedSecret);
|
|
100
|
+
}
|
|
101
|
+
return deriveTrafficKeys({
|
|
102
|
+
hellos,
|
|
103
|
+
cipherSuite,
|
|
104
|
+
masterSecret: handshakeTrafficSecret,
|
|
105
|
+
secretType
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
export async function deriveTrafficKeys({ masterSecret, cipherSuite, hellos, secretType, }) {
|
|
109
|
+
const { hashAlgorithm, hashLength } = SUPPORTED_CIPHER_SUITE_MAP[cipherSuite];
|
|
110
|
+
const handshakeHash = await getHash(hellos, cipherSuite);
|
|
111
|
+
const clientSecret = await hkdfExtractAndExpandLabel(hashAlgorithm, masterSecret, `c ${secretType} traffic`, handshakeHash, hashLength);
|
|
112
|
+
const serverSecret = await hkdfExtractAndExpandLabel(hashAlgorithm, masterSecret, `s ${secretType} traffic`, handshakeHash, hashLength);
|
|
113
|
+
const { encKey: clientEncKey, iv: clientIv } = await deriveTrafficKeysForSide(clientSecret, cipherSuite);
|
|
114
|
+
const { encKey: serverEncKey, iv: serverIv } = await deriveTrafficKeysForSide(serverSecret, cipherSuite);
|
|
115
|
+
return {
|
|
116
|
+
type: 'TLS1_3',
|
|
117
|
+
masterSecret,
|
|
118
|
+
clientSecret,
|
|
119
|
+
serverSecret,
|
|
120
|
+
clientEncKey,
|
|
121
|
+
serverEncKey,
|
|
122
|
+
clientIv,
|
|
123
|
+
serverIv,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
export async function deriveTrafficKeysForSide(masterSecret, cipherSuite) {
|
|
127
|
+
const { hashAlgorithm, keyLength, cipher, ivLength } = SUPPORTED_CIPHER_SUITE_MAP[cipherSuite];
|
|
128
|
+
const encKey = await hkdfExtractAndExpandLabel(hashAlgorithm, masterSecret, 'key', new Uint8Array(), keyLength);
|
|
129
|
+
const iv = await hkdfExtractAndExpandLabel(hashAlgorithm, masterSecret, 'iv', new Uint8Array(0), ivLength);
|
|
130
|
+
return {
|
|
131
|
+
masterSecret,
|
|
132
|
+
encKey: await crypto.importKey(cipher, encKey),
|
|
133
|
+
iv
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
export async function hkdfExtractAndExpandLabel(algorithm, secret, label, context, length) {
|
|
137
|
+
const tmpLabel = `tls13 ${label}`;
|
|
138
|
+
const lengthBuffer = new Uint8Array(2);
|
|
139
|
+
const lengthBufferView = uint8ArrayToDataView(lengthBuffer);
|
|
140
|
+
lengthBufferView.setUint16(0, length);
|
|
141
|
+
const hkdfLabel = concatenateUint8Arrays([
|
|
142
|
+
lengthBuffer,
|
|
143
|
+
packWithLength(asciiToUint8Array(tmpLabel)).slice(1),
|
|
144
|
+
packWithLength(context).slice(1)
|
|
145
|
+
]);
|
|
146
|
+
const key = await crypto.importKey(algorithm, secret);
|
|
147
|
+
return hkdfExpand(algorithm, length, key, length, hkdfLabel, crypto);
|
|
148
|
+
}
|
|
149
|
+
export async function getHash(msgs, cipherSuite) {
|
|
150
|
+
if (Array.isArray(msgs) && !(msgs instanceof Uint8Array)) {
|
|
151
|
+
const { hashAlgorithm } = SUPPORTED_CIPHER_SUITE_MAP[cipherSuite];
|
|
152
|
+
return crypto.hash(hashAlgorithm, concatenateUint8Arrays(msgs));
|
|
153
|
+
}
|
|
154
|
+
return msgs;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get the PRF algorithm for the given cipher suite
|
|
158
|
+
* Relevant for TLS 1.2
|
|
159
|
+
*/
|
|
160
|
+
export function getPrfHashAlgorithm(cipherSuite) {
|
|
161
|
+
const opts = SUPPORTED_CIPHER_SUITE_MAP[cipherSuite];
|
|
162
|
+
// all key derivation in TLS 1.2 uses min SHA-256
|
|
163
|
+
return ('prfHashAlgorithm' in opts)
|
|
164
|
+
? opts.prfHashAlgorithm
|
|
165
|
+
: opts.hashAlgorithm;
|
|
166
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CipherSuite } from '../types/index.ts';
|
|
2
|
+
type VerifyFinishMessageOptions = {
|
|
3
|
+
secret: Uint8Array;
|
|
4
|
+
handshakeMessages: Uint8Array[];
|
|
5
|
+
cipherSuite: CipherSuite;
|
|
6
|
+
};
|
|
7
|
+
export declare function verifyFinishMessage(verifyData: Uint8Array, opts: VerifyFinishMessageOptions): Promise<void>;
|
|
8
|
+
export declare function packFinishMessagePacket(opts: VerifyFinishMessageOptions): Promise<Uint8Array<ArrayBufferLike>>;
|
|
9
|
+
export declare function packClientFinishTls12(opts: VerifyFinishMessageOptions): Promise<Uint8Array<ArrayBufferLike>>;
|
|
10
|
+
export declare function generateFinishTls12(type: 'client' | 'server', { secret, handshakeMessages, cipherSuite }: VerifyFinishMessageOptions): Promise<Uint8Array<ArrayBuffer>>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { crypto } from "../crypto/index.js";
|
|
2
|
+
import { getHash, getPrfHashAlgorithm, hkdfExtractAndExpandLabel } from "../utils/decryption-utils.js";
|
|
3
|
+
import { SUPPORTED_CIPHER_SUITE_MAP, SUPPORTED_RECORD_TYPE_MAP } from "./constants.js";
|
|
4
|
+
import { areUint8ArraysEqual, asciiToUint8Array, concatenateUint8Arrays } from "./generics.js";
|
|
5
|
+
import { packWith3ByteLength, packWithLength } from "./packets.js";
|
|
6
|
+
export async function verifyFinishMessage(verifyData, opts) {
|
|
7
|
+
const computedData = await computeFinishMessageHash(opts);
|
|
8
|
+
if (!areUint8ArraysEqual(computedData, verifyData)) {
|
|
9
|
+
throw new Error('Invalid finish message');
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export async function packFinishMessagePacket(opts) {
|
|
13
|
+
const hash = await computeFinishMessageHash(opts);
|
|
14
|
+
const packet = concatenateUint8Arrays([
|
|
15
|
+
new Uint8Array([SUPPORTED_RECORD_TYPE_MAP.FINISHED, 0x00]),
|
|
16
|
+
packWithLength(hash)
|
|
17
|
+
]);
|
|
18
|
+
return packet;
|
|
19
|
+
}
|
|
20
|
+
async function computeFinishMessageHash({ secret, handshakeMessages, cipherSuite }) {
|
|
21
|
+
const { hashAlgorithm, hashLength } = SUPPORTED_CIPHER_SUITE_MAP[cipherSuite];
|
|
22
|
+
const handshakeHash = await getHash(handshakeMessages, cipherSuite);
|
|
23
|
+
const finishKey = await hkdfExtractAndExpandLabel(hashAlgorithm, secret, 'finished', new Uint8Array(0), hashLength);
|
|
24
|
+
const hmacKey = await crypto.importKey(hashAlgorithm, finishKey);
|
|
25
|
+
return crypto.hmac(hashAlgorithm, hmacKey, handshakeHash);
|
|
26
|
+
}
|
|
27
|
+
const TLS12_CLIENT_FINISH_DATA_LABEL = asciiToUint8Array('client finished');
|
|
28
|
+
const TLS12_SERVER_FINISH_DATA_LABEL = asciiToUint8Array('server finished');
|
|
29
|
+
export async function packClientFinishTls12(opts) {
|
|
30
|
+
return concatenateUint8Arrays([
|
|
31
|
+
new Uint8Array([SUPPORTED_RECORD_TYPE_MAP.FINISHED]),
|
|
32
|
+
packWith3ByteLength(await generateFinishTls12('client', opts))
|
|
33
|
+
]);
|
|
34
|
+
}
|
|
35
|
+
export async function generateFinishTls12(type, { secret, handshakeMessages, cipherSuite }) {
|
|
36
|
+
// all key derivation in TLS 1.2 uses SHA-256
|
|
37
|
+
const hashAlgorithm = getPrfHashAlgorithm(cipherSuite);
|
|
38
|
+
const handshakeHash = await crypto.hash(hashAlgorithm, concatenateUint8Arrays(handshakeMessages));
|
|
39
|
+
const seed = concatenateUint8Arrays([
|
|
40
|
+
type === 'client'
|
|
41
|
+
? TLS12_CLIENT_FINISH_DATA_LABEL
|
|
42
|
+
: TLS12_SERVER_FINISH_DATA_LABEL,
|
|
43
|
+
handshakeHash
|
|
44
|
+
]);
|
|
45
|
+
const key = await crypto.importKey(hashAlgorithm, secret);
|
|
46
|
+
const a1 = await crypto.hmac(hashAlgorithm, key, seed);
|
|
47
|
+
const p1 = await crypto.hmac(hashAlgorithm, key, concatenateUint8Arrays([a1, seed]));
|
|
48
|
+
return p1.slice(0, 12);
|
|
49
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { AuthenticatedSymmetricCryptoAlgorithm, Crypto, HashAlgorithm, Key, SymmetricCryptoAlgorithm, TLSProtocolVersion } from '../types/index.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Converts a buffer to a hex string with whitespace between each byte
|
|
4
|
+
* @returns eg. '01 02 03 04'
|
|
5
|
+
*/
|
|
6
|
+
export declare function toHexStringWithWhitespace(buff: Uint8Array, whitespace?: string): string;
|
|
7
|
+
export declare function xor(a: Uint8Array, b: Uint8Array): Uint8Array<ArrayBuffer>;
|
|
8
|
+
export declare function concatenateUint8Arrays(arrays: Uint8Array[]): Uint8Array;
|
|
9
|
+
export declare function areUint8ArraysEqual(a: Uint8Array, b: Uint8Array): boolean;
|
|
10
|
+
export declare function uint8ArrayToDataView(arr: Uint8Array): DataView<ArrayBufferLike>;
|
|
11
|
+
export declare function asciiToUint8Array(str: string): Uint8Array<ArrayBufferLike>;
|
|
12
|
+
/**
|
|
13
|
+
* convert a Uint8Array to a binary encoded str
|
|
14
|
+
* from: https://github.com/feross/buffer/blob/795bbb5bda1b39f1370ebd784bea6107b087e3a7/index.js#L1063
|
|
15
|
+
* @param buf
|
|
16
|
+
* @returns
|
|
17
|
+
*/
|
|
18
|
+
export declare function uint8ArrayToBinaryStr(buf: Uint8Array): string;
|
|
19
|
+
export declare function generateIV(iv: Uint8Array, recordNumber: number): Uint8Array<ArrayBuffer>;
|
|
20
|
+
/**
|
|
21
|
+
* TLS has this special sort of padding where the last byte
|
|
22
|
+
* is the number of padding bytes, and all the padding bytes
|
|
23
|
+
* are the same as the last byte.
|
|
24
|
+
* Eg. for an 8 byte block [ 0x0a, 0x0b, 0x0c, 0xd ]
|
|
25
|
+
* -> [ 0x0a, 0x0b, 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04 ]
|
|
26
|
+
*/
|
|
27
|
+
export declare function padTls(data: Uint8Array, blockSize: number): Uint8Array<ArrayBuffer>;
|
|
28
|
+
/**
|
|
29
|
+
* Unpad a TLS-spec padded buffer
|
|
30
|
+
*/
|
|
31
|
+
export declare function unpadTls(data: Uint8Array): Uint8Array<ArrayBuffer>;
|
|
32
|
+
export declare function isSymmetricCipher(cipher: SymmetricCryptoAlgorithm | AuthenticatedSymmetricCryptoAlgorithm): cipher is SymmetricCryptoAlgorithm;
|
|
33
|
+
export declare function chunkUint8Array(arr: Uint8Array, chunkSize: number): Uint8Array<ArrayBufferLike>[];
|
|
34
|
+
export declare function getTlsVersionFromBytes(bytes: Uint8Array): TLSProtocolVersion;
|
|
35
|
+
export declare const hkdfExpand: (alg: HashAlgorithm, hashLength: number, key: Key, expLength: number, info: Uint8Array, crypto: Crypto<unknown>) => Promise<Uint8Array<ArrayBuffer>>;
|