sliftutils 1.7.143 → 1.7.145
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/misc/random.ts +1 -1
- package/package.json +1 -1
- package/security/machines/identity2.ts +27 -29
package/misc/random.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
NOTE: This whole verification process isn't secure if someone man-in-the-middles the IP. There is a way to make it more secure, but right now it is quite a bit of work. Here is the outline, though.
|
|
3
|
+
|
|
4
|
+
a) Use libsodium, do handshake ourself over unsecured websocket (or random self signed websocket), and use ChaCha20 for encryption so it's fast
|
|
5
|
+
- This lets us use ED25519 certificates.
|
|
6
|
+
b) Integrate with socket-function, So we can do everything with regular socket function calls. It's a lot nicer of an interface. We can even have it proxied to Python. As annoying as that is...
|
|
7
|
+
c) Use github pages to host the site, so we can also host the machine keys
|
|
8
|
+
- Every time they change, we need to redeploy, and... we don't get revocations without deploying. BUT... it's better than nothing.
|
|
9
|
+
|
|
10
|
+
NOTE: Just using real HTTPS certificates isn't really an alternative.
|
|
11
|
+
- They won't be bound to IPs
|
|
12
|
+
- Revoking them is a lot more difficult (and they won't be automatically revoked)
|
|
13
|
+
- Once you reach certain level of machines, you're going to run into problems with different certificates per machine
|
|
14
|
+
- You're still going to need a way to give servers access to either the certificates or a way to generate the certificates.
|
|
15
|
+
|
|
16
|
+
So doing it with our own code isn't about cost or difficulty. The reason we would want to implement it ourselves is because HTTPS wasn't made to create an internal network of trust. It's designed around web browsing, and it works well for that. However, for two-way authentication, there are significantly better systems.
|
|
17
|
+
*/
|
|
18
|
+
|
|
1
19
|
import sha256 from "js-sha256";
|
|
2
20
|
import { isNode } from "socket-function/src/misc";
|
|
3
21
|
import { getCommonName, getIdentityCA, getMachineId, getThreadKeyCert, sign, validateCertificate, verify } from "../../misc/https/certs";
|
|
4
22
|
import { getTrustedMachines } from "./machines";
|
|
5
23
|
|
|
6
|
-
const MAX_SIGNED_AGE = 5 * 60 * 1000;
|
|
7
|
-
|
|
8
24
|
export type SignedRequest<T> = {
|
|
9
25
|
signature: string;
|
|
10
26
|
payload: {
|
|
11
|
-
//
|
|
12
|
-
time: number;
|
|
13
|
-
// Who the sender believes it is talking to: an IP, a machineId, a threadId - whatever
|
|
14
|
-
// both sides use to name the server. Being inside the signed payload is what stops a
|
|
15
|
-
// machine in the middle: a request addressed to someone else cannot be replayed at the
|
|
16
|
-
// real server, and the middleman cannot write requests of its own because it is not a
|
|
17
|
-
// trusted machine.
|
|
27
|
+
// The ip we are talking to.
|
|
18
28
|
targetId: string;
|
|
19
29
|
// The thread certificate that signed this
|
|
20
30
|
cert: string;
|
|
@@ -27,7 +37,6 @@ export type SignedRequest<T> = {
|
|
|
27
37
|
export type SignedReply = {
|
|
28
38
|
signature: string;
|
|
29
39
|
payload: {
|
|
30
|
-
time: number;
|
|
31
40
|
// The request this reply answers, with the data collapsed to its hash - resending the
|
|
32
41
|
// data would say nothing the hash does not
|
|
33
42
|
request: Omit<SignedRequest<unknown>["payload"], "data"> & { dataHash: string };
|
|
@@ -44,7 +53,6 @@ export function signRequest<T>(domain: string, config: { targetId: string; data:
|
|
|
44
53
|
let threadKeyCert = getThreadKeyCert(domain);
|
|
45
54
|
let issuer = getIdentityCA(domain);
|
|
46
55
|
let payload: SignedRequest<T>["payload"] = {
|
|
47
|
-
time: Date.now(),
|
|
48
56
|
targetId: config.targetId,
|
|
49
57
|
cert: threadKeyCert.cert.toString(),
|
|
50
58
|
certIssuer: issuer.cert.toString(),
|
|
@@ -54,11 +62,10 @@ export function signRequest<T>(domain: string, config: { targetId: string; data:
|
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
/** Returns the machineId that signed, the data, and the signed reply to send back - always
|
|
57
|
-
produced, so the caller's only job is to return it. Throws if the signature
|
|
58
|
-
chain
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
them.
|
|
65
|
+
produced, so the caller's only job is to return it. Throws if the signature or certificate
|
|
66
|
+
chain do not check out, or if the request was addressed to someone we are not: ownIdentities
|
|
67
|
+
is every address this server answers to - there are always several, internal and external -
|
|
68
|
+
and the signed targetId must be one of them.
|
|
62
69
|
|
|
63
70
|
IMPORTANT! You still need to check the machine with isMachineAccepted if you want to know if
|
|
64
71
|
the machine is trusted. We just verify that they are who they say they are, not that who they
|
|
@@ -69,10 +76,6 @@ export function verifyRequest<T>(domain: string, signed: SignedRequest<T>, ownId
|
|
|
69
76
|
reply: SignedReply;
|
|
70
77
|
} {
|
|
71
78
|
let { signature, payload } = signed;
|
|
72
|
-
let signedThreshold = Date.now() - MAX_SIGNED_AGE;
|
|
73
|
-
if (payload.time < signedThreshold) {
|
|
74
|
-
throw new Error(`Signed request too old, ${payload.time} < ${signedThreshold}`);
|
|
75
|
-
}
|
|
76
79
|
verify(payload.cert, signature, payload);
|
|
77
80
|
validateCertificate(domain, payload.cert, payload.certIssuer);
|
|
78
81
|
if (!ownIdentities.includes(payload.targetId)) {
|
|
@@ -83,7 +86,6 @@ export function verifyRequest<T>(domain: string, signed: SignedRequest<T>, ownId
|
|
|
83
86
|
let issuer = getIdentityCA(domain);
|
|
84
87
|
let { data: requestData, ...requestRest } = payload;
|
|
85
88
|
let replyPayload: SignedReply["payload"] = {
|
|
86
|
-
time: Date.now(),
|
|
87
89
|
request: { ...requestRest, dataHash: dataHash(requestData) },
|
|
88
90
|
cert: threadKeyCert.cert.toString(),
|
|
89
91
|
certIssuer: issuer.cert.toString(),
|
|
@@ -95,16 +97,12 @@ export function verifyRequest<T>(domain: string, signed: SignedRequest<T>, ownId
|
|
|
95
97
|
};
|
|
96
98
|
}
|
|
97
99
|
|
|
98
|
-
/** Returns the machineId that replied. Throws if the signature
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
/** Returns the machineId that replied. Throws if the signature or certificate chain do not check
|
|
101
|
+
out, or if the reply does not answer the request we actually sent. On node the replying
|
|
102
|
+
machine must also be one of the trusted machines - the browser has no machine list, so there
|
|
103
|
+
that check is skipped. */
|
|
102
104
|
export async function verifyReply(domain: string, request: SignedRequest<unknown>, reply: SignedReply): Promise<{ machineId: string }> {
|
|
103
105
|
let { signature, payload } = reply;
|
|
104
|
-
let signedThreshold = Date.now() - MAX_SIGNED_AGE;
|
|
105
|
-
if (payload.time < signedThreshold) {
|
|
106
|
-
throw new Error(`Signed reply too old, ${payload.time} < ${signedThreshold}`);
|
|
107
|
-
}
|
|
108
106
|
verify(payload.cert, signature, payload);
|
|
109
107
|
validateCertificate(domain, payload.cert, payload.certIssuer);
|
|
110
108
|
let { data: requestData, ...requestRest } = request.payload;
|