@bradford-tech/supabase-integrity-attest 0.2.1 → 0.2.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"certificate.d.ts","sourceRoot":"","sources":["../../src/src/certificate.ts"],"names":[],"mappings":"AAsBA;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,UAAU,EAAE,EACjB,SAAS,CAAC,EAAE,IAAI,GACf,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,UAAU,GAAG,UAAU,CAqCpE;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,UAAU,CAAC,CAkBrB"}
1
+ {"version":3,"file":"certificate.d.ts","sourceRoot":"","sources":["../../src/src/certificate.ts"],"names":[],"mappings":"AA+UA;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,UAAU,EAAE,EACjB,SAAS,CAAC,EAAE,IAAI,GACf,OAAO,CAAC,IAAI,CAAC,CAkEf;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,UAAU,GAAG,UAAU,CA6BpE;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,UAAU,CAAC,CAqBrB"}
@@ -1,19 +1,222 @@
1
1
  // src/certificate.ts
2
- import * as pkijs from "pkijs";
3
2
  import * as asn1js from "asn1js";
3
+ import { p384 } from "@noble/curves/nist.js";
4
+ import { decodeBase64 } from "../deps/jsr.io/@std/encoding/1.0.10/base64.js";
4
5
  import { APPLE_APP_ATTESTATION_ROOT_CA_PEM, APPLE_NONCE_EXTENSION_OID, } from "./constants.js";
6
+ import { derToRaw } from "./der.js";
5
7
  import { AttestationError, AttestationErrorCode } from "./errors.js";
8
+ import { constantTimeEqual } from "./utils.js";
9
+ // ── OID mappings ────────────────────────────────────────────────────
10
+ /** Signature algorithm OID → hash algorithm name */
11
+ const SIG_ALG_HASH = {
12
+ "1.2.840.10045.4.3.2": "SHA-256",
13
+ "1.2.840.10045.4.3.3": "SHA-384",
14
+ };
15
+ /** Curve OID → WebCrypto namedCurve */
16
+ const CURVE_OID_NAME = {
17
+ "1.2.840.10045.3.1.7": "P-256",
18
+ "1.3.132.0.34": "P-384",
19
+ };
20
+ /** Curve name → ECDSA r||s component size in bytes */
21
+ const CURVE_COMPONENT_SIZE = {
22
+ "P-256": 32,
23
+ "P-384": 48,
24
+ };
25
+ // ── Helpers ─────────────────────────────────────────────────────────
6
26
  /**
7
- * Parse the Apple App Attestation Root CA from PEM into a pkijs Certificate.
27
+ * Safely create an ArrayBuffer from a Uint8Array, handling subarrays.
28
+ * If `der` is a subarray, `der.buffer` includes the full underlying buffer,
29
+ * so we must slice to get only the relevant portion.
8
30
  */
