Skip to content

Commit 7d29830

Browse files
bitgoAaronhrishikeshjain
authored andcommitted
feat: define sjcl type definitions
fix sjcl dependencies to use @bitgo/sjcl fork define types for this package set package overrides to this version, and ignore upgrade flatted resolution Ticket: WP-8258
1 parent ed6e69b commit 7d29830

10 files changed

Lines changed: 297 additions & 23 deletions

File tree

.iyarc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,13 @@ GHSA-qffp-2rhf-9h96
5656
# - We only use tar for packing; low risk in terms of exploitability
5757
# - Security exception approved
5858
GHSA-9ppj-qmqm-q256
59+
60+
# Excluded because:
61+
# - CVE-2026-4258: missing point-on-curve validation in sjcl.ecc.basicKey.publicKey()
62+
# - Transitive dependency via @bitgo/abstract-lightning > macaroon > sjcl
63+
# - The vulnerability is in sjcl.ecc (ECDH invalid-curve attack); macaroon only uses
64+
# sjcl.codec, sjcl.bitArray, sjcl.misc.hmac, and sjcl.hash.sha256 — no ECC operations
65+
# - Additionally, @bitgo/sjcl (our fork) does not include sjcl.ecc at all
66+
# - Resolved sjcl -> npm:@bitgo/sjcl@1.0.1 in root resolutions; sjcl.ecc is absent at runtime
67+
# - No patched version of sjcl exists upstream (first_patched_version: null)
68+
GHSA-2w8x-224x-785m

Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ COPY --from=builder /tmp/bitgo/modules/abstract-lightning /var/modules/abstract-
4545
COPY --from=builder /tmp/bitgo/modules/sdk-core /var/modules/sdk-core/
4646
COPY --from=builder /tmp/bitgo/modules/sdk-lib-mpc /var/modules/sdk-lib-mpc/
4747
COPY --from=builder /tmp/bitgo/modules/sdk-opensslbytes /var/modules/sdk-opensslbytes/
48-
COPY --from=builder /tmp/bitgo/modules/secp256k1 /var/modules/secp256k1/
4948
COPY --from=builder /tmp/bitgo/modules/sjcl /var/modules/sjcl/
49+
COPY --from=builder /tmp/bitgo/modules/secp256k1 /var/modules/secp256k1/
5050
COPY --from=builder /tmp/bitgo/modules/statics /var/modules/statics/
5151
COPY --from=builder /tmp/bitgo/modules/utxo-lib /var/modules/utxo-lib/
5252
COPY --from=builder /tmp/bitgo/modules/blake2b /var/modules/blake2b/
@@ -145,8 +145,8 @@ RUN cd /var/modules/abstract-lightning && yarn link && \
145145
cd /var/modules/sdk-core && yarn link && \
146146
cd /var/modules/sdk-lib-mpc && yarn link && \
147147
cd /var/modules/sdk-opensslbytes && yarn link && \
148-
cd /var/modules/secp256k1 && yarn link && \
149148
cd /var/modules/sjcl && yarn link && \
149+
cd /var/modules/secp256k1 && yarn link && \
150150
cd /var/modules/statics && yarn link && \
151151
cd /var/modules/utxo-lib && yarn link && \
152152
cd /var/modules/blake2b && yarn link && \
@@ -248,8 +248,8 @@ RUN cd /var/bitgo-express && \
248248
yarn link @bitgo/sdk-core && \
249249
yarn link @bitgo/sdk-lib-mpc && \
250250
yarn link @bitgo/sdk-opensslbytes && \
251-
yarn link @bitgo/secp256k1 && \
252251
yarn link @bitgo/sjcl && \
252+
yarn link @bitgo/secp256k1 && \
253253
yarn link @bitgo/statics && \
254254
yarn link @bitgo/utxo-lib && \
255255
yarn link @bitgo/blake2b && \

