Skip to content

Commit 1e9499f

Browse files
committed
feat: srp internal functions
1 parent 41a9fbd commit 1e9499f

File tree

8 files changed

+397
-2
lines changed

8 files changed

+397
-2
lines changed

bun.lockb

11.8 KB
Binary file not shown.

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@
3030
"bugs": "https://github.com/wobsoriano/pkg-name/issues",
3131
"author": "Robert Soriano <[email protected]>",
3232
"devDependencies": {
33-
"bun-plugin-dts": "^0.3.0",
34-
"@types/bun": "^1.1.10"
33+
"@types/bun": "^1.1.10",
34+
"@types/jsbn": "^1.2.33",
35+
"bun-plugin-dts": "^0.3.0"
36+
},
37+
"dependencies": {
38+
"assert": "^2.1.0",
39+
"jsbn": "^1.1.0"
3540
}
3641
}

src/srp/littleK.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { createHash } from "node:crypto";
2+
import { BigInteger } from "jsbn";
3+
import { hexToBigInt } from "../utils/hex";
4+
import { knownGroups } from "./srpGroup";
5+
import { bigIntToBytes } from "../utils/bigint";
6+
7+
const makeLittleK = (N: BigInteger, g: BigInteger) => {
8+
const hash = createHash("sha256");
9+
const nBytes = bigIntToBytes(N);
10+
const gBytes = bigIntToBytes(g);
11+
console.log(Buffer.from(gBytes).toString("base64"));
12+
hash.update(nBytes);
13+
hash.update(gBytes);
14+
return hexToBigInt(hash.digest("hex"));
15+
};
16+
17+
const k = makeLittleK(
18+
knownGroups[8192].getN(),
19+
knownGroups[8192].getGenerator()
20+
);
21+
console.log(k.toString());

src/srp/srpClient.ts

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { bigIntToBytes, maxInt } from "../utils/bigint";
2+
import { hexToBigInt } from "../utils/hex";
3+
import { minExponentSize, SrpGroup } from "./srpGroup";
4+
import { createHash, randomBytes } from "node:crypto";
5+
import { BigInteger } from "jsbn";
6+
7+
const zero = new BigInteger("0");
8+
9+
export class SrpClient {
10+
private ephemeralPrivate: BigInteger = zero;
11+
private ephemeralPublicA: BigInteger = zero;
12+
private ephemeralPublicB: BigInteger = zero;
13+
private x: BigInteger = zero;
14+
private v: BigInteger = zero;
15+
private u: BigInteger | null = zero;
16+
private k: BigInteger = zero;
17+
private premasterKey: BigInteger;
18+
private key: Uint8Array | null = null;
19+
private m: Uint8Array | null = null;
20+
private cProof: Uint8Array | null = null;
21+
private isServerProved: boolean = false;
22+
private group: SrpGroup;
23+
private badState = false;
24+
25+
constructor(group: SrpGroup, v: BigInteger, k?: BigInteger) {
26+
this.group = group;
27+
28+
if (k) {
29+
this.k = k;
30+
} else {
31+
}
32+
}
33+
34+
private makeLittleK(): BigInteger {
35+
const hash = createHash("sha256");
36+
hash.update(new Uint8Array(this.group.getN().toByteArray()));
37+
hash.update(new Uint8Array(this.group.getGenerator().toByteArray()));
38+
return hexToBigInt(hash.digest("hex"));
39+
}
40+
41+
private generateMySecret(): BigInteger {
42+
const eSize = maxInt(this.group.exponentSize, minExponentSize);
43+
// get eSize random bytes
44+
const bytes = randomBytes(eSize);
45+
this.ephemeralPrivate = hexToBigInt(bytes.toString("hex"));
46+
return this.ephemeralPrivate;
47+
}
48+
49+
private makeA(): BigInteger {
50+
if (this.ephemeralPrivate === zero) {
51+
this.generateMySecret();
52+
}
53+
this.ephemeralPublicA = this.group
54+
.getGenerator()
55+
.modPow(this.ephemeralPrivate, this.group.getN());
56+
return this.ephemeralPublicA;
57+
}
58+
59+
private isUValid(): boolean {
60+
if (this.u === null || this.badState) {
61+
this.u = null;
62+
return false;
63+
}
64+
if (this.u.compareTo(zero) === 0) {
65+
return false;
66+
}
67+
return true;
68+
}
69+
70+
private makeVerifier(): BigInteger {
71+
if (this.badState) {
72+
throw new Error("we have bad data");
73+
}
74+
if (this.x.equals(zero)) {
75+
throw new Error("x must be known to calculate v");
76+
}
77+
this.v = this.group.getGenerator().modPow(this.x, this.group.getN());
78+
return this.v;
79+
}
80+
81+
public isPublicValid(AorB: BigInteger): boolean {
82+
if (this.group.getGenerator().compareTo(zero) === 0) {
83+
return false;
84+
}
85+
86+
if (AorB.mod(this.group.getGenerator()).compareTo(zero) === 0) {
87+
return false;
88+
}
89+
const bigOne = new BigInteger("1");
90+
if (AorB.gcd(this.group.getN()).compareTo(bigOne) !== 0) {
91+
return false;
92+
}
93+
94+
return true;
95+
}
96+
private calculateU(): BigInteger {
97+
if (
98+
!this.isPublicValid(this.ephemeralPublicB) ||
99+
!this.isPublicValid(this.ephemeralPublicA)
100+
) {
101+
this.u = null;
102+
throw new Error("both A and B must be known to calculate u");
103+
}
104+
105+
const hash = createHash("sha256");
106+
hash.update(
107+
new TextEncoder().encode(
108+
this.ephemeralPublicA.toString() + this.ephemeralPublicB.toString()
109+
)
110+
);
111+
112+
this.u = new BigInteger(hash.digest().toString("hex"), 16);
113+
if (this.u.compareTo(zero) === 0) {
114+
throw new Error("u == 0, which is a bad thing");
115+
}
116+
return this.u;
117+
}
118+
}

