@orangecheck/sdk 1.15.0 → 1.16.1

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/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { bech32 } from '@scure/base';
2
- import { Buffer } from 'buffer';
3
2
  import { verifySignature as verifySignature$1, decodeAddress } from '@orangecheck/bip322';
3
+ import { Buffer } from 'buffer';
4
4
  import { hmac } from '@noble/hashes/hmac.js';
5
5
  import { sha256 } from '@noble/hashes/sha2.js';
6
6
  import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils.js';
@@ -213,6 +213,7 @@ function logVerificationError(protocol, identifier, error, context) {
213
213
  }
214
214
 
215
215
  // src/utils/time.ts
216
+ var ISSUED_AHEAD_MS = 12e4;
216
217
  var RFC3339 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
217
218
  function rfc3339Ms(value) {
218
219
  if (!value || !RFC3339.test(value)) return null;
@@ -245,109 +246,28 @@ function computeScore(sats, days, config) {
245
246
  function computeAllScores(sats, days) {
246
247
  return { v0: scoreV0(sats, days), tier: scoreTier(sats, days) };
247
248
  }
248
-
249
- // src/verify.ts
250
- var log = createLogger("ocp/verify");
251
- function looksLikeHex(s) {
252
- return /^[0-9a-fA-F]+$/.test(s) && s.length % 2 === 0;
253
- }
254
249
  function toBase64Maybe(sig) {
255
- if (looksLikeHex(sig)) {
250
+ if (/^[0-9a-fA-F]+$/.test(sig) && sig.length % 2 === 0) {
256
251
  return Buffer.from(sig, "hex").toString("base64");
257
252
  }
258
253
  return sig;
259
254
  }
260
- function detectSignatureScheme(addr, sig) {
261
- const isSegWit = addr.startsWith("bc1") || addr.startsWith("tb1");
262
- if (isSegWit) {
263
- return "bip322";
264
- }
255
+ function signatureScheme(sigBase64) {
256
+ if (/^(ful|pof)/.test(sigBase64)) return null;
257
+ if (sigBase64.startsWith("smp")) return "bip322";
258
+ return Buffer.from(sigBase64, "base64").length === 65 ? "legacy" : "bip322";
259
+ }
260
+
261
+ // src/verify.ts
262
+ var log = createLogger("ocp/verify");
263
+ async function verifySignature({ addr, msg, sig }) {
265
264
  const sigBase64 = toBase64Maybe(sig);
266
- const looksLikeLegacy = sigBase64.length === 88 && /^[IHG]/.test(sigBase64);
267
- if (looksLikeLegacy) {
268
- return "legacy";
269
- }
270
- return "bip322";
271
- }
272
- async function verifySignature({
273
- addr,
274
- msg,
275
- sig,
276
- scheme
277
- }) {
278
- const detectedScheme = scheme || detectSignatureScheme(addr, sig);
279
- log.debug(
280
- { addr, msgLength: msg.length, sigLength: sig.length, scheme: detectedScheme },
281
- "Verifying signature"
282
- );
283
- if (detectedScheme === "bip322") {
284
- const sigBase64 = toBase64Maybe(sig);
285
- log.debug(
286
- { sigBase64: sigBase64.substring(0, 50) + "..." },
287
- "Signature converted to base64"
288
- );
289
- log.debug(
290
- {
291
- msgFull: msg,
292
- msgLines: msg.split("\n").length,
293
- msgFirstLine: msg.split("\n")[0],
294
- msgLastLine: msg.split("\n")[msg.split("\n").length - 1]
295
- },
296
- "Message being verified"
297
- );
298
- const ok = verifySignature$1(addr, msg, sigBase64);
299
- log.info({ addr, ok }, "BIP-322 verification result");
300
- if (!ok) {
301
- const isSegWit = addr.startsWith("bc1") || addr.startsWith("tb1");
302
- if (isSegWit && sigBase64.length === 88) {
303
- log.warn(
304
- { addr, wallet: "possibly Electrum", sigLength: sigBase64.length },
305
- "BIP-322 verification failed - likely Electrum wallet (does not support BIP-322 for SegWit)"
306
- );
307
- } else {
308
- log.error(
309
- {
310
- addr,
311
- sigLength: sigBase64.length,
312
- msgLength: msg.length,
313
- msgPreview: msg.substring(0, 200)
314
- },
315
- "BIP-322 verification failed - signature does not match message"
316
- );
317
- }
318
- }
319
- return ok ? "sig_ok_bip322" : "sig_invalid";
320
- }
321
- if (detectedScheme === "legacy") {
322
- const sigBase64 = toBase64Maybe(sig);
323
- if (!addr.startsWith("1") && !addr.startsWith("m") && !addr.startsWith("n")) {
324
- log.warn({ addr }, "Legacy signature attempted on non-P2PKH address");
325
- return "sig_unsupported_script";
326
- }
327
- log.debug(
328
- {
329
- addr,
330
- sigBase64: sigBase64.substring(0, 50) + "...",
331
- msgPreview: msg.substring(0, 100) + "...",
332
- msgLength: msg.length
333
- },
334
- "Verifying legacy signature"
335
- );
336
- const ok = verifySignature$1(addr, msg, sigBase64);
337
- log.info({ addr, ok, sigBase64Length: sigBase64.length }, "Legacy verification result");
338
- if (!ok) {
339
- log.error(
340
- {
341
- addr,
342
- msgFirstLine: msg.split("\n")[0],
343
- sigFormat: sigBase64.substring(0, 10)
344
- },
345
- "Legacy signature verification failed - check message format and signature"
346
- );
347
- }
348
- return ok ? "sig_ok_legacy" : "sig_invalid";
349
- }
350
- return "invalid_scheme";
265
+ const scheme = signatureScheme(sigBase64);
266
+ if (!scheme) return "sig_unsupported_script";
267
+ const ok = verifySignature$1(addr, msg, sigBase64);
268
+ log.info({ addr, ok, scheme, msgLength: msg.length }, "Signature verification result");
269
+ if (!ok) return "sig_invalid";
270
+ return scheme === "legacy" ? "sig_ok_legacy" : "sig_ok_bip322";
351
271
  }
352
272
  async function signatureIsValid(input) {
353
273
  try {
@@ -532,6 +452,15 @@ async function verify(input, opts = {}) {
532
452
  return { ok: false, codes, network, attestation_id };
533
453
  }
534
454
  }
455
+ const issued = rfc3339Ms(parsed.core.issued_at);
456
+ if (issued === null) {
457
+ codes.push("decode_error");
458
+ return { ok: false, codes, network, attestation_id };
459
+ }
460
+ if (issued > Date.now() + ISSUED_AHEAD_MS) {
461
+ codes.push("not_yet_valid");
462
+ return { ok: false, codes, network, attestation_id };
463
+ }
535
464
  if (parsed.expires !== void 0) {
536
465
  const exp = rfc3339Ms(parsed.expires);
537
466
  if (exp === null || exp < Date.now()) {
@@ -1260,7 +1189,6 @@ function extractAttestationIdFromUrl(url) {
1260
1189
  }
1261
1190
 
1262
1191
  // src/check.ts
1263
- var ISSUED_AHEAD_MS = 12e4;
1264
1192
  var MAX_CANDIDATES = 20;
1265
1193
  async function check(params) {
1266
1194
  const {
@@ -1486,24 +1414,17 @@ async function verifyChallenge(opts) {
1486
1414
  if (opts.expectedPurpose && purpose !== opts.expectedPurpose) {
1487
1415
  return { ok: false, reason: "purpose_mismatch" };
1488
1416
  }
1489
- const scheme = opts.scheme ?? "bip322";
1490
- let sigValid;
1491
- if (scheme === "bip322") {
1492
- const sigBase64 = /^[0-9a-fA-F]+$/.test(opts.signature) && opts.signature.length % 2 === 0 ? Buffer.from(opts.signature, "hex").toString("base64") : opts.signature;
1493
- sigValid = verifySignature$1(address, opts.message, sigBase64);
1494
- } else if (scheme === "legacy") {
1495
- if (!/^[1m][a-km-zA-HJ-NP-Z1-9]{25,34}$/.test(address)) {
1496
- return { ok: false, reason: "sig_unsupported_scheme" };
1497
- }
1498
- sigValid = verifySignature$1(address, opts.message, opts.signature);
1499
- } else {
1500
- return { ok: false, reason: "sig_unsupported_scheme" };
1417
+ const sigBase64 = toBase64Maybe(opts.signature);
1418
+ const scheme = signatureScheme(sigBase64);
1419
+ if (!scheme) return { ok: false, reason: "sig_unsupported_scheme" };
1420
+ if (!verifySignature$1(address, opts.message, sigBase64)) {
1421
+ return { ok: false, reason: "sig_invalid" };
1501
1422
  }
1502
- if (!sigValid) return { ok: false, reason: "sig_invalid" };
1503
1423
  return {
1504
1424
  ok: true,
1505
1425
  reason: "ok",
1506
1426
  address,
1427
+ scheme,
1507
1428
  expiresAt: expiresAt.getTime(),
1508
1429
  nonce,
1509
1430
  ...audience !== void 0 ? { audience } : {},
@@ -2409,12 +2330,12 @@ var STATUS_META = {
2409
2330
  },
2410
2331
  sig_invalid: {
2411
2332
  label: "Signature verification failed",
2412
- detail: "The signature does not match the message and address. Common issues: (1) Message not copied exactly from Step 2, (2) Wrong wallet/address used, (3) Wallet does not support BIP-322 (Electrum does NOT support BIP-322 for SegWit addresses - use Sparrow Wallet instead). For testnet, ensure your wallet is in testnet mode.",
2333
+ detail: "The signature does not match the message and address. Common issues: (1) the message was not copied exactly, (2) a different address signed it, (3) the wallet signed on another network.",
2413
2334
  severity: "error"
2414
2335
  },
2415
2336
  sig_unsupported_script: {
2416
- label: "Wallet does not support BIP-322",
2417
- detail: 'Your wallet generated a legacy signature, but your SegWit address (bc1/tb1) requires BIP-322 message signing. Please use a wallet that supports BIP-322 (like Sparrow Wallet or Bitcoin Core), or switch to a P2PKH address (starting with "1" for mainnet or "m"/"n" for testnet) if your wallet only supports legacy signing.',
2337
+ label: "Signature form not supported",
2338
+ detail: "Your wallet made a BIP-322 full or proof-of-funds signature. Sign with a BIP-322 simple signature, or with legacy message signing, which every address type here accepts.",
2418
2339
  severity: "error"
2419
2340
  },
2420
2341
  bond_confirmed: {
@@ -2447,6 +2368,11 @@ var STATUS_META = {
2447
2368
  detail: "This signature has passed its expiration date. Generate a new signature with a fresh message.",
2448
2369
  severity: "error"
2449
2370
  },
2371
+ not_yet_valid: {
2372
+ label: "Not valid yet",
2373
+ detail: "The message is dated ahead of this clock. It counts once that time arrives, or once a clock that is wrong is set right.",
2374
+ severity: "error"
2375
+ },
2450
2376
  network_testmode: {
2451
2377
  label: "Test network detected",
2452
2378
  detail: "This signature uses testnet or signet. Test networks are for development only and cannot be verified in production mode.",