modules/sdk-lib-mpc/package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,11 @@
5353
},
5454
"devDependencies": {
5555
"@bitgo/sdk-opensslbytes": "^2.1.0",
56+
"@bitgo/sjcl": "^1.0.1",
5657
"@silencelaboratories/dkls-wasm-ll-bundler": "1.2.0-pre.4",
5758
"@types/lodash": "^4.14.151",
5859
"@types/node": "^24.10.9",
59-
"@types/sjcl": "1.0.34",
60-
"nyc": "^15.0.0",
61-
"sjcl": "1.0.8"
60+
"nyc": "^15.0.0"
6261
},
6362
"peerDependencies": {
6463
"@silencelaboratories/dkls-wasm-ll-bundler": "1.2.0-pre.4"

modules/sdk-lib-mpc/test/unit/tss/ecdsa/dklsDsg.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import assert from 'assert';
77
import { Keyshare } from '@silencelaboratories/dkls-wasm-ll-node';
88
import { decode } from 'cbor-x';
99
import * as mpcv2KeyCardData from './fixtures/mpcv2keycarddata';
10-
import * as sjcl from 'sjcl';
10+
import * as sjcl from '@bitgo/sjcl';
1111
import {
1212
DeserializedBroadcastMessage,
1313
DeserializedDklsSignature,

modules/sjcl/index.d.ts

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
export = sjcl;
2+
export as namespace sjcl;
3+
4+
declare namespace sjcl {
5+
export var bitArray: BitArrayStatic;
6+
export var codec: SjclCodecs;
7+
export var hash: SjclHashes;
8+
export var exception: SjclExceptions;
9+
export var cipher: SjclCiphers;
10+
export var mode: SjclModes;
11+
export var misc: SjclMisc;
12+
export var random: SjclRandom;
13+
export var prng: SjclRandomStatic;
14+
export var keyexchange: Record<string, unknown>;
15+
export var json: SjclJson;
16+
export var encrypt: SjclConvenienceEncryptor;
17+
export var decrypt: SjclConvenienceDecryptor;
18+
19+
// ________________________________________________________________________
20+
21+
interface BitArray extends Array<number> {}
22+
23+
interface BitArrayStatic {
24+
/** Array slices in units of bits. */
25+
bitSlice(a: BitArray, bstart: number, bend: number): BitArray;
26+
27+
/** Extract a number packed into a bit array. */
28+
extract(a: BitArray, bstart: number, blength: number): number;
29+
30+
/** Concatenate two bit arrays. */
31+
concat(a1: BitArray, a2: BitArray): BitArray;
32+
33+
/** Find the length of an array of bits. */
34+
bitLength(a: BitArray): number;
35+
36+
/** Truncate an array. */
37+
clamp(a: BitArray, len: number): BitArray;
38+
39+
/** Make a partial word for a bit array. */
40+
partial(len: number, x: number, _end?: number): number;
41+
42+
/** Get the number of bits used by a partial word. */
43+
getPartial(x: number): number;
44+
45+
/** Compare two arrays for equality in a predictable amount of time. */
46+
equal(a: BitArray, b: BitArray): boolean;
47+
}
48+
49+
// ________________________________________________________________________
50+
51+
interface SjclCodec<T> {
52+
fromBits(bits: BitArray): T;
53+
toBits(value: T): BitArray;
54+
}
55+
56+
interface SjclCodecs {
57+
utf8String: SjclCodec<string>;
58+
hex: SjclCodec<string>;
59+
bytes: SjclCodec<number[]>;
60+
base64: SjclCodec<string>;
61+
base64url: SjclCodec<string>;
62+
}
63+
64+
// ________________________________________________________________________
65+
66+
interface SjclHash {
67+
blockSize: number;
68+
reset(): SjclHash;
69+
update(data: BitArray | string): SjclHash;
70+
finalize(): BitArray;
71+
}
72+
73+
interface SjclHashStatic {
74+
new (hash?: SjclHash): SjclHash;
75+
hash(data: BitArray | string): BitArray;
76+
}
77+
78+
interface SjclHashes {
79+
sha1: SjclHashStatic;
80+
sha256: SjclHashStatic;
81+
sha512: SjclHashStatic;
82+
}
83+
84+
// ________________________________________________________________________
85+
86+
interface SjclExceptions {
87+
corrupt: SjclExceptionFactory;
88+
invalid: SjclExceptionFactory;
89+
bug: SjclExceptionFactory;
90+
notReady: SjclExceptionFactory;
91+
}
92+
93+
interface SjclExceptionFactory {
94+
new (message: string): Error;
95+
}
96+
97+
// ________________________________________________________________________
98+
99+
interface SjclCiphers {
100+
aes: SjclCipherStatic;
101+
}
102+
103+
interface SjclCipher {
104+
encrypt(data: number[]): number[];
105+
decrypt(data: number[]): number[];
106+
}
107+
108+
interface SjclCipherStatic {
109+
new (key: number[]): SjclCipher;
110+
}
111+
112+
// ________________________________________________________________________
113+
114+
interface SjclModes {
115+
gcm: SjclGCMMode;
116+
ccm: SjclCCMMode;
117+
ocb2: SjclOCB2Mode;
118+
}
119+
120+
interface SjclGCMMode {
121+
encrypt(prf: SjclCipher, plaintext: BitArray, iv: BitArray, adata?: BitArray, tlen?: number): BitArray;
122+
decrypt(prf: SjclCipher, ciphertext: BitArray, iv: BitArray, adata?: BitArray, tlen?: number): BitArray;
123+
}
124+
125+
interface SjclCCMMode {
126+
encrypt(prf: SjclCipher, plaintext: BitArray, iv: BitArray, adata?: BitArray, tlen?: number): BitArray;
127+
decrypt(prf: SjclCipher, ciphertext: BitArray, iv: BitArray, adata?: BitArray, tlen?: number): BitArray;
128+
}
129+
130+
interface SjclOCB2Mode {
131+
encrypt(
132+
prf: SjclCipher,
133+
plaintext: BitArray,
134+
iv: BitArray,
135+
adata?: BitArray,
136+
tlen?: number,
137+
premac?: boolean
138+
): BitArray;
139+
decrypt(
140+
prf: SjclCipher,
141+
ciphertext: BitArray,
142+
iv: BitArray,
143+
adata?: BitArray,
144+
tlen?: number,
145+
premac?: boolean
146+
): BitArray;
147+
pmac(prf: SjclCipher, adata: BitArray): number[];
148+
}
149+
150+
// ________________________________________________________________________
151+
152+
interface PBKDF2Params {
153+
iter?: number | undefined;
154+
salt?: BitArray | undefined;
155+
}
156+
157+
interface SjclMisc {
158+
pbkdf2(
159+
password: BitArray | string,
160+
salt: BitArray | string,
161+
count?: number,
162+
length?: number,
163+
Prff?: SjclPRFFamilyStatic
164+
): BitArray;
165+
hmac: SjclHMACStatic;
166+
cachedPbkdf2(
167+
password: string,
168+
obj?: PBKDF2Params
169+
): {
170+
key: BitArray;
171+
salt: BitArray;
172+
};
173+
}
174+
175+
class SjclPRFFamily {
176+
encrypt(data: BitArray | string): BitArray;
177+
}
178+
179+
interface SjclHMAC extends SjclPRFFamily {
180+
mac(data: BitArray | string): BitArray;
181+
reset(): void;
182+
update(data: BitArray | string): void;
183+
digest(): BitArray;
184+
}
185+
186+
interface SjclPRFFamilyStatic {
187+
new (key: BitArray): SjclPRFFamily;
188+
}
189+
190+
interface SjclHMACStatic {
191+
new (key: BitArray, Hash?: SjclHashStatic): SjclHMAC;
192+
}
193+
194+
// ________________________________________________________________________
195+
196+
interface SjclRandom {
197+
randomWords(nwords: number, paranoia?: number): BitArray;
198+
setDefaultParanoia(paranoia: number, allowZeroParanoia: string): void;
199+
addEntropy(data: number | number[] | string, estimatedEntropy: number, source: string): void;
200+
isReady(paranoia?: number): number;
201+
getProgress(paranoia?: number): number;
202+
startCollectors(): void;
203+
stopCollectors(): void;
204+
addEventListener(name: string, cb: () => void): void;
205+
removeEventListener(name: string, cb: () => void): void;
206+
}
207+
208+
interface SjclRandomStatic {
209+
new (defaultParanoia: number): SjclRandom;
210+
}
211+
212+
// ________________________________________________________________________
213+
214+
interface SjclCipherParams {
215+
v?: number | undefined;
216+
iter?: number | undefined;
217+
ks?: number | undefined;
218+
ts?: number | undefined;
219+
mode?: string | undefined;
220+
adata?: string | undefined;
221+
cipher?: string | undefined;
222+
}
223+
224+
interface SjclCipherEncryptParams extends SjclCipherParams {
225+
salt: BitArray;
226+
iv: BitArray;
227+
}
228+
229+
interface SjclCipherDecryptParams extends SjclCipherParams {
230+
salt?: BitArray | undefined;
231+
iv?: BitArray | undefined;
232+
}
233+
234+
interface SjclCipherEncrypted extends SjclCipherEncryptParams {
235+
kemtag?: BitArray | undefined;
236+
ct: BitArray;
237+
}
238+
239+
interface SjclCipherDecrypted extends SjclCipherEncrypted {
240+
key: BitArray;
241+
}
242+
243+
interface SjclConvenienceEncryptor {
244+
(
245+
password: BitArray | string | undefined,
246+
plaintext: string | undefined,
247+
params?: SjclCipherEncryptParams,
248+
rp?: SjclCipherEncrypted
249+
): string;
250+
}
251+
252+
interface SjclConvenienceDecryptor {
253+
(
254+
password: BitArray | string | undefined,
255+
ciphertext: SjclCipherEncrypted | string | undefined,
256+
params?: SjclCipherDecryptParams,
257+
rp?: SjclCipherDecrypted
258+
): string;
259+
}
260+
261+
interface SjclJson {
262+
defaults: Required<SjclCipherParams>;
263+
encrypt: SjclConvenienceEncryptor;
264+
decrypt: SjclConvenienceDecryptor;
265+
encode(obj: object): string;
266+
decode(obj: string): object;
267+
}
268+
}

modules/sjcl/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "1.0.1",
44
"description": "fork of Stanford Javascript Crypto Library",
55
"main": "sjcl.min.js",
6+
"types": "index.d.ts",
67
"author": "BitGo SDK Team <sdkteam@bitgo.com>",
78
"license": "(BSD-2-Clause OR GPL-2.0-only)",
89
"repository": {

modules/web-demo/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,14 @@
6363
"@bitgo/sdk-core": "^36.35.0",
6464
"@bitgo/sdk-lib-mpc": "^10.9.0",
6565
"@bitgo/sdk-opensslbytes": "^2.1.0",
66+
"@bitgo/sjcl": "^1.0.1",
6667
"@bitgo/statics": "^58.31.0",
6768
"bitgo": "^50.28.0",
6869
"lodash": "^4.17.15",
6970
"react": "^18.0.0",
7071
"react-dom": "^18.0.0",
7172
"react-json-view": "^1.21.3",
7273
"react-router-dom": "6.3.0",
73-
"sjcl": "1.0.8",
7474
"styled-components": "^5.3.5"
7575
},
7676
"devDependencies": {

modules/web-demo/src/components/KeyCard/fixtures.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { generateKeycard } from '@bitgo/key-card';
22
import { KeyCurve, coins } from '@bitgo/statics';
33
import { Keychain } from '@bitgo/sdk-core';
44
import { DklsDkg, DklsTypes } from '@bitgo/sdk-lib-mpc';
5-
import * as sjcl from 'sjcl';
5+
import * as sjcl from '@bitgo/sjcl';
66

77
function downloadKeycardImage(coinFamily: string): Promise<HTMLImageElement> {
88
return new Promise<HTMLImageElement>((resolve, reject) => {

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@
121121
"**/tronweb/**/validator": "13.15.23",
122122
"@isaacs/brace-expansion": "5.0.1",
123123
"basic-ftp": ">=5.2.0",
124-
"flatted": "3.4.0"
124+
"flatted": "3.4.2",
125+
"sjcl": "npm:@bitgo/sjcl@1.0.1"
125126
},
126127
"workspaces": [
127128
"modules/*"

0 commit comments

Comments
 (0)