src/srp/srpGroup.ts

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import { hexToBigInt } from "../utils/hex";
2+
import { BigInteger } from "jsbn";
3+
export const minExponentSize = 32;
4+
5+
export class SrpGroup {
6+
private g: BigInteger;
7+
private n: BigInteger;
8+
public label: string;
9+
public exponentSize: number;
10+
11+
public constructor(
12+
g: BigInteger,
13+
n: BigInteger,
14+
label: string,
15+
exponentSize: number
16+
) {
17+
this.g = g;
18+
this.n = n;
19+
this.label = label;
20+
this.exponentSize = exponentSize;
21+
}
22+
23+
public getN(): BigInteger {
24+
return this.n;
25+
}
26+
27+
public getGenerator(): BigInteger {
28+
return this.g;
29+
}
30+
}
31+
32+
const g2048n =
33+
"0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC319294" +
34+
"3DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310D" +
35+
"CD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FB" +
36+
"D5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF74" +
37+
"7359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A" +
38+
"436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D" +
39+
"5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E73" +
40+
"03CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB6" +
41+
"94B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F" +
42+
"9E4AFF73";
43+
44+
const g3072n =
45+
"0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" +
46+
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" +
47+
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" +
48+
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" +
49+
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" +
50+
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" +
51+
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
52+
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" +
53+
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" +
54+
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" +
55+
"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" +
56+
"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" +
57+
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" +
58+
"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" +
59+
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" +
60+
"43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
61+
62+
const g4096n =
63+
"0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" +
64+
"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" +
65+
"302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" +
66+
"A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" +
67+
"49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" +
68+
"FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
69+
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" +
70+
"180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" +
71+
"3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" +
72+
"04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" +
73+
"B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" +
74+
"1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" +
75+
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" +
76+
"E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" +
77+
"99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" +
78+
"04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" +
79+
"233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" +
80+
"D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" +
81+
"FFFFFFFFFFFFFFFF";
82+
83+
const g6144n =
84+
"0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" +
85+
"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" +
86+
"302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" +
87+
"A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" +
88+
"49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" +
89+
"FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
90+
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" +
91+
"180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" +
92+
"3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" +
93+
"04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" +
94+
"B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" +
95+
"1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" +
96+
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" +
97+
"E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" +
98+
"99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" +
99+
"04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" +
100+
"233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" +
101+
"D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" +
102+
"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" +
103+
"AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" +
104+
"DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" +
105+
"2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" +
106+
"F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" +
107+
"BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" +
108+
"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" +
109+
"B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" +
110+
"387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" +
111+
"6DCC4024FFFFFFFFFFFFFFFF";
112+
113+
const g8192n =
114+
"0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" +
115+
"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" +
116+
"302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" +
117+
"A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" +
118+
"49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" +
119+
"FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
120+
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" +
121+
"180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" +
122+
"3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" +
123+
"04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" +
124+
"B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" +
125+
"1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" +
126+
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" +
127+
"E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" +
128+
"99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" +
129+
"04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" +
130+
"233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" +
131+
"D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" +
132+
"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" +
133+
"AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" +
134+
"DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" +
135+
"2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" +
136+
"F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" +
137+
"BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" +
138+
"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" +
139+
"B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" +
140+
"387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" +
141+
"6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA" +
142+
"3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C" +
143+
"5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" +
144+
"22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886" +
145+
"2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6" +
146+
"6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5" +
147+
"0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268" +
148+
"359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6" +
149+
"FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" +
150+
"60C980DD98EDD3DFFFFFFFFFFFFFFFFF";
151+
152+
export const knownGroups = {
153+
2048: new SrpGroup(new BigInteger("2"), hexToBigInt(g2048n), "5054A2048", 27),
154+
3072: new SrpGroup(new BigInteger("5"), hexToBigInt(g3072n), "5054A3072", 32),
155+
4096: new SrpGroup(new BigInteger("5"), hexToBigInt(g4096n), "5054A4096", 38),
156+
6144: new SrpGroup(new BigInteger("5"), hexToBigInt(g6144n), "5054A6144", 43),
157+
8192: new SrpGroup(
158+
new BigInteger("19"),
159+
hexToBigInt(g8192n),
160+
"5054A8192",
161+
48
162+
),
163+
};

