@atproto/xrpc-server 0.4.0 → 0.4.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @atproto/xrpc-server
2
2
 
3
+ ## 0.4.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies []:
8
+ - @atproto/lexicon@0.3.1
9
+
10
+ ## 0.4.1
11
+
12
+ ### Patch Changes
13
+
14
+ - [#1839](https://github.com/bluesky-social/atproto/pull/1839) [`e1b5f253`](https://github.com/bluesky-social/atproto/commit/e1b5f2537a5ba4d8b951a741269b604856028ae5) Thanks [@dholms](https://github.com/dholms)! - Prevent signature malleability through DER-encoded signatures
15
+
16
+ - Updated dependencies [[`e1b5f253`](https://github.com/bluesky-social/atproto/commit/e1b5f2537a5ba4d8b951a741269b604856028ae5)]:
17
+ - @atproto/crypto@0.3.0
18
+
3
19
  ## 0.4.0
4
20
 
5
21
  ### Minor Changes
package/dist/index.js CHANGED
@@ -43596,13 +43596,13 @@ var ensureValidAtUri = (uri2) => {
43596
43596
  throw new Error("ATURI requires at least method and authority sections");
43597
43597
  }
43598
43598
  try {
43599
- ensureValidHandle(parts[2]);
43600
- } catch {
43601
- try {
43599
+ if (parts[2].startsWith("did:")) {
43602
43600
  ensureValidDid(parts[2]);
43603
- } catch {
43604
- throw new Error("ATURI authority must be a valid handle or DID");
43601
+ } else {
43602
+ ensureValidHandle(parts[2]);
43605
43603
  }
43604
+ } catch {
43605
+ throw new Error("ATURI authority must be a valid handle or DID");
43606
43606
  }
43607
43607
  if (parts.length >= 4) {
43608
43608
  if (parts[3].length == 0) {
@@ -44938,7 +44938,7 @@ function datetime(path, value) {
44938
44938
  } catch {
44939
44939
  return {
44940
44940
  success: false,
44941
- error: new ValidationError(`${path} must be an iso8601 formatted datetime`)
44941
+ error: new ValidationError(`${path} must be an valid atproto datetime (both RFC-3339 and ISO-8601)`)
44942
44942
  };
44943
44943
  }
44944
44944
  return { success: true, value };
@@ -51925,11 +51925,23 @@ var verifyDidSig = async (did2, data, sig, opts) => {
51925
51925
  return verifySig(keyBytes, data, sig, opts);
51926
51926
  };
51927
51927
  var verifySig = async (publicKey, data, sig, opts) => {
51928
+ const allowMalleable = opts?.allowMalleableSig ?? false;
51928
51929
  const msgHash = await sha2562(data);
51930
+ if (!allowMalleable && !isCompactFormat(sig)) {
51931
+ return false;
51932
+ }
51929
51933
  return p256.verify(sig, msgHash, publicKey, {
51930
- lowS: opts?.lowS ?? true
51934
+ lowS: !allowMalleable
51931
51935
  });
51932
51936
  };
51937
+ var isCompactFormat = (sig) => {
51938
+ try {
51939
+ const parsed = p256.Signature.fromCompact(sig);
51940
+ return equals3(parsed.toCompactRawBytes(), sig);
51941
+ } catch {
51942
+ return false;
51943
+ }
51944
+ };
51933
51945
 
51934
51946
  // ../crypto/src/p256/plugin.ts
51935
51947
  var p256Plugin = {
@@ -51948,11 +51960,23 @@ var verifyDidSig2 = async (did2, data, sig, opts) => {
51948
51960
  return verifySig2(keyBytes, data, sig, opts);
51949
51961
  };
51950
51962
  var verifySig2 = async (publicKey, data, sig, opts) => {
51963
+ const allowMalleable = opts?.allowMalleableSig ?? false;
51951
51964
  const msgHash = await sha2562(data);
51965
+ if (!allowMalleable && !isCompactFormat2(sig)) {
51966
+ return false;
51967
+ }
51952
51968
  return secp256k1.verify(sig, msgHash, publicKey, {
51953
- lowS: opts?.lowS ?? true
51969
+ lowS: !allowMalleable
51954
51970
  });
51955
51971
  };
51972
+ var isCompactFormat2 = (sig) => {
51973
+ try {
51974
+ const parsed = secp256k1.Signature.fromCompact(sig);
51975
+ return equals3(parsed.toCompactRawBytes(), sig);
51976
+ } catch {
51977
+ return false;
51978
+ }
51979
+ };
51956
51980
 
51957
51981
  // ../crypto/src/secp256k1/plugin.ts
51958
51982
  var secp256k1Plugin = {
@@ -52051,7 +52075,7 @@ var verifyJwt = async (jwtStr, ownDid, getSigningKey) => {
52051
52075
  const sigBytes = fromString2(sig, "base64url");
52052
52076
  const verifySignatureWithKey = (key) => {
52053
52077
  return verifySignature(key, msgBytes, sigBytes, {
52054
- lowS: false
52078
+ allowMalleableSig: true
52055
52079
  });
52056
52080
  };
52057
52081
  const signingKey = await getSigningKey(payload.iss, false);
@@ -52500,7 +52524,7 @@ function decodeQueryParam(type, value) {
52500
52524
  if (type === "float") {
52501
52525
  return Number(String(value));
52502
52526
  } else if (type === "integer") {
52503
- return Number(String(value)) | 0;
52527
+ return parseInt(String(value), 10) || 0;
52504
52528
  } else if (type === "boolean") {
52505
52529
  return value === "true";
52506
52530
  }
@@ -52899,11 +52923,9 @@ var Server = class {
52899
52923
  req,
52900
52924
  res
52901
52925
  };
52902
- if (consumeRateLimit) {
52903
- const result = await consumeRateLimit(reqCtx);
52904
- if (result instanceof RateLimitExceededError) {
52905
- return next(result);
52906
- }
52926
+ const result = await consumeRateLimit(reqCtx);
52927
+ if (result instanceof RateLimitExceededError) {
52928
+ return next(result);
52907
52929
  }
52908
52930
  const outputUnvalidated = await handler(reqCtx);
52909
52931
  if (isHandlerError(outputUnvalidated)) {