sliftutils 1.5.5 → 1.5.6

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/index.d.ts CHANGED
@@ -71,7 +71,7 @@ declare module "sliftutils/misc/https/certs" {
71
71
  /// <reference types="node" />
72
72
  import * as forge from "node-forge";
73
73
  export declare const CA_NOT_FOUND_ERROR = "18aa7318-f88f-4d2d-b41f-3daf4a433827";
74
- export declare const identityStorageKey = "machineCA_12";
74
+ export declare const identityStorageKey = "machineCA_14";
75
75
  export type IdentityStorageType = {
76
76
  domain: string;
77
77
  certB64: string;
@@ -90,9 +90,9 @@ declare module "sliftutils/misc/https/certs" {
90
90
  keyPair: {
91
91
  publicKey: forge.Ed25519PublicKey;
92
92
  privateKey: forge.Ed25519PrivateKey;
93
- } | forge.pki.KeyPair;
93
+ };
94
94
  }): X509KeyPair;
95
- export declare function privateKeyToPem(buffer: forge.pki.PrivateKey | forge.Ed25519PrivateKey): string;
95
+ export declare function privateKeyToPem(key: forge.Ed25519PrivateKey): string;
96
96
  export declare function parseCert(PEMorDER: string | Buffer): forge.pki.Certificate;
97
97
  export declare function getPublicIdentifier(PEMorDER: string | Buffer): Buffer;