9
- function parseRootCa() {
31
+ function safeBuffer(der) {
32
+ return der.buffer.slice(der.byteOffset, der.byteOffset + der.byteLength);
33
+ }
34
+ /**
35
+ * Slice bytes from a valueBeforeDecodeView, producing an independent copy.
36
+ */
37
+ function sliceFromView(view) {
38
+ return new Uint8Array(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
39
+ }
40
+ /**
41
+ * Parse a date from an ASN.1 time element (UTCTime or GeneralizedTime).
42
+ */
43
+ function parseAsn1Date(element) {
44
+ if (element instanceof asn1js.UTCTime) {
45
+ return element.toDate();
46
+ }
47
+ if (element instanceof asn1js.GeneralizedTime) {
48
+ return element.toDate();
49
+ }
50
+ throw new AttestationError(AttestationErrorCode.INVALID_FORMAT, "Unexpected ASN.1 time type in certificate validity");
51
+ }
52
+ // ── parseCertificate ────────────────────────────────────────────────
53
+ /**
54
+ * Parse an X.509 DER-encoded certificate into structured fields.
55
+ * All byte slices come from the original input buffer.
56
+ */
57
+ function parseCertificate(der) {
58
+ const buf = safeBuffer(der);
59
+ const asn1 = asn1js.fromBER(buf);
60
+ if (asn1.offset === -1) {
61
+ throw new AttestationError(AttestationErrorCode.INVALID_FORMAT, "Failed to parse certificate DER");
62
+ }
63
+ const certSeq = asn1.result;
64
+ const certChildren = certSeq.valueBlock.value;
65
+ // certChildren: [tbsCertificate, signatureAlgorithm, signatureValue]
66
+ const tbsElement = certChildren[0];
67
+ const sigAlgElement = certChildren[1];
68
+ const sigValueElement = certChildren[2];
69
+ // TBS bytes — slice from the element's full encoding (tag + length + value)
70
+ const tbsView = tbsElement
71
+ .valueBeforeDecodeView;
72
+ const tbsCertificateDer = sliceFromView(tbsView);
73
+ // Signature algorithm OID
74
+ const sigAlgOid = sigAlgElement.valueBlock.value[0].valueBlock
75
+ .toString();
76
+ // Signature value — unused-bits byte already stripped by asn1js
77
+ const signatureValue = new Uint8Array(sigValueElement.valueBlock.valueHexView);
78
+ // TBS children — check for v3 version tag
79
+ const tbsChildren = tbsElement.valueBlock.value;
80
+ let offset = 0;
81
+ // First child is explicit [0] version tag for v3 certs
82
+ const firstChild = tbsChildren[0];
83
+ if (firstChild.idBlock.tagClass === 3 && // CONTEXT-SPECIFIC
84
+ firstChild.idBlock.tagNumber === 0) {
85
+ offset = 1; // Version tag present, shift all indices
86
+ }
87
+ // TBS layout (with offset): serial, sigAlg, issuer, validity, subject, SPKI, [extensions]
88
+ // [offset+0]=serial, [offset+1]=sigAlg, [offset+2]=issuer, [offset+3]=validity,
89
+ // [offset+4]=subject, [offset+5]=SPKI
90
+ const issuerElement = tbsChildren[offset + 2];
91
+ const issuerView = issuerElement
92
+ .valueBeforeDecodeView;
93
+ const issuer = sliceFromView(issuerView);
94
+ const validityElement = tbsChildren[offset + 3];
95
+ const validityChildren = validityElement.valueBlock.value;
96
+ const validityNotBefore = parseAsn1Date(validityChildren[0]);
97
+ const validityNotAfter = parseAsn1Date(validityChildren[1]);
98
+ const subjectElement = tbsChildren[offset + 4];
99
+ const subjectView = subjectElement
100
+ .valueBeforeDecodeView;
101
+ const subject = sliceFromView(subjectView);
102
+ const spkiElement = tbsChildren[offset + 5];
103
+ const spkiView = spkiElement
104
+ .valueBeforeDecodeView;
105
+ const subjectPublicKeyInfoDer = sliceFromView(spkiView);
106
+ // Extract curve OID from SPKI's AlgorithmIdentifier
107
+ const spkiSeq = spkiElement;
108
+ const spkiAlgId = spkiSeq.valueBlock.value[0];
109
+ const curveOidElement = spkiAlgId.valueBlock
110
+ .value[1];
111
+ const publicKeyCurveOid = curveOidElement.valueBlock.toString();
112
+ // Extensions — found in explicit [3] tag within TBS
113
+ const extensions = [];
114
+ for (let i = offset + 6; i < tbsChildren.length; i++) {
115
+ const child = tbsChildren[i];
116
+ if (child.idBlock.tagClass === 3 && // CONTEXT-SPECIFIC
117
+ child.idBlock.tagNumber === 3) {
118
+ // This is the extensions wrapper: explicit [3] containing a SEQUENCE of extensions
119
+ const extWrapper = child;
120
+ const extSeqOfSeq = extWrapper.valueBlock.value[0];
121
+ for (const extSeq of extSeqOfSeq.valueBlock.value) {
122
+ const extChildren = extSeq.valueBlock.value;
123
+ const oid = extChildren[0].valueBlock
124
+ .toString();
125
+ let critical = false;
126
+ let valueElement;
127
+ if (extChildren[1] instanceof asn1js.Boolean) {
128
+ critical = extChildren[1].getValue();
129
+ valueElement = extChildren[2];
130
+ }
131
+ else {
132
+ valueElement = extChildren[1];
133
+ }
134
+ extensions.push({
135
+ oid,
136
+ critical,
137
+ value: new Uint8Array(valueElement.valueBlock.valueHexView),
138
+ });
139
+ }
140
+ break;
141
+ }
142
+ }
143
+ return {
144
+ tbsCertificateDer,
145
+ signatureAlgorithmOid: sigAlgOid,
146
+ signatureValue,
147
+ issuer,
148
+ subject,
149
+ validityNotBefore,
150
+ validityNotAfter,
151
+ subjectPublicKeyInfoDer,
152
+ publicKeyCurveOid,
153
+ extensions,
154
+ };
155
+ }
156
+ // ── extractRawPublicKeyFromSpki ─────────────────────────────────────
157
+ /**
158
+ * Extract the raw public key bytes from a DER-encoded SubjectPublicKeyInfo.
159
+ * Returns the uncompressed EC point (0x04 || x || y).
160
+ */
161
+ function extractRawPublicKeyFromSpki(spkiDer) {
162
+ const buf = safeBuffer(spkiDer);
163
+ const asn1 = asn1js.fromBER(buf);
164
+ if (asn1.offset === -1) {
165
+ throw new AttestationError(AttestationErrorCode.INVALID_CERTIFICATE_CHAIN, "Failed to parse SPKI DER");
166
+ }
167
+ const spkiSeq = asn1.result;
168
+ const publicKeyBitString = spkiSeq.valueBlock.value[1];
169
+ return new Uint8Array(publicKeyBitString.valueBlock.valueHexView);
170
+ }
171
+ // ── verifySignature ─────────────────────────────────────────────────
172
+ /**
173
+ * Verify the signature of a child certificate against the parent's public key.
174
+ *
175
+ * Deno WebCrypto throws "Not implemented" for cross-paired curve+hash
176
+ * (e.g., P-384 key signing with SHA-256). Apple's intermediate (P-384)
177
+ * signs the leaf cert with SHA-256. Use @noble/curves for this case.
178
+ */
179
+ async function verifySignature(child, parent) {
180
+ const hash = SIG_ALG_HASH[child.signatureAlgorithmOid];
181
+ if (!hash) {
182
+ throw new AttestationError(AttestationErrorCode.INVALID_CERTIFICATE_CHAIN, `Unsupported signature algorithm OID: ${child.signatureAlgorithmOid}`);
183
+ }
184
+ const namedCurve = CURVE_OID_NAME[parent.publicKeyCurveOid];
185
+ if (!namedCurve) {
186
+ throw new AttestationError(AttestationErrorCode.INVALID_CERTIFICATE_CHAIN, `Unsupported curve OID: ${parent.publicKeyCurveOid}`);
187
+ }
188
+ const componentSize = CURVE_COMPONENT_SIZE[namedCurve];
189
+ const sigRaw = derToRaw(child.signatureValue, componentSize);
190
+ // Apple's intermediate (P-384 key) signs the leaf cert with SHA-256.
191
+ // Deno WebCrypto throws "Not implemented" for this cross-pairing.
192
+ // Use @noble/curves p384 to verify instead.
193
+ if (namedCurve === "P-384" && hash === "SHA-256") {
194
+ // Pre-hash TBS, then verify with @noble/curves.
195
+ // lowS: false — X.509 signatures don't enforce BIP-62 low-S normalization.
196
+ // prehash: false — we hash manually since the hash algorithm differs from the curve's default.
197
+ const digest = new Uint8Array(await crypto.subtle.digest(hash, child.tbsCertificateDer));
198
+ const rawPubKey = extractRawPublicKeyFromSpki(parent.subjectPublicKeyInfoDer);
199
+ return p384.verify(sigRaw, digest, rawPubKey, {
200
+ prehash: false,
201
+ lowS: false,
202
+ });
203
+ }
204
+ // Standard pairing — WebCrypto
205
+ const parentKey = await crypto.subtle.importKey("spki", parent.subjectPublicKeyInfoDer, { name: "ECDSA", namedCurve }, false, ["verify"]);
206
+ return crypto.subtle.verify({ name: "ECDSA", hash }, parentKey, sigRaw, child.tbsCertificateDer);
207
+ }
208
+ // ── parseRootCaDer ──────────────────────────────────────────────────
209
+ /**
210
+ * Decode the Apple App Attestation Root CA from PEM to DER bytes.
211
+ */
212
+ function parseRootCaDer() {
10
213
  const b64 = APPLE_APP_ATTESTATION_ROOT_CA_PEM
11
214
  .replace("-----BEGIN CERTIFICATE-----", "")
12
215
  .replace("-----END CERTIFICATE-----", "")
13
216
  .replace(/\s/g, "");
14
- const der = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
15
- return pkijs.Certificate.fromBER(der);
217
+ return decodeBase64(b64);
16
218
  }
219
+ // ── Exported functions ──────────────────────────────────────────────
17
220
  /**
18
221
  * Verify the x5c certificate chain against the Apple App Attestation Root CA.
19
222
  *
@@ -24,17 +227,41 @@ export async function verifyCertificateChain(x5c, checkDate) {
24
227
  if (x5c.length === 0) {
25
228
  throw new AttestationError(AttestationErrorCode.INVALID_CERTIFICATE_CHAIN, "Certificate chain (x5c) is empty");
26
229
  }
27
- const rootCa = parseRootCa();
28
- // Parse all x5c certs
29
- const certs = x5c.map((der) => pkijs.Certificate.fromBER(der));
30
- const chainEngine = new pkijs.CertificateChainValidationEngine({
31
- trustedCerts: [rootCa],
32
- certs: certs,
33
- checkDate: checkDate ?? new Date(),
34
- });
35
- const result = await chainEngine.verify();
36
- if (!result.result) {
37
- throw new AttestationError(AttestationErrorCode.INVALID_CERTIFICATE_CHAIN, `Certificate chain verification failed: ${result.resultMessage}`);
230
+ // Parse root CA and all x5c certs
231
+ const rootCaDer = parseRootCaDer();
232
+ const rootCa = parseCertificate(rootCaDer);
233
+ const certs = x5c.map((der) => parseCertificate(der));
234
+ // Build chain: [leaf, ..., intermediate, rootCA]
235
+ const chain = [...certs, rootCa];
236
+ // Verify root is self-signed
237
+ if (!constantTimeEqual(rootCa.issuer, rootCa.subject)) {
238
+ throw new AttestationError(AttestationErrorCode.INVALID_CERTIFICATE_CHAIN, "Root CA is not self-signed (issuer !== subject)");
239
+ }
240
+ const rootSelfSigned = await verifySignature(rootCa, rootCa);
241
+ if (!rootSelfSigned) {
242
+ throw new AttestationError(AttestationErrorCode.INVALID_CERTIFICATE_CHAIN, "Root CA self-signature verification failed");
243
+ }
244
+ // Verify each child→parent pair in the chain
245
+ for (let i = 0; i < chain.length - 1; i++) {
246
+ const child = chain[i];
247
+ const parent = chain[i + 1];
248
+ // Issuer must match parent's subject
249
+ if (!constantTimeEqual(child.issuer, parent.subject)) {
250
+ throw new AttestationError(AttestationErrorCode.INVALID_CERTIFICATE_CHAIN, `Certificate ${i} issuer does not match certificate ${i + 1} subject`);
251
+ }
252
+ // Verify signature
253
+ const valid = await verifySignature(child, parent);
254
+ if (!valid) {
255
+ throw new AttestationError(AttestationErrorCode.INVALID_CERTIFICATE_CHAIN, `Certificate ${i} signature verification failed`);
256
+ }
257
+ }
258
+ // Check validity periods
259
+ const now = checkDate ?? new Date();
260
+ for (let i = 0; i < chain.length; i++) {
261
+ const cert = chain[i];
262
+ if (now < cert.validityNotBefore || now > cert.validityNotAfter) {
263
+ throw new AttestationError(AttestationErrorCode.INVALID_CERTIFICATE_CHAIN, `Certificate ${i} is not valid at ${now.toISOString()} (valid from ${cert.validityNotBefore.toISOString()} to ${cert.validityNotAfter.toISOString()})`);
264
+ }
38
265
  }
39
266
  }
40
267
  /**
@@ -47,23 +274,19 @@ export async function verifyCertificateChain(x5c, checkDate) {
47
274
  * @returns The 32-byte nonce
48
275
  */
49
276
  export function extractNonceFromCert(certDer) {
50
- const cert = pkijs.Certificate.fromBER(certDer);
51
- const extensions = cert.extensions;
52
- if (!extensions) {
53
- throw new AttestationError(AttestationErrorCode.INVALID_FORMAT, "Certificate has no extensions");
54
- }
55
- const nonceExt = extensions.find((ext) => ext.extnID === APPLE_NONCE_EXTENSION_OID);
277
+ const cert = parseCertificate(certDer);
278
+ const nonceExt = cert.extensions.find((ext) => ext.oid === APPLE_NONCE_EXTENSION_OID);
56
279
  if (!nonceExt) {
57
280
  throw new AttestationError(AttestationErrorCode.INVALID_FORMAT, `Certificate missing nonce extension (OID ${APPLE_NONCE_EXTENSION_OID})`);
58
281
  }
59
282
  // Parse the extension value as ASN.1
60
- const extValue = nonceExt.extnValue.getValue();
61
- const asn1 = asn1js.fromBER(extValue);
62
- if (asn1.offset === -1) {
283
+ const extBuf = safeBuffer(nonceExt.value);
284
+ const extAsn1 = asn1js.fromBER(extBuf);
285
+ if (extAsn1.offset === -1) {
63
286
  throw new AttestationError(AttestationErrorCode.INVALID_FORMAT, "Failed to parse nonce extension ASN.1");
64
287
  }
65
288
  // Navigate: SEQUENCE -> tagged [1] -> OCTET STRING
66
- const sequence = asn1.result;
289
+ const sequence = extAsn1.result;
67
290
  const tagged = sequence.valueBlock.value[0];
68
291
  const octetString = tagged.valueBlock.value[0];
69
292
  return new Uint8Array(octetString.valueBlock.valueHexView);
@@ -75,12 +298,13 @@ export function extractNonceFromCert(certDer) {
75
298
  * @returns 65-byte uncompressed EC point (0x04 || x || y)
76
299
  */
77
300
  export async function extractPublicKeyFromCert(certDer) {
78
- const cert = pkijs.Certificate.fromBER(certDer);
79
- // Get the SubjectPublicKeyInfo and convert to DER
80
- const spkiDer = cert.subjectPublicKeyInfo.toSchema().toBER(false);
81
- // Import as SPKI to get a CryptoKey
82
- const cryptoKey = await crypto.subtle.importKey("spki", spkiDer, { name: "ECDSA", namedCurve: "P-256" }, true, ["verify"]);
83
- // Export as raw (uncompressed point)
301
+ const cert = parseCertificate(certDer);
302
+ const namedCurve = CURVE_OID_NAME[cert.publicKeyCurveOid];
303
+ if (!namedCurve) {
304
+ throw new AttestationError(AttestationErrorCode.INVALID_FORMAT, `Unsupported curve OID: ${cert.publicKeyCurveOid}`);
305
+ }
306
+ // Import SPKI as CryptoKey, then export as raw uncompressed point
307
+ const cryptoKey = await crypto.subtle.importKey("spki", cert.subjectPublicKeyInfoDer, { name: "ECDSA", namedCurve }, true, ["verify"]);
84
308
  const rawKey = await crypto.subtle.exportKey("raw", cryptoKey);
85
309
  return new Uint8Array(rawKey);
86
310
  }
package/esm/src/der.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export declare function derToRaw(der: Uint8Array): Uint8Array;
2
- export declare function rawToDer(raw: Uint8Array): Uint8Array;
1
+ export declare function derToRaw(der: Uint8Array, componentSize?: number): Uint8Array;
2
+ export declare function rawToDer(raw: Uint8Array, componentSize?: number): Uint8Array;
3
3
  //# sourceMappingURL=der.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"der.d.ts","sourceRoot":"","sources":["../../src/src/der.ts"],"names":[],"mappings":"AAEA,wBAAgB,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,UAAU,CAoCpD;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,UAAU,CAiBpD"}
1
+ {"version":3,"file":"der.d.ts","sourceRoot":"","sources":["../../src/src/der.ts"],"names":[],"mappings":"AAEA,wBAAgB,QAAQ,CACtB,GAAG,EAAE,UAAU,EACf,aAAa,SAAK,GACjB,UAAU,CAoCZ;AAED,wBAAgB,QAAQ,CACtB,GAAG,EAAE,UAAU,EACf,aAAa,SAAK,GACjB,UAAU,CAmBZ"}
package/esm/src/der.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/der.ts
2
- export function derToRaw(der) {
2
+ export function derToRaw(der, componentSize = 32) {
3
3
  if (der[0] !== 0x30) {
4
4
  throw new Error("Invalid DER signature: expected SEQUENCE tag (0x30)");
5
5
  }
@@ -8,7 +8,7 @@ export function derToRaw(der) {
8
8
  const lengthBytes = der[1] & 0x7f;
9
9
  offset = 2 + lengthBytes;
10
10
  }
11
- const raw = new Uint8Array(64);
11
+ const raw = new Uint8Array(componentSize * 2);
12
12
  if (der[offset] !== 0x02) {
13
13
  throw new Error("Invalid DER signature: expected INTEGER tag (0x02) for r");
14
14
  }
@@ -22,16 +22,16 @@ export function derToRaw(der) {
22
22
  offset++;
23
23
  const sLen = der[offset++];
24
24
  const sBytes = der.subarray(offset, offset + sLen);
25
- copyInteger(rBytes, raw, 0);
26
- copyInteger(sBytes, raw, 32);
25
+ copyInteger(rBytes, raw, 0, componentSize);
26
+ copyInteger(sBytes, raw, componentSize, componentSize);
27
27
  return raw;
28
28
  }
29
- export function rawToDer(raw) {
30
- if (raw.length !== 64) {
31
- throw new Error(`Invalid raw signature: expected 64 bytes, got ${raw.length}`);
29
+ export function rawToDer(raw, componentSize = 32) {
30
+ if (raw.length !== componentSize * 2) {
31
+ throw new Error(`Invalid raw signature: expected ${componentSize * 2} bytes, got ${raw.length}`);
32
32
  }
33
- const r = encodeInteger(raw.subarray(0, 32));
34
- const s = encodeInteger(raw.subarray(32, 64));
33
+ const r = encodeInteger(raw.subarray(0, componentSize));
34
+ const s = encodeInteger(raw.subarray(componentSize, componentSize * 2));
35
35
  const seqLen = r.length + s.length;
36
36
  const der = new Uint8Array(2 + seqLen);
37
37
  der[0] = 0x30;
@@ -40,16 +40,16 @@ export function rawToDer(raw) {
40
40
  der.set(s, 2 + r.length);
41
41
  return der;
42
42
  }
43
- function copyInteger(src, dst, dstOffset) {
43
+ function copyInteger(src, dst, dstOffset, componentSize) {
44
44
  let srcOffset = 0;
45
45
  while (srcOffset < src.length - 1 && src[srcOffset] === 0) {
46
46
  srcOffset++;
47
47
  }
48
48
  const len = src.length - srcOffset;
49
- if (len > 32) {
49
+ if (len > componentSize) {
50
50
  throw new Error(`Integer too large: ${len} bytes`);
51
51
  }
52
- dst.set(src.subarray(srcOffset), dstOffset + (32 - len));
52
+ dst.set(src.subarray(srcOffset), dstOffset + (componentSize - len));
53
53
  }
54
54
  function encodeInteger(value) {
55
55
  let start = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bradford-tech/supabase-integrity-attest",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Verify Apple App Attest attestations and assertions using WebCrypto.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,9 +20,9 @@
20
20
  "test": "node test_runner.js"
21
21
  },
22
22
  "dependencies": {
23
+ "@noble/curves": "^2.0.1",
23
24
  "asn1js": "^3.0.7",
24
- "cborg": "^4.5.8",
25
- "pkijs": "^3.3.3"
25
+ "cborg": "^4.5.8"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^20.9.0",