Skip to content

Commit 1ee1a5a

Browse files
committed
fix: data transformation nuance
1 parent 6a2238e commit 1ee1a5a

File tree

2 files changed

+48
-49
lines changed

2 files changed

+48
-49
lines changed

src/srp/srpClient.ts

+24-49
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { bigIntToBytes, maxInt } from "../utils/bigint";
1+
import {
2+
bigIntToBytes,
3+
maxInt,
4+
serverStyleHexFromBigInt,
5+
setBigIntegerFromBytes,
6+
} from "../utils/bigint";
27
import { hexToBigInt } from "../utils/hex";
38
import { minExponentSize, SrpGroup } from "./srpGroup";
49
import { createHash, randomBytes } from "node:crypto";
@@ -42,8 +47,8 @@ export class SrpClient {
4247
if (!this.group) {
4348
throw new Error("group is not set");
4449
}
45-
hash.update(new Uint8Array(this.group.getN().toByteArray()));
46-
hash.update(new Uint8Array(this.group.getGenerator().toByteArray()));
50+
hash.update(bigIntToBytes(this.group.getN()));
51+
hash.update(bigIntToBytes(this.group.getGenerator()));
4752
return hexToBigInt(hash.digest("hex"));
4853
}
4954

@@ -59,7 +64,7 @@ export class SrpClient {
5964
}
6065

6166
private makeA(): BigInteger {
62-
if (this.ephemeralPrivate === zero) {
67+
if (this.ephemeralPrivate.compareTo(zero) === 0) {
6368
this.generateMySecret();
6469
}
6570
if (!this.group) {
@@ -123,17 +128,21 @@ export class SrpClient {
123128
throw new Error("both A and B must be known to calculate u");
124129
}
125130

131+
const trimmedHexPublicA = serverStyleHexFromBigInt(this.ephemeralPublicA);
132+
const trimmedHexPublicB = serverStyleHexFromBigInt(this.ephemeralPublicB);
133+
126134
const hash = createHash("sha256");
127135
hash.update(
128-
new TextEncoder().encode(
129-
this.ephemeralPublicA.toString() + this.ephemeralPublicB.toString()
130-
)
136+
new TextEncoder().encode(trimmedHexPublicA + trimmedHexPublicB)
131137
);
132138

133-
this.u = new BigInteger(hash.digest().toString("hex"), 16);
139+
const hashed = hash.digest();
140+
141+
this.u = setBigIntegerFromBytes(new Uint8Array(hashed));
134142
if (this.u.compareTo(zero) === 0) {
135143
throw new Error("u == 0, which is a bad thing");
136144
}
145+
137146
return this.u;
138147
}
139148

@@ -191,8 +200,8 @@ need that.
191200
throw new Error("cannot make Key with my ephemeral secret");
192201
}
193202

194-
let b = new BigInteger("0");
195-
let e = new BigInteger("0");
203+
let b: BigInteger;
204+
let e: BigInteger;
196205

197206
if (
198207
this.ephemeralPublicB.compareTo(zero) === 0 ||
@@ -201,19 +210,20 @@ need that.
201210
) {
202211
throw new Error("not enough is known to create Key");
203212
}
213+
204214
e = this.u.multiply(this.x);
205215
e = e.add(this.ephemeralPrivate);
206216

207217
if (!this.group) {
208218
throw new Error("group is not set");
209219
}
210220

211-
b = this.group.getGenerator().modPow(this.x, this.group.getN());
221+
b = this.group.getGenerator().modPow(this.x, this.group.getN().abs());
212222
b = b.multiply(this.k);
223+
213224
b = this.ephemeralPublicB.subtract(b);
214225
b = b.mod(this.group.getN());
215-
216-
this.premasterKey = b.modPow(e, this.group.getN());
226+
this.premasterKey = b.modPow(e, this.group.getN().abs());
217227

218228
const hash = createHash("sha256");
219229
hash.update(new TextEncoder().encode(this.premasterKey.toString(16)));
@@ -238,7 +248,6 @@ slice (without padding to size of N)
238248
throw new Error("group is not set");
239249
}
240250
const nLen = bigIntToBytes(this.group.getN()).length;
241-
console.log(`Server padding length: ${nLen}`);
242251

