toiljs 0.0.51 → 0.0.53
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/CHANGELOG.md +19 -0
- package/TYPESCRIPT_LAW.md +12601 -0
- package/build/cli/.tsbuildinfo +1 -1
- package/build/cli/index.js +16 -1
- package/build/client/.tsbuildinfo +1 -1
- package/build/client/auth.d.ts +9 -20
- package/build/client/auth.js +112 -95
- package/build/client/index.d.ts +2 -2
- package/build/client/index.js +1 -1
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/generate.js +1 -1
- package/build/devserver/.tsbuildinfo +1 -1
- package/build/devserver/crypto.js +33 -0
- package/build/devserver/host.js +2 -0
- package/build/devserver/kv.d.ts +3 -0
- package/build/devserver/kv.js +53 -0
- package/build/devserver/module.js +2 -1
- package/docs/auth-todo.md +149 -0
- package/docs/auth.md +234 -173
- package/examples/basic/client/routes/pq.tsx +72 -103
- package/examples/basic/server/core/AppHandler.ts +24 -3
- package/examples/basic/server/main.ts +0 -1
- package/examples/basic/server/routes/Auth.ts +304 -99
- package/examples/basic/server/routes/Session.ts +5 -2
- package/package.json +2 -1
- package/server/globals/auth.ts +263 -10
- package/src/cli/diagnostics.ts +22 -0
- package/src/cli/doctor.ts +2 -0
- package/src/client/auth.ts +192 -174
- package/src/client/index.ts +2 -2
- package/src/compiler/generate.ts +1 -1
- package/src/devserver/crypto.ts +54 -0
- package/src/devserver/host.ts +6 -0
- package/src/devserver/kv.ts +93 -0
- package/src/devserver/module.ts +4 -1
- package/test/devserver-pqauth.test.ts +153 -0
- package/test/doctor.test.ts +22 -0
- package/test/pqauth-e2e.test.ts +207 -0
- package/examples/basic/server/routes/PqDemo.ts +0 -127
package/build/client/auth.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export declare const LOGIN_CONTEXT = "qauth:login:v1";
|
|
2
|
+
export declare const REGISTER_CONTEXT = "qauth:register:v1";
|
|
3
|
+
export declare const SESSION_KEY_LABEL = "toil-session-key-v1";
|
|
4
|
+
export declare const SERVER_CONFIRM_LABEL = "toil-server-confirm-v1";
|
|
5
|
+
export declare const KEM_PUBLIC_KEY_LEN = 1184;
|
|
6
|
+
export declare const KEM_CIPHERTEXT_LEN = 1088;
|
|
7
|
+
export declare const SHARED_SECRET_LEN = 32;
|
|
8
|
+
export declare const SERVER_KEM_PUBLIC_KEY: Uint8Array<ArrayBufferLike>;
|
|
2
9
|
export declare const PUBLIC_KEY_LEN = 1312;
|
|
3
10
|
export declare const SECRET_KEY_LEN = 2560;
|
|
4
11
|
export declare const SIGNATURE_LEN = 2420;
|
|
@@ -9,34 +16,16 @@ export interface KdfParams {
|
|
|
9
16
|
readonly parallelism: number;
|
|
10
17
|
readonly salt: Uint8Array;
|
|
11
18
|
}
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
readonly aud: string;
|
|
15
|
-
readonly kdf: KdfParams;
|
|
16
|
-
readonly nonce: Uint8Array;
|
|
17
|
-
readonly iat: bigint;
|
|
18
|
-
readonly exp: bigint;
|
|
19
|
-
}
|
|
20
|
-
export declare function buildLoginMessage(sub: string, aud: string, cid: Uint8Array, nonce: Uint8Array, iat: bigint, exp: bigint): Uint8Array;
|
|
19
|
+
export declare function buildLoginMessage(sub: string, aud: string, cid: Uint8Array, nonce: Uint8Array, iat: bigint, exp: bigint, ciphertext: Uint8Array, memKiB: number, iterations: number, parallelism: number, serverKemKeyId: Uint8Array): Uint8Array;
|
|
20
|
+
export declare function buildRegisterMessage(username: string, publicKey: Uint8Array): Uint8Array;
|
|
21
21
|
export interface AuthOptions {
|
|
22
22
|
readonly baseUrl?: string;
|
|
23
23
|
}
|
|
24
24
|
export declare function register(username: string, password: string, opts?: AuthOptions): Promise<void>;
|
|
25
25
|
export declare function login(username: string, password: string, opts?: AuthOptions): Promise<Uint8Array>;
|
|
26
|
-
export interface IdentityProof {
|
|
27
|
-
readonly envelope: Uint8Array;
|
|
28
|
-
readonly publicKeyHex: string;
|
|
29
|
-
readonly nonceHex: string;
|
|
30
|
-
readonly signatureLen: number;
|
|
31
|
-
readonly deriveMs: number;
|
|
32
|
-
}
|
|
33
|
-
export declare function proveIdentity(username: string, password: string, opts?: {
|
|
34
|
-
baseUrl?: string;
|
|
35
|
-
}): Promise<IdentityProof>;
|
|
36
26
|
export declare const Auth: {
|
|
37
27
|
readonly register: typeof register;
|
|
38
28
|
readonly login: typeof login;
|
|
39
|
-
readonly proveIdentity: typeof proveIdentity;
|
|
40
29
|
readonly buildLoginMessage: typeof buildLoginMessage;
|
|
41
30
|
readonly LOGIN_CONTEXT: "qauth:login:v1";
|
|
42
31
|
};
|
package/build/client/auth.js
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
|
-
import { argon2id,
|
|
1
|
+
import { argon2id, createSHA256, createHMAC } from 'hash-wasm';
|
|
2
2
|
import { ml_dsa44 } from '@dacely/noble-post-quantum/ml-dsa.js';
|
|
3
|
+
import { ml_kem768 } from '@dacely/noble-post-quantum/ml-kem.js';
|
|
4
|
+
import { ristretto255_oprf } from '@noble/curves/ed25519.js';
|
|
3
5
|
import { DataReader, DataWriter } from 'toiljs/io';
|
|
4
6
|
export const LOGIN_CONTEXT = 'qauth:login:v1';
|
|
7
|
+
export const REGISTER_CONTEXT = 'qauth:register:v1';
|
|
8
|
+
export const SESSION_KEY_LABEL = 'toil-session-key-v1';
|
|
9
|
+
export const SERVER_CONFIRM_LABEL = 'toil-server-confirm-v1';
|
|
10
|
+
export const KEM_PUBLIC_KEY_LEN = 1184;
|
|
11
|
+
export const KEM_CIPHERTEXT_LEN = 1088;
|
|
12
|
+
export const SHARED_SECRET_LEN = 32;
|
|
13
|
+
function fromHex(hex) {
|
|
14
|
+
const out = new Uint8Array(hex.length / 2);
|
|
15
|
+
for (let i = 0; i < out.length; i++)
|
|
16
|
+
out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
export const SERVER_KEM_PUBLIC_KEY = fromHex('29d765e8083182891302569b3712a856e564fdd484b0706b0c68568d5ab7edc742cf74459d64595455a60f267973aa55e43c5be61925a3822eafcca445e36dc4655636e31e6fc9bec338b253f94290008ef7f40dbddb49c15c690f6755a23a1b3c85cfd5207e71a607086a6fc6d74a05080f43276901a19cafdb8de7771d58ea07f0f1056b905127b22223d08e75173199f13ab13c5dcd3b51ac784f84e520484a262b845a897c41cf27324ab6ba545c78c9ccab361051e0bba53498af26240fa0d566d1572684f4b42e253e6d052c848650915063c35641e1121ef8d9cfd17b667b351103c56d195007c9376d0c08aa268396814490eab4c364175a94533267a1933862cc4c33bcf0a13d1fa2b9d6c5082eeca1480672f2526cbe013beff14dc908a386e0b633c8761023cbed760deac6709bc328d865ac82e12307b673d96711dbb27a4d939230d25b53d594169a318be0200fa33550e9418e2a3b30e9719edc09d5fc4306f1abfd021eab14637a8a72c5931d25dc9b56db0e6ab677522b10f25307dbb804a6774ce05b87b0976a4b227bfe6caf20a79e64004fbd27b1eea018b3ab8ffa629f2dc87f19278f95168e94e44660a3370c537795678eb2f056260609769740583b51b291862927a1938737c6a37f40b78f00671cccbcb88ac3427b37915ed58782998f84051647707d48995472baad3f64a7cca54e1c0734db08751c614a34f28b84f2c1b5a6817355ab61957c486b7acffbc092bc8a7b46387f33b53ed372f7168d31a71cd008539928b0cdf91e835aa97f6a2be6d327b87a6ae478701d75a59a25179cb14997bb2552853014724170a1c49b82c2bcebc3279024e1fa44c53c7afdc43f0bd22116490f3b74c90e7296be58b9a91168f2fa0c3d378a3bcac959f357825c9976a8c9ee944f29b45e96d7345d9b478431a20cf1c5d3a3227c717fd204619777636c0cb140db5c50d2a3302334461030bee34e4eb1a6f02b733f9ccda4290fa168bc039568373241542728d00030d1f251e83737cb215adbdc1de75978675a0cd0d75b12748abdda7a9852629c63697d145af2c69854b06e03f37c4b064e4c9a4c03f2ad4d081e70180e9547247921918118086b62b4f7727f46b24e3e79ba3f28209f32b5102035bf935856232f83642268c0292ec6bf8e9462382163d30a20b4bcb7b4439310ec9d0a148193907fc07697342967cf1a16c6b3c71558951fa915400736cf699262b54b723abb2ecc27b74b68ee494287595ef818388adb49e883c67bfa5c226c0eef037a0851a29d34675912c1ea1068310b6dfcd017c809c8fbfc2c3ae78dfef07299960eeefba182662a90fa422c1790f356a2ea909012b15623a9b9e450a282cb530589a68368b3583159d9010ac3e52cc974753c342e58279516339dfb691df94b13a223ad97eb6a09c21dafe6304a3642d6d2067b5238497661fe88ad1227ca3557be2a576b6e17c5a7f997ea07929e76407e376aba74c44cd8504804776f39bbb8327624188a63501e83b404d9438cade0b11dc3ac61856447fb072b91761c228878f01b2eb6b4b21ba664c2c75882431603b25a449ffeb8410b910558581777562aa9b2181fd9c04713ad9326462d3e842121c4997f9aa932417c67851625816de66e0d65637434629f39');
|
|
5
20
|
export const PUBLIC_KEY_LEN = 1312;
|
|
6
21
|
export const SECRET_KEY_LEN = 2560;
|
|
7
22
|
export const SIGNATURE_LEN = 2420;
|
|
@@ -10,9 +25,9 @@ function wipe(buf) {
|
|
|
10
25
|
crypto.getRandomValues(buf);
|
|
11
26
|
buf.fill(0);
|
|
12
27
|
}
|
|
13
|
-
async function deriveSeed(
|
|
28
|
+
async function deriveSeed(oprfOutput, kdf) {
|
|
14
29
|
return argon2id({
|
|
15
|
-
password:
|
|
30
|
+
password: oprfOutput,
|
|
16
31
|
salt: kdf.salt,
|
|
17
32
|
iterations: kdf.iterations,
|
|
18
33
|
parallelism: kdf.parallelism,
|
|
@@ -21,7 +36,40 @@ async function deriveSeed(password, kdf) {
|
|
|
21
36
|
outputType: 'binary',
|
|
22
37
|
});
|
|
23
38
|
}
|
|
24
|
-
|
|
39
|
+
const utf8 = (s) => new TextEncoder().encode(s);
|
|
40
|
+
async function sha256Bytes(data) {
|
|
41
|
+
const h = await createSHA256();
|
|
42
|
+
h.init();
|
|
43
|
+
h.update(data);
|
|
44
|
+
return h.digest('binary');
|
|
45
|
+
}
|
|
46
|
+
async function hmacSha256(key, msg) {
|
|
47
|
+
const h = await createHMAC(createSHA256(), key);
|
|
48
|
+
h.init();
|
|
49
|
+
h.update(msg);
|
|
50
|
+
return h.digest('binary');
|
|
51
|
+
}
|
|
52
|
+
function concatBytes(...parts) {
|
|
53
|
+
let n = 0;
|
|
54
|
+
for (const p of parts)
|
|
55
|
+
n += p.length;
|
|
56
|
+
const out = new Uint8Array(n);
|
|
57
|
+
let off = 0;
|
|
58
|
+
for (const p of parts) {
|
|
59
|
+
out.set(p, off);
|
|
60
|
+
off += p.length;
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
function bytesEqual(a, b) {
|
|
65
|
+
if (a.length !== b.length)
|
|
66
|
+
return false;
|
|
67
|
+
let diff = 0;
|
|
68
|
+
for (let i = 0; i < a.length; i++)
|
|
69
|
+
diff |= a[i] ^ b[i];
|
|
70
|
+
return diff === 0;
|
|
71
|
+
}
|
|
72
|
+
export function buildLoginMessage(sub, aud, cid, nonce, iat, exp, ciphertext, memKiB, iterations, parallelism, serverKemKeyId) {
|
|
25
73
|
return new DataWriter()
|
|
26
74
|
.writeU8(1)
|
|
27
75
|
.writeString(sub)
|
|
@@ -30,8 +78,16 @@ export function buildLoginMessage(sub, aud, cid, nonce, iat, exp) {
|
|
|
30
78
|
.writeBytes(nonce)
|
|
31
79
|
.writeU64(iat)
|
|
32
80
|
.writeU64(exp)
|
|
81
|
+
.writeBytes(ciphertext)
|
|
82
|
+
.writeU32(memKiB)
|
|
83
|
+
.writeU32(iterations)
|
|
84
|
+
.writeU32(parallelism)
|
|
85
|
+
.writeBytes(serverKemKeyId)
|
|
33
86
|
.toBytes();
|
|
34
87
|
}
|
|
88
|
+
export function buildRegisterMessage(username, publicKey) {
|
|
89
|
+
return new DataWriter().writeU8(1).writeString(username).writeBytes(publicKey).toBytes();
|
|
90
|
+
}
|
|
35
91
|
function decodeKdf(r) {
|
|
36
92
|
return {
|
|
37
93
|
memKiB: r.readU32(),
|
|
@@ -40,15 +96,6 @@ function decodeKdf(r) {
|
|
|
40
96
|
salt: r.readBytes(),
|
|
41
97
|
};
|
|
42
98
|
}
|
|
43
|
-
function decodeChallenge(r) {
|
|
44
|
-
const cid = r.readBytes();
|
|
45
|
-
const aud = r.readString();
|
|
46
|
-
const kdf = decodeKdf(r);
|
|
47
|
-
const nonce = r.readBytes();
|
|
48
|
-
const iat = r.readU64();
|
|
49
|
-
const exp = r.readU64();
|
|
50
|
-
return { cid, aud, kdf, nonce, iat, exp };
|
|
51
|
-
}
|
|
52
99
|
async function postBinary(baseUrl, path, body) {
|
|
53
100
|
const res = await fetch(baseUrl + path, {
|
|
54
101
|
method: 'POST',
|
|
@@ -62,39 +109,68 @@ async function postBinary(baseUrl, path, body) {
|
|
|
62
109
|
}
|
|
63
110
|
export async function register(username, password, opts = {}) {
|
|
64
111
|
const baseUrl = opts.baseUrl ?? '/auth';
|
|
65
|
-
const
|
|
112
|
+
const oprf = ristretto255_oprf.oprf;
|
|
113
|
+
const pw = utf8(password.normalize('NFKC'));
|
|
114
|
+
const { blind, blinded } = oprf.blind(pw);
|
|
115
|
+
const start = await postBinary(baseUrl, '/register/start', new DataWriter().writeString(username).writeBytes(blinded).toBytes());
|
|
66
116
|
const status = start.readU8();
|
|
67
117
|
if (status !== 0)
|
|
68
118
|
throw new Error('auth: registration unavailable');
|
|
69
119
|
const kdf = decodeKdf(start);
|
|
70
|
-
const
|
|
120
|
+
const evaluated = start.readBytes();
|
|
121
|
+
const oprfOutput = oprf.finalize(pw, blind, evaluated);
|
|
122
|
+
const seed = await deriveSeed(oprfOutput, kdf);
|
|
71
123
|
let publicKey;
|
|
124
|
+
let regProof;
|
|
72
125
|
try {
|
|
73
126
|
const kp = ml_dsa44.keygen(seed);
|
|
74
127
|
publicKey = kp.publicKey;
|
|
75
|
-
|
|
128
|
+
try {
|
|
129
|
+
regProof = ml_dsa44.sign(buildRegisterMessage(username, publicKey), kp.secretKey, {
|
|
130
|
+
context: utf8(REGISTER_CONTEXT),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
wipe(kp.secretKey);
|
|
135
|
+
}
|
|
76
136
|
}
|
|
77
137
|
finally {
|
|
78
138
|
wipe(seed);
|
|
79
139
|
}
|
|
80
140
|
if (publicKey.length !== PUBLIC_KEY_LEN)
|
|
81
141
|
throw new Error('auth: bad public key length');
|
|
82
|
-
const finish = await postBinary(baseUrl, '/register/finish', new DataWriter().writeString(username).writeBytes(publicKey).toBytes());
|
|
83
|
-
|
|
142
|
+
const finish = await postBinary(baseUrl, '/register/finish', new DataWriter().writeString(username).writeBytes(publicKey).writeBytes(regProof).toBytes());
|
|
143
|
+
const finishStatus = finish.readU8();
|
|
144
|
+
if (finishStatus === 1)
|
|
145
|
+
throw new Error('auth: username already registered (log in instead)');
|
|
146
|
+
if (finishStatus !== 0)
|
|
84
147
|
throw new Error('auth: registration rejected');
|
|
85
148
|
}
|
|
86
149
|
export async function login(username, password, opts = {}) {
|
|
87
150
|
const baseUrl = opts.baseUrl ?? '/auth';
|
|
88
|
-
const
|
|
89
|
-
|
|
151
|
+
const oprf = ristretto255_oprf.oprf;
|
|
152
|
+
const pw = utf8(password.normalize('NFKC'));
|
|
153
|
+
const { blind, blinded } = oprf.blind(pw);
|
|
154
|
+
const r = await postBinary(baseUrl, '/login/start', new DataWriter().writeString(username).writeBytes(blinded).toBytes());
|
|
155
|
+
const cid = r.readBytes();
|
|
156
|
+
const aud = r.readString();
|
|
157
|
+
const kdf = decodeKdf(r);
|
|
158
|
+
const nonce = r.readBytes();
|
|
159
|
+
const iat = r.readU64();
|
|
160
|
+
const exp = r.readU64();
|
|
161
|
+
const evaluated = r.readBytes();
|
|
162
|
+
if (BigInt(Math.floor(Date.now() / 1000)) >= exp)
|
|
90
163
|
throw new Error('auth: challenge expired');
|
|
91
|
-
const
|
|
92
|
-
const seed = await deriveSeed(
|
|
164
|
+
const oprfOutput = oprf.finalize(pw, blind, evaluated);
|
|
165
|
+
const seed = await deriveSeed(oprfOutput, kdf);
|
|
166
|
+
const { cipherText, sharedSecret } = ml_kem768.encapsulate(SERVER_KEM_PUBLIC_KEY);
|
|
167
|
+
const serverKemKeyId = await sha256Bytes(SERVER_KEM_PUBLIC_KEY);
|
|
168
|
+
const message = buildLoginMessage(username, aud, cid, nonce, iat, exp, cipherText, kdf.memKiB, kdf.iterations, kdf.parallelism, serverKemKeyId);
|
|
93
169
|
let signature;
|
|
94
170
|
try {
|
|
95
171
|
const kp = ml_dsa44.keygen(seed);
|
|
96
172
|
try {
|
|
97
|
-
signature = ml_dsa44.sign(message, kp.secretKey, { context:
|
|
173
|
+
signature = ml_dsa44.sign(message, kp.secretKey, { context: utf8(LOGIN_CONTEXT) });
|
|
98
174
|
}
|
|
99
175
|
finally {
|
|
100
176
|
wipe(kp.secretKey);
|
|
@@ -105,78 +181,19 @@ export async function login(username, password, opts = {}) {
|
|
|
105
181
|
}
|
|
106
182
|
if (signature.length !== SIGNATURE_LEN)
|
|
107
183
|
throw new Error('auth: bad signature length');
|
|
108
|
-
const res = await postBinary(baseUrl, '/login/finish', new DataWriter().writeBytes(
|
|
109
|
-
if (res.readU8() !== 0)
|
|
184
|
+
const res = await postBinary(baseUrl, '/login/finish', new DataWriter().writeBytes(cid).writeBytes(cipherText).writeBytes(signature).toBytes());
|
|
185
|
+
if (res.readU8() !== 0) {
|
|
186
|
+
wipe(sharedSecret);
|
|
110
187
|
throw new Error('auth: login failed');
|
|
111
|
-
return res.readBytes();
|
|
112
|
-
}
|
|
113
|
-
function toHex(bytes) {
|
|
114
|
-
let s = '';
|
|
115
|
-
for (const b of bytes)
|
|
116
|
-
s += b.toString(16).padStart(2, '0');
|
|
117
|
-
return s;
|
|
118
|
-
}
|
|
119
|
-
async function demoSalt(username) {
|
|
120
|
-
const hex = await sha256('pq-demo|' + username);
|
|
121
|
-
const out = new Uint8Array(16);
|
|
122
|
-
for (let i = 0; i < 16; i++)
|
|
123
|
-
out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
124
|
-
return out;
|
|
125
|
-
}
|
|
126
|
-
export async function proveIdentity(username, password, opts = {}) {
|
|
127
|
-
const baseUrl = opts.baseUrl ?? '/pq';
|
|
128
|
-
const cres = await fetch(baseUrl + '/challenge', { credentials: 'same-origin' });
|
|
129
|
-
if (!cres.ok)
|
|
130
|
-
throw new Error('pq: challenge request failed');
|
|
131
|
-
const cr = new DataReader(new Uint8Array(await cres.arrayBuffer()));
|
|
132
|
-
const aud = cr.readString();
|
|
133
|
-
const cid = cr.readBytes();
|
|
134
|
-
const nonce = cr.readBytes();
|
|
135
|
-
const iat = cr.readU64();
|
|
136
|
-
const exp = cr.readU64();
|
|
137
|
-
const token = cr.readString();
|
|
138
|
-
const salt = await demoSalt(username);
|
|
139
|
-
const t0 = Date.now();
|
|
140
|
-
const seed = await argon2id({
|
|
141
|
-
password: new TextEncoder().encode(password.normalize('NFKC')),
|
|
142
|
-
salt,
|
|
143
|
-
iterations: 2,
|
|
144
|
-
parallelism: 1,
|
|
145
|
-
memorySize: 16 * 1024,
|
|
146
|
-
hashLength: SEED_LEN,
|
|
147
|
-
outputType: 'binary',
|
|
148
|
-
});
|
|
149
|
-
const deriveMs = Date.now() - t0;
|
|
150
|
-
const message = buildLoginMessage(username, aud, cid, nonce, iat, exp);
|
|
151
|
-
let publicKey;
|
|
152
|
-
let signature;
|
|
153
|
-
try {
|
|
154
|
-
const kp = ml_dsa44.keygen(seed);
|
|
155
|
-
publicKey = kp.publicKey;
|
|
156
|
-
try {
|
|
157
|
-
signature = ml_dsa44.sign(message, kp.secretKey, {
|
|
158
|
-
context: new TextEncoder().encode(LOGIN_CONTEXT),
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
finally {
|
|
162
|
-
wipe(kp.secretKey);
|
|
163
|
-
}
|
|
164
188
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return {
|
|
175
|
-
envelope,
|
|
176
|
-
publicKeyHex: toHex(publicKey.slice(0, 16)),
|
|
177
|
-
nonceHex: toHex(nonce.slice(0, 16)),
|
|
178
|
-
signatureLen: signature.length,
|
|
179
|
-
deriveMs,
|
|
180
|
-
};
|
|
189
|
+
const session = res.readBytes();
|
|
190
|
+
const serverConfirm = res.readBytes();
|
|
191
|
+
const transcriptHash = await sha256Bytes(message);
|
|
192
|
+
const sessionKey = await hmacSha256(sharedSecret, concatBytes(utf8(SESSION_KEY_LABEL), transcriptHash));
|
|
193
|
+
wipe(sharedSecret);
|
|
194
|
+
const expected = await hmacSha256(sessionKey, concatBytes(utf8(SERVER_CONFIRM_LABEL), transcriptHash));
|
|
195
|
+
if (!bytesEqual(expected, serverConfirm))
|
|
196
|
+
throw new Error('auth: server authentication failed');
|
|
197
|
+
return session;
|
|
181
198
|
}
|
|
182
|
-
export const Auth = { register, login,
|
|
199
|
+
export const Auth = { register, login, buildLoginMessage, LOGIN_CONTEXT };
|
package/build/client/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { mount } from './routing/mount.js';
|
|
2
2
|
export { Router } from './routing/Router.js';
|
|
3
|
-
export { Auth, register as authRegister, login as authLogin,
|
|
4
|
-
export type { KdfParams,
|
|
3
|
+
export { Auth, register as authRegister, login as authLogin, buildLoginMessage, LOGIN_CONTEXT } from './auth.js';
|
|
4
|
+
export type { KdfParams, AuthOptions } from './auth.js';
|
|
5
5
|
export { Link } from './navigation/Link.js';
|
|
6
6
|
export type { LinkProps } from './navigation/Link.js';
|
|
7
7
|
export { NavLink, matchActive } from './navigation/NavLink.js';
|
package/build/client/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { mount } from './routing/mount.js';
|
|
2
2
|
export { Router } from './routing/Router.js';
|
|
3
|
-
export { Auth, register as authRegister, login as authLogin,
|
|
3
|
+
export { Auth, register as authRegister, login as authLogin, buildLoginMessage, LOGIN_CONTEXT } from './auth.js';
|
|
4
4
|
export { Link } from './navigation/Link.js';
|
|
5
5
|
export { NavLink, matchActive } from './navigation/NavLink.js';
|
|
6
6
|
export { navigate, back, forward, refresh, setViewTransitions, setTransitions, href, } from './navigation/navigation.js';
|