98
98
  export declare const sign: (keyPair: {
@@ -101,8 +101,10 @@ declare module "sliftutils/misc/https/certs" {
101
101
  export declare function verify(cert: string, signature: string, data: unknown): void;
102
102
  export declare function validateCACert(domain: string, cert: string | Buffer): void;
103
103
  export declare function validateCertificate(domain: string, cert: Buffer | string, issuerCert: Buffer | string): void;
104
- export declare function generateKeyPair(): forge.pki.rsa.KeyPair;
105
- export declare function generateRSAKeyPair(): forge.pki.rsa.KeyPair;
104
+ export declare function generateKeyPair(): {
105
+ publicKey: forge.Ed25519PublicKey;
106
+ privateKey: forge.Ed25519PrivateKey;
107
+ };
106
108
  export declare function generateTestCA(domain: string): X509KeyPair;
107
109
  export declare function createCertFromCA(config: {
108
110
  CAKeyPair: X509KeyPair;
@@ -129,11 +131,6 @@ declare module "sliftutils/misc/https/certs" {
129
131
  publicKey: Buffer;
130
132
  }): boolean;
131
133
  export declare function getThreadKeyCert(domain: string): X509KeyPair;
132
- export declare const createTestBrowserKeyCert: {
133
- (): Promise<X509KeyPair>;
134
- reset(): void;
135
- set(newValue: Promise<X509KeyPair>): void;
136
- };
137
134
  export declare function getOwnNodeId(): string;
138
135
  export declare function getOwnNodeIdAllowUndefined(): string;
139
136
 
@@ -201,10 +198,14 @@ declare module "sliftutils/misc/https/node-forge-ed25519" {
201
198
  declare module "node-forge" {
202
199
  declare type Ed25519PublicKey = {
203
200
  publicKeyBytes: Buffer;
204
- } & Buffer;
201
+ keyType: string;
202
+ verify(message: string | Buffer, signature: string): boolean;
203
+ };
205
204
  declare type Ed25519PrivateKey = {
206
205
  privateKeyBytes: Buffer;
207
- } & Buffer;
206
+ keyType: string;
207
+ sign(message: string | Buffer): string;
208
+ };
208
209
  class ed25519 {
209
210
  static generateKeyPair(): { publicKey: Ed25519PublicKey, privateKey: Ed25519PrivateKey };
210
211
  static privateKeyToPem(key: Ed25519PrivateKey): string;
@@ -213,6 +214,7 @@ declare module "sliftutils/misc/https/node-forge-ed25519" {
213
214
  static publicKeyFromPem(pem: string): Ed25519PublicKey;
214
215
  }
215
216
  }
217
+
216
218
  }
217
219
 
218
220
  declare module "sliftutils/misc/https/persistentLocalStorage" {
@@ -4,7 +4,7 @@
4
4
  /// <reference types="node" />
5
5
  import * as forge from "node-forge";
6
6
  export declare const CA_NOT_FOUND_ERROR = "18aa7318-f88f-4d2d-b41f-3daf4a433827";
7
- export declare const identityStorageKey = "machineCA_12";
7
+ export declare const identityStorageKey = "machineCA_14";
8
8
  export type IdentityStorageType = {
9
9
  domain: string;
10
10
  certB64: string;
@@ -23,9 +23,9 @@ export declare function createX509(config: {
23
23
  keyPair: {
24
24
  publicKey: forge.Ed25519PublicKey;
25
25
  privateKey: forge.Ed25519PrivateKey;
26
- } | forge.pki.KeyPair;
26
+ };
27
27
  }): X509KeyPair;
28
- export declare function privateKeyToPem(buffer: forge.pki.PrivateKey | forge.Ed25519PrivateKey): string;
28
+ export declare function privateKeyToPem(key: forge.Ed25519PrivateKey): string;
29
29
  export declare function parseCert(PEMorDER: string | Buffer): forge.pki.Certificate;
30
30
  export declare function getPublicIdentifier(PEMorDER: string | Buffer): Buffer;
31
31
  export declare const sign: (keyPair: {
@@ -34,8 +34,10 @@ export declare const sign: (keyPair: {
34
34
  export declare function verify(cert: string, signature: string, data: unknown): void;
35
35
  export declare function validateCACert(domain: string, cert: string | Buffer): void;
36
36
  export declare function validateCertificate(domain: string, cert: Buffer | string, issuerCert: Buffer | string): void;
37
- export declare function generateKeyPair(): forge.pki.rsa.KeyPair;
38
- export declare function generateRSAKeyPair(): forge.pki.rsa.KeyPair;
37
+ export declare function generateKeyPair(): {
38
+ publicKey: forge.Ed25519PublicKey;
39
+ privateKey: forge.Ed25519PrivateKey;
40
+ };
39
41
  export declare function generateTestCA(domain: string): X509KeyPair;
40
42
  export declare function createCertFromCA(config: {
41
43
  CAKeyPair: X509KeyPair;
@@ -62,10 +64,5 @@ export declare function verifyMachineIdForPublicKey(config: {
62
64
  publicKey: Buffer;
63
65
  }): boolean;
64
66
  export declare function getThreadKeyCert(domain: string): X509KeyPair;
65
- export declare const createTestBrowserKeyCert: {
66
- (): Promise<X509KeyPair>;
67
- reset(): void;
68
- set(newValue: Promise<X509KeyPair>): void;
69
- };
70
67
  export declare function getOwnNodeId(): string;
71
68
  export declare function getOwnNodeIdAllowUndefined(): string;
@@ -2,6 +2,9 @@
2
2
 
3
3
  module.allowclient = true;
4
4
 
5
+ // NOTE: We can't use crypto.subtle (non-extractable CryptoKeys) for our keys, because subtle is asynchronous, and everything here (key generation, cert creation, signing) must be available synchronously. The only real benefit of subtle is that a cross-site scripting attack can't exfiltrate the key. Which, while nice, is of minor benefit, as the cross-site script can already do quite a bit of damage anyway with access. And if that happens, the first thing the user is probably going to do is reset all their credentials, which solves the case of the key being exfiltrated anyway (by telling the server to stop trusting all identities).
6
+ // NOTE: We are just not going to support HTTPS browser certs. Our code is purely for identification, and so it only supports ED25519.
7
+
5
8
  // https://www.rfc-editor.org/rfc/rfc5280#page-42
6
9
 
7
10
  import { setFlag } from "socket-function/require/compileFlags";
@@ -27,7 +30,7 @@ const timeInDay = 1000 * 60 * 60 * 24;
27
30
 
28
31
  export const CA_NOT_FOUND_ERROR = "18aa7318-f88f-4d2d-b41f-3daf4a433827";
29
32
 
30
- export const identityStorageKey = "machineCA_12";
33
+ export const identityStorageKey = "machineCA_14";
31
34
  export type IdentityStorageType = { domain: string; certB64: string; keyB64: string };
32
35
 
33
36
  function getIdentityStore(domain: string) {
@@ -52,19 +55,16 @@ export function createX509(
52
55
  keyPair: {
53
56
  publicKey: forge.Ed25519PublicKey;
54
57
  privateKey: forge.Ed25519PrivateKey;
55
- } | forge.pki.KeyPair;
58
+ };
56
59
  }
57
60
  ): X509KeyPair {
58
61
  return measureBlock(function createX509() {
59
62
  let { domain, issuer, lifeSpan, keyPair } = config;
60
63
 
61
64
  let certObj = forge.pki.createCertificate();
62
- certObj.publicKey = keyPair.publicKey;
65
+ certObj.publicKey = keyPair.publicKey as unknown as forge.pki.PublicKey;
63
66
  certObj.serialNumber = "01";
64
- // Give it 5 minutes before now. If we give it too much time, it can look like the cert is really
65
- // old, which will trigger various processes to try to get a fresher one (as if it lasts for
66
- // 1 hour, but we set notBefore to 1 month ago, it looks 1 month old, and so almost expired,
67
- // when it isn't...)
67
+ // Give it 5 minutes before now. If we give it too much time, it can look like the cert is really old, which will trigger various processes to try to get a fresher one (as if it lasts for 1 hour, but we set notBefore to 1 month ago, it looks 1 month old, and so almost expired, when it isn't...)
68
68
  certObj.validity.notBefore = new Date(Date.now() - 1000 * 60 * 5);
69
69
  certObj.validity.notAfter = new Date(Date.now() + lifeSpan);
70
70
 
@@ -94,15 +94,10 @@ export function createX509(
94
94
  { type: 2, value: domain },
95
95
  { type: 2, value: "*." + domain },
96
96
  { type: 2, value: localHostDomain },
97
- // NOTE: No longer allow 127.0.0.1, to make this more secure. We might enable this
98
- // behavior behind a flag, for development.
99
- //{ type: 7, ip: "127.0.0.1" }
97
+ // NOTE: No longer allow 127.0.0.1 ({ type: 7, ip: "127.0.0.1" }), to make this more secure. We might enable this behavior behind a flag, for development.
100
98
  ]
101
99
  },
102
- // NOTE: nameConstraints require our forked node-forge:
103
- // "node-forge": "https://github.com/sliftist/forge#e618181b469b07bdc70b968b0391beb8ef5fecd6",
104
- // Chrome ignores them (https://bugs.chromium.org/p/chromium/issues/detail?id=1072083),
105
- // but our own validation (validateCACert/validateCertificate) enforces them.
100
+ // NOTE: nameConstraints require our forked node-forge ("node-forge": "https://github.com/sliftist/forge#e618181b469b07bdc70b968b0391beb8ef5fecd6"). Chrome ignores them (https://bugs.chromium.org/p/chromium/issues/detail?id=1072083), but our own validation (validateCACert/validateCertificate) enforces them.
106
101
  {
107
102
  name: "nameConstraints",
108
103
  permittedSubtrees: [
@@ -116,9 +111,9 @@ export function createX509(
116
111
 
117
112
  measureBlock(function sign() {
118
113
  if (issuer === "self") {
119
- certObj.sign(keyPair.privateKey as any, forge.md.sha256.create());
114
+ certObj.sign(keyPair.privateKey as any);
120
115
  } else {
121
- certObj.sign(privateKeyFromPem(issuer.key.toString()) as any, forge.md.sha256.create());
116
+ certObj.sign(forge.ed25519.privateKeyFromPem(issuer.key.toString()) as any);
122
117
  }
123
118
  });
124
119
 
@@ -131,75 +126,37 @@ export function createX509(
131
126
  });
132
127
  });
133
128
  }
134
- export function privateKeyToPem(buffer: forge.pki.PrivateKey | forge.Ed25519PrivateKey) {
135
- if ("privateKeyBytes" in buffer) {
136
- return forge.ed25519.privateKeyToPem(buffer);
137
- }
138
- return forge.pki.privateKeyToPem(buffer);
139
- }
140
- function privateKeyFromPem(pem: string) {
141
- // We want to guess the type correctly, as caught exceptions make debugging annoying
142
- if (pem.length < 200) {
143
- try {
144
- return forge.ed25519.privateKeyFromPem(pem);
145
- } catch { }
146
- }
147
- try {
148
- return forge.pki.privateKeyFromPem(pem);
149
- } catch {
150
- return forge.ed25519.privateKeyFromPem(pem);
151
- }
152
- }
153
- function publicKeyFromCert(cert: string) {
154
- return parseCert(cert).publicKey;
129
+ export function privateKeyToPem(key: forge.Ed25519PrivateKey) {
130
+ return forge.ed25519.privateKeyToPem(key);
155
131
  }
156
132
  export function parseCert(PEMorDER: string | Buffer) {
157
133
  return forge.pki.certificateFromPem(normalizeCertToPEM(PEMorDER));
158
134
  }
159
135
 
160
- // Gets a unique value to represent the public key
161
- export function getPublicIdentifier(PEMorDER: string | Buffer): Buffer {
162
- let obj = parseCert(PEMorDER);
163
- let publicKey = obj.publicKey;
164
- if ("publicKeyBytes" in publicKey) {
165
- return Buffer.from(publicKey.publicKeyBytes as any);
136
+ function getED25519PublicKey(certParsed: forge.pki.Certificate): forge.Ed25519PublicKey {
137
+ let publicKey = certParsed.publicKey;
138
+ if (!("publicKeyBytes" in publicKey)) {
139
+ throw new Error(`Only ED25519 certificates are supported, the certificate public key is not ED25519 (subject: ${certParsed.subject.getField("CN")?.value})`);
166
140
  }
167
- return Buffer.from(new Uint32Array((publicKey as any).n.data).buffer);
141
+ return publicKey as unknown as forge.Ed25519PublicKey;
168
142
  }
169
143
 
170
- function isED25519(key: string | Buffer) {
171
- return key.length < 256;
144
+ // Gets a unique value to represent the public key
145
+ export function getPublicIdentifier(PEMorDER: string | Buffer): Buffer {
146
+ return Buffer.from(getED25519PublicKey(parseCert(PEMorDER)).publicKeyBytes);
172
147
  }
173
148
 
174
- // EQUIVALENT TO: `crypto.createSign("SHA256").update(JSON.stringify(payload)).sign(keyCert.key, "binary")`
175
149
  export const sign = measureWrap(function sign(keyPair: { key: string | Buffer }, data: unknown): string {
176
150
  let dataStr = JSON.stringify(data);
177
- if (isED25519(keyPair.key)) {
178
- let privateKey = (forge.pki.ed25519 as any).privateKeyFromPem(keyPair.key.toString());
179
- return privateKey.sign(dataStr);
180
- } else {
181
- let privateKey = forge.pki.privateKeyFromPem(keyPair.key.toString());
182
- const md = forge.md.sha256.create();
183
- md.update(dataStr);
184
- return privateKey.sign(md);
185
- }
151
+ let privateKey = forge.ed25519.privateKeyFromPem(keyPair.key.toString());
152
+ return privateKey.sign(dataStr);
186
153
  });
187
154
 
188
155
  export function verify(cert: string, signature: string, data: unknown) {
189
156
  let certObj = parseCert(cert);
190
- let publicKey = certObj.publicKey;
157
+ let publicKey = getED25519PublicKey(certObj);
191
158
  let dataStr = JSON.stringify(data);
192
- let verified: boolean;
193
- if ("publicKeyBytes" in publicKey) {
194
- // ed25519 verifies the raw message (it hashes internally)
195
- verified = (publicKey as unknown as { verify(message: string, signature: string): boolean }).verify(dataStr, signature);
196
- } else {
197
- // RSA verifies against the digest, matching the digest sign() creates
198
- const md = forge.md.sha256.create();
199
- md.update(dataStr);
200
- verified = (publicKey as forge.pki.rsa.PublicKey).verify(md.digest().bytes(), signature);
201
- }
202
- if (!verified) {
159
+ if (!publicKey.verify(dataStr, signature)) {
203
160
  throw new Error(`Signature verification failed. Signature: ${JSON.stringify(signature)} | Data: ${ellipsize(dataStr, 1024)}`);
204
161
  }
205
162
  }
@@ -212,8 +169,7 @@ function normalizeCertToPEM(PEMorDER: string | Buffer): string {
212
169
  return "-----BEGIN CERTIFICATE-----\n" + PEMorDER + "\n-----END CERTIFICATE-----";
213
170
  }
214
171
 
215
- // Base32 (RFC 4648, lowercase, unpadded), as domain names are case-insensitive,
216
- // which rules out base64/hex-with-case
172
+ // Base32 (RFC 4648, lowercase, unpadded), as domain names are case-insensitive, which rules out base64/hex-with-case
217
173
  const base32Alphabet = "abcdefghijklmnopqrstuvwxyz234567";
218
174
  function encodeBase32(bytes: Buffer): string {
219
175
  let result = "";
@@ -233,14 +189,12 @@ function encodeBase32(bytes: Buffer): string {
233
189
  return result;
234
190
  }
235
191
 
236
- function getDomainPartFromPublicKey(publicKey: { publicKeyBytes: Buffer } | forge.pki.KeyPair["publicKey"] | Buffer) {
192
+ function getDomainPartFromPublicKey(publicKey: { publicKeyBytes: Buffer } | Buffer) {
237
193
  let bytes: Buffer;
238
194
  if ("publicKeyBytes" in publicKey) {
239
195
  bytes = publicKey.publicKeyBytes;
240
- } else if (publicKey instanceof Buffer) {
241
- bytes = publicKey;
242
196
  } else {
243
- bytes = Buffer.from(new Uint32Array((publicKey as any).n.data).buffer);
197
+ bytes = publicKey;
244
198
  }
245
199
  return "b" + encodeBase32(Buffer.from(sha265.sha256.array(Buffer.from(bytes)))).slice(0, 20);
246
200
  }
@@ -255,9 +209,7 @@ export function validateCACert(domain: string, cert: string | Buffer) {
255
209
 
256
210
  let rootDomainParsed = [domainParts.shift(), domainParts.shift()].reverse().join(".");
257
211
  if (rootDomainParsed !== domain) {
258
- // This is important, as our trust store contains more then just OUR certificates,
259
- // so if we allow any domains then real domains can impersonate anyone! It has to
260
- // be one OUR domain to be trusted!
212
+ // This is important, as our trust store contains more then just OUR certificates, so if we allow any domains then real domains can impersonate anyone! It has to be OUR domain to be trusted!
261
213
  throw new Error(`Certificate root domain should be ${domain}, but is ${rootDomainParsed}`);
262
214
  }
263
215
  // TODO: Maybe just skip if it isn't a hash string?
@@ -266,7 +218,7 @@ export function validateCACert(domain: string, cert: string | Buffer) {
266
218
  }
267
219
 
268
220
  let certExpectedPublicKeyPart = (domainParts.shift() || "").split("-").slice(-1)[0];
269
- let certActualPublicKeyPart = getDomainPartFromPublicKey(certParsed.publicKey);
221
+ let certActualPublicKeyPart = getDomainPartFromPublicKey(getED25519PublicKey(certParsed));
270
222
  if (certExpectedPublicKeyPart !== certActualPublicKeyPart) {
271
223
  throw new Error(`Certificate public key in the url is ${certExpectedPublicKeyPart}, but in the cert is ${certActualPublicKeyPart}`);
272
224
  }
@@ -281,8 +233,7 @@ export function validateCACert(domain: string, cert: string | Buffer) {
281
233
  throw new Error(`Certificate must have nameConstraints.permittedSubtrees`);
282
234
  }
283
235
  let subtreeValues = subtrees.map((x: any) => x.value);
284
- // Ignore localhostDomain, as it can always safely be allowed (the same machine
285
- // is always allowed).
236
+ // Ignore localhostDomain, as it can always safely be allowed (the same machine is always allowed).
286
237
  subtreeValues = subtreeValues.filter((x: string) => x !== localhostDomain);
287
238
  if (subtreeValues.length !== 1 || subtreeValues[0] !== subject) {
288
239
  throw new Error(`Certificate must have a single constrained domain (had ${JSON.stringify(subtreeValues)})`);
@@ -312,14 +263,14 @@ export function validateCertificate(domain: string, cert: Buffer | string, issue
312
263
  let issuerCertParsed = parseCert(issuerCert);
313
264
 
314
265
  let issuerExpectedPublicKeyPart = domainParts.shift() || "";
315
- let issuerActualPublicKeyPart = getDomainPartFromPublicKey(issuerCertParsed.publicKey);
266
+ let issuerActualPublicKeyPart = getDomainPartFromPublicKey(getED25519PublicKey(issuerCertParsed));
316
267
  if (issuerExpectedPublicKeyPart !== issuerActualPublicKeyPart) {
317
268
  throw new Error(`Issuer public key in the url is ${issuerExpectedPublicKeyPart}, but in the cert is ${issuerActualPublicKeyPart}`);
318
269
  }
319
270
 
320
271
  // Take the last part
321
272
  let certExpectedPublicKeyPart = domainParts.shift() || "";
322
- let certActualPublicKeyPart = getDomainPartFromPublicKey(certParsed.publicKey);
273
+ let certActualPublicKeyPart = getDomainPartFromPublicKey(getED25519PublicKey(certParsed));
323
274
  if (certExpectedPublicKeyPart !== certActualPublicKeyPart) {
324
275
  throw new Error(`Certificate public key in the url is ${certExpectedPublicKeyPart}, but in the cert is ${certActualPublicKeyPart}`);
325
276
  }
@@ -334,8 +285,7 @@ export function validateCertificate(domain: string, cert: Buffer | string, issue
334
285
  throw new Error(`CA must have nameConstraints.permittedSubtrees`);
335
286
  }
336
287
  let subtreeValues = subtrees.map((x: any) => x.value);
337
- // Ignore localhostDomain, as it can always safely be allowed (the same machine
338
- // is always allowed).
288
+ // Ignore localhostDomain, as it can always safely be allowed (the same machine is always allowed).
339
289
  subtreeValues = subtreeValues.filter((x: string) => x !== localhostDomain);
340
290
  if (subtreeValues.length !== 1) {
341
291
  throw new Error(`CA must have a single constrained domain (had ${JSON.stringify(subtreeValues)})`);
@@ -367,16 +317,7 @@ function validateAltNames(certParsed: forge.pki.Certificate, subject: string) {
367
317
  !(
368
318
  x === subject
369
319
  || x.endsWith("." + subject)
370
- // Commented out, because... it is so easy to publish a 127.0.0.1 A record,
371
- // and even to generate a real cert, so, we should jsut do that, and keep it secure.
372
- // If we need this for development we can put it behind a flag, so non-development
373
- // instances are still secure.
374
- // // Also allow 127.0.0.1, for local testing, for now?
375
- // // - This might be insecure if these are trusted by the browser,
376
- // // as then anyone that stores any cookies in this ip can have
377
- // // the cookies stolen. But... if this is just cross-server...
378
- // // I don't see how this could cause a security vulnerability.
379
- // || x === Buffer.from([127, 0, 0, 1]).toString()
320
+ // NOTE: We don't allow 127.0.0.1 (|| x === Buffer.from([127, 0, 0, 1]).toString()), because it is so easy to publish a 127.0.0.1 A record, and even to generate a real cert, so we should just do that, and keep it secure. If we need this for development we can put it behind a flag, so non-development instances are still secure.
380
321
  )
381
322
  )
382
323
  ) {
@@ -385,29 +326,10 @@ function validateAltNames(certParsed: forge.pki.Certificate, subject: string) {
385
326
  }
386
327
 
387
328
 
388
- // NOTE: We can't use crypto.subtle (non-extractable CryptoKeys) for our keys, because subtle
389
- // is asynchronous, and everything here (key generation, cert creation, signing) must be
390
- // available synchronously. The only real benefit of subtle is that a cross-site scripting
391
- // attack can't exfiltrate the key. Which, while nice, is of minor benefit, as the cross-site
392
- // script can already do quite a bit of damage anyway with access. And if that happens, the
393
- // first thing the user is probably going to do is reset all their credentials, which solves
394
- // the case of the key being exfiltrated anyway (by telling the server to stop trusting
395
- // all identities).
396
329
  export function generateKeyPair() {
397
330
  return measureBlock(function generateKeyPair() {
398
- // NOTE: We use ED25519 because it can generated keys about 10X faster, WHICH, is still slow
399
- // (~6ms on my machine). So we DEFINITELY don't want it to be 10X slower!
400
- // NOTE: ED25519 doens't have great support in browsers, but we shouldn't need self signed certificates
401
- // in the browser anyway.
402
- // - https://security.stackexchange.com/a/236943/282367
403
- //let keyPair = forge.ed25519.generateKeyPair();
404
- let keyPair = forge.pki.rsa.generateKeyPair();
405
- return keyPair;
406
- });
407
- }
408
- export function generateRSAKeyPair() {
409
- return measureBlock(function generateKeyPair() {
410
- return forge.pki.rsa.generateKeyPair();
331
+ // NOTE: We use ED25519 because it can generate keys about 10X faster than RSA (which is still slow, ~6ms on my machine, so we DEFINITELY don't want it to be 10X slower!) - https://security.stackexchange.com/a/236943/282367
332
+ return forge.ed25519.generateKeyPair();
411
333
  });
412
334
  }
413
335
 
@@ -415,10 +337,6 @@ export function generateTestCA(domain: string) {
415
337
  const keyPair = generateKeyPair();
416
338
  let caPublicKeyPart = getDomainPartFromPublicKey(keyPair.publicKey);
417
339
  let fullDomain = `${caPublicKeyPart}.${domain}`;
418
- if (!isNode()) {
419
- fullDomain = `${caPublicKeyPart}.${domain}`;
420
- }
421
-
422
340
  return createX509({ domain: fullDomain, issuer: "self", keyPair, lifeSpan: timeInDay * 365 * 20 });
423
341
  }
424
342
 
@@ -449,12 +367,7 @@ let identityCA = cache((domain: string) => lazy((): X509KeyPair => {
449
367
  return result;
450
368
  }));
451
369
 
452
- // IMPORTANT! We do not embed any debug info in this domain. If we did, it would be useful,
453
- // but... potentally a security vulnerability, as if the debug info (such as a prefix)
454
- // is used to identify what a certificate is for, it would be easy for an attack to
455
- // forge this (as the debug info won't be secured). So it is much better to keep
456
- // the certificate opaque, and then require any metadata to be actually vetted
457
- // (and hopefully stored in a UI, showing IP, time, etc).
370
+ // IMPORTANT! We do not embed any debug info in this domain. If we did, it would be useful, but... potentally a security vulnerability, as if the debug info (such as a prefix) is used to identify what a certificate is for, it would be easy for an attack to forge this (as the debug info won't be secured). So it is much better to keep the certificate opaque, and then require any metadata to be actually vetted (and hopefully stored in a UI, showing IP, time, etc).
458
371
  export function createCertFromCA(config: {
459
372
  CAKeyPair: X509KeyPair;
460
373
  }): X509KeyPair {
@@ -488,11 +401,7 @@ export function decodeNodeId(nodeId: string, domain: string, allowMissingThreadI
488
401
  return undefined;
489
402
  }
490
403
  let parts = locationObj.address.split(".");
491
- // NOTE: We have to only allow localhost domains on our own domain, as the underlying domain
492
- // gets stripped when we're looking at the machineId. So if we allowed localhost domains on
493
- // other domains, a server could trick us into connecting to it, and then once the connection
494
- // is established, it could talk back and we would think it has a localhost machineId, which
495
- // is implicitly trusted, which would then give it access to everything.
404
+ // NOTE: We have to only allow localhost domains on our own domain, as the underlying domain gets stripped when we're looking at the machineId. So if we allowed localhost domains on other domains, a server could trick us into connecting to it, and then once the connection is established, it could talk back and we would think it has a localhost machineId, which is implicitly trusted, which would then give it access to everything.
496
405
  if (locationObj.address === `127-0-0-1.${domain}` && nodeId.includes(":")) {
497
406
  return {
498
407
  threadId: "",
@@ -542,9 +451,7 @@ export async function setIdentityCARaw(domain: string, json: string) {
542
451
  resetAllNodeCallFactories();
543
452
  }
544
453
 
545
- // NOTE: The identity CA is available synchronously (storage is fs/localStorage, both
546
- // synchronous), so this only exists for backwards compatibility with startup code
547
- // that awaits it.
454
+ // NOTE: The identity CA is available synchronously (storage is fs/localStorage, both synchronous), so this only exists for backwards compatibility with startup code that awaits it.
548
455
  export async function loadIdentityCA(domain: string) {
549
456
  identityCA(domain)();
550
457
  }
@@ -552,8 +459,7 @@ export function getIdentityCA(domain: string): X509KeyPair {
552
459
  return identityCA(domain)();
553
460
  }
554
461
 
555
- // TODO: Replace this with a database, so it is easy for us to trust CAs
556
- // cross machine, and even have multiple users, etc, etc.
462
+ // TODO: Replace this with a database, so it is easy for us to trust CAs cross machine, and even have multiple users, etc, etc.
557
463
  export function getIdentityCAPromise(domain: string): X509KeyPair {
558
464
  return identityCA(domain)();
559
465
  }
@@ -578,11 +484,8 @@ export function verifyMachineIdForPublicKey(config: {
578
484
  }
579
485
 
580
486
  // NOTE: We don't have a cache per CA, as... the CA should be set first
581
- // TODO: Maybe throw if they try to change the CA after they generate any certificates?
582
- // TODO: Regenerate certificates after enough time (as thread certs should be relatively short lived,
583
- // so it is plausible for them to expire)
584
- // - We will also need to provide a callback so that users of the cert can update the cert they
585
- // are using as well.
487
+ // TODO: Maybe throw if they try to change the CA after they generate any certificates?
488
+ // TODO: Regenerate certificates after enough time (as thread certs should be relatively short lived, so it is plausible for them to expire). We will also need to provide a callback so that users of the cert can update the cert they are using as well.
586
489
  export function getThreadKeyCert(domain: string) {
587
490
  return getThreadKeyCertBase(domain)();
588
491
  }
@@ -591,11 +494,6 @@ const getThreadKeyCertBase = cache((domain: string) => lazy(() => {
591
494
  return createCertFromCA({ CAKeyPair: ca });
592
495
  }));
593
496
 
594
- export const createTestBrowserKeyCert = lazy(async () => {
595
- let keyPair = generateRSAKeyPair();
596
- return await createX509({ domain: "test", issuer: "self", keyPair, lifeSpan: timeInDay * 365 * 20 });
597
- });
598
-
599
497
  export function getOwnNodeId(): string {
600
498
  let nodeId = SocketFunction.mountedNodeId;
601
499
  if (!nodeId) {
@@ -3,10 +3,14 @@
3
3
  declare module "node-forge" {
4
4
  declare type Ed25519PublicKey = {
5
5
  publicKeyBytes: Buffer;
6
- } & Buffer;
6
+ keyType: string;
7
+ verify(message: string | Buffer, signature: string): boolean;
8
+ };
7
9
  declare type Ed25519PrivateKey = {
8
10
  privateKeyBytes: Buffer;
9
- } & Buffer;
11
+ keyType: string;
12
+ sign(message: string | Buffer): string;
13
+ };
10
14
  class ed25519 {
11
15
  static generateKeyPair(): { publicKey: Ed25519PublicKey, privateKey: Ed25519PrivateKey };
12
16
  static privateKeyToPem(key: Ed25519PrivateKey): string;
@@ -14,4 +18,4 @@ declare module "node-forge" {
14
18
  static publicKeyToPem(key: Ed25519PublicKey): string;
15
19
  static publicKeyFromPem(pem: string): Ed25519PublicKey;
16
20
  }
17
- }
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sliftutils",
3
- "version": "1.5.5",
3
+ "version": "1.5.6",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "files": [