243252
if (this.m !== null) {
244253
return this.m;
@@ -257,8 +266,7 @@ slice (without padding to size of N)
257266
.update(bigIntToBytes(this.group.getGenerator()))
258267
.digest();
259268
const gHash = new Uint8Array(gHashBuffer);
260-
console.log(`nHash: ${nHashBuffer.toString("hex")}`);
261-
console.log(`gHash: ${gHashBuffer.toString("hex")}`);
269+
262270
let groupXOR = new Uint8Array(SHA256_SIZE);
263271
const length = safeXORBytes(groupXOR, nHash, gHash);
264272
if (length !== SHA256_SIZE) {
@@ -268,43 +276,11 @@ slice (without padding to size of N)
268276
}
269277
const groupHashBuffer = createHash("sha256").update(groupXOR).digest();
270278
const groupHash = new Uint8Array(groupHashBuffer);
271-
console.log(`groupHash: ${groupHashBuffer.toString("hex")}`);
272279

273280
const uHashBuffer = createHash("sha256")
274281
.update(new TextEncoder().encode(uname))
275282
.digest();
276283
const uHash = new Uint8Array(uHashBuffer);
277-
console.log(`uHash: ${uHashBuffer.toString("hex")}`);
278-
279-
let m1 = createHash("sha256");
280-
m1.update(groupHash);
281-
console.log("After groupHash:", m1.digest("hex"));
282-
283-
let m2 = createHash("sha256");
284-
m2.update(groupHash);
285-
m2.update(uHash);
286-
console.log("After uHash:", m2.digest("hex"));
287-
288-
let m3 = createHash("sha256");
289-
m3.update(groupHash);
290-
m3.update(uHash);
291-
m3.update(salt);
292-
console.log("After salt:", m3.digest("hex"));
293-
294-
let m4 = createHash("sha256");
295-
m4.update(groupHash);
296-
m4.update(uHash);
297-
m4.update(salt);
298-
m4.update(bigIntToBytes(this.ephemeralPublicA));
299-
console.log("After ephemeralPublicA:", m4.digest("hex"));
300-
301-
let m5 = createHash("sha256");
302-
m5.update(groupHash);
303-
m5.update(uHash);
304-
m5.update(salt);
305-
m5.update(bigIntToBytes(this.ephemeralPublicA));
306-
m5.update(bigIntToBytes(this.ephemeralPublicB));
307-
console.log("After ephemeralPublicB:", m5.digest("hex"));
308284

309285
let m6 = createHash("sha256");
310286
m6.update(groupHash);
@@ -313,7 +289,6 @@ slice (without padding to size of N)
313289
m6.update(bigIntToBytes(this.ephemeralPublicA));
314290
m6.update(bigIntToBytes(this.ephemeralPublicB));
315291
m6.update(this.key);
316-
console.log("After key:", m6.digest("hex"));
317292

318293
this.m = new Uint8Array(m6.digest());
319294
return this.m;

src/utils/bigint.ts

+24
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,27 @@ export const maxInt = (n1: number, ...nums: number[]): number => {
2828
export const uint8ArrayToBigInt = (arr: Uint8Array): BigInteger => {
2929
return new BigInteger([...arr]);
3030
};
31+
32+
export function serverStyleHexFromBigInt(bn: BigInteger): string {
33+
// Convert BigInteger to byte array
34+
const bytes = bn.toByteArray();
35+
36+
// Convert bytes to hex string and ensure lowercase
37+
const hexString = bytes
38+
.map((b) => (b & 0xff).toString(16).padStart(2, "0"))
39+
.join("")
40+
.toLowerCase();
41+
42+
// Remove leading zeros
43+
return hexString.replace(/^0+/, "") || "0";
44+
}
45+
46+
export function setBigIntegerFromBytes(buf: Uint8Array): BigInteger {
47+
// Convert bytes to hex string (big-endian)
48+
const hexString = Array.from(buf)
49+
.map((b) => b.toString(16).padStart(2, "0"))
50+
.join("");
51+
52+
// Create BigInteger from hex string
53+
return new BigInteger(hexString, 16);
54+
}

0 commit comments

Comments
 (0)