src/utils/bigint.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import assert from "assert";
2+
import { bigIntToBytes } from "./bigint";
3+
4+
// Test cases
5+
function runTests() {
6+
// Test zero
7+
assert.deepStrictEqual(
8+
Array.from(bigIntToBytes(0n)),
9+
[],
10+
"Zero should return empty array"
11+
);
12+
13+
// Test single byte values
14+
assert.deepStrictEqual(
15+
Array.from(bigIntToBytes(255n)),
16+
[255],
17+
"255 should be [255]"
18+
);
19+
20+
// Test two byte values
21+
assert.deepStrictEqual(
22+
Array.from(bigIntToBytes(256n)),
23+
[1, 0],
24+
"256 should be [1, 0]"
25+
);
26+
27+
// Test negative values
28+
assert.deepStrictEqual(
29+
Array.from(bigIntToBytes(-255n)),
30+
[255],
31+
"Negative values should return same as positive"
32+
);
33+
34+
// Test larger numbers
35+
assert.deepStrictEqual(
36+
Array.from(bigIntToBytes(65535n)),
37+
[255, 255],
38+
"65535 should be [255, 255]"
39+
);
40+
41+
// Test very large number
42+
assert.deepStrictEqual(
43+
Array.from(bigIntToBytes(0x123456789an)),
44+
[18, 52, 86, 120, 154],
45+
"0x123456789a should be [18, 52, 86, 120, 154]"
46+
);
47+
48+
console.log("All tests passed!");
49+
}
50+
51+
try {
52+
runTests();
53+
} catch (error) {
54+
console.error("Test failed:", (error as Error).message);
55+
}

0 commit comments

Comments
 (0)