Skip to content

Commit f947b35

Browse files
committed
Fix RFC 6979 nonconformance; INVARIANT-34 rewritten, INVARIANT-35 added
RFC 6979 conformance -------------------- ama_nistp_ecdsa_sign normalised `s` to the low representative unconditionally, so it failed RFC 6979's own Appendix A.2.5/A.2.6/A.2.7 vectors on every case whose natural `s` came out high — roughly half — while the header advertised "deterministic per RFC 6979". `r` matched everywhere, so the nonce derivation was correct and the divergence was invisible to every test that existed. It was invisible for a second, worse reason: the "independent" reference in tests/test_nistp_curves.py normalised too, because it was written alongside the C code rather than from the specification. Two implementations that share an assumption do not check each other. Signing now emits RFC 6979's `s` verbatim. All 18 in-scope vectors from the RFC are vendored under tests/kat/rfc6979/ and replayed, including the RFC's printed public keys. _ref_sign takes the policy as a parameter instead of baking one in. INVARIANT-34 — low-`s` is a property of the sign/verify pair ------------------------------------------------------------ Normalising on the signer while verifying permissively — what shipped — prevents nothing: the twin of an AMA signature still verifies under AMA. It only costs conformance. The pair is the control; either half alone is a costume. NIST prime curves now default to neither half; secp256k1 keeps both (INVARIANT-28 unchanged). New ama_nistp_ecdsa_sign_ex / _sign_raw_ex make every combination of {deterministic, hedged} x {DER, raw} x {RFC s, low s} reachable through one entry point, with unknown flag bits rejected. The previous API made hedged+raw raise purely because a fourth function had not been written. INVARIANT-35 — a selector must never resolve weaker than it was asked --------------------------------------------------------------------- INVARIANT-7 covers availability; nothing covered selection until the library grew nine selectable levels across three families. A selector that maps an unrecognised value onto a neighbour produces working code, valid signatures and successful handshakes at a level nobody chose, and never surfaces. tests/test_selector_strictness.py derives its selector list from the modules rather than a literal, drives each with plausible near-misses including every other family's valid values, and asserts the C side returns 0/NULL rather than another set's size. It found one on its first run: ama_nist_curve_t was 0/1/2, so `0` — what an uninitialised field holds — meant P-256. Renumbered to 256/384/521, which also no longer collides with the ML-KEM or ML-DSA selector spaces. Also ---- NativeBackendUnavailableError is now the single type for "native backend missing", replacing 36 bare RuntimeError raises. PQCUnavailableError is a subclass, so every existing except-clause is unaffected; asserted directly rather than assumed. ctest 56/56, pytest 2854 passed, Wycheproof 4263/0 failures, all gates green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GtRczQn3XzfaikLDrxJLZZ
1 parent 37670da commit f947b35

14 files changed

Lines changed: 1217 additions & 230 deletions

CHANGELOG.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,69 @@ All notable changes to AMA Cryptography will be documented in this file. The for
1919

2020
## [Unreleased]
2121

22+
### Fixed
23+
24+
- **`ama_nistp_ecdsa_sign` did not conform to RFC 6979.** The signer normalised
25+
`s` to the low representative unconditionally, so it failed RFC 6979's own
26+
Appendix A.2.5 / A.2.6 / A.2.7 vectors on every case whose natural `s` came
27+
out high — roughly half of them — while the header advertised "deterministic
28+
per RFC 6979". `r` matched everywhere, so the nonce derivation was correct and
29+
the divergence was invisible to every test that existed.
30+
31+
It was invisible for a second reason worth recording: the "independent"
32+
pure-Python reference in `tests/test_nistp_curves.py` normalised too, because
33+
it was written alongside the C code rather than from the specification. Two
34+
implementations that share an assumption do not check each other. `_ref_sign`
35+
now takes the signing policy as a parameter, and RFC 6979's own 18 in-scope
36+
vectors are vendored under `tests/kat/rfc6979/` and replayed on every run —
37+
including the RFC's printed public keys — so neither implementation can talk
38+
its way out of the specification again.
39+
40+
Signing now emits RFC 6979's `s` verbatim. Low-`s` is opt-in via
41+
`AMA_NISTP_ECDSA_SIGN_LOW_S` / `low_s=True`.
42+
43+
### Changed
44+
45+
- **INVARIANT-34 rewritten around the sign/verify pair.** Low-`s` normalisation
46+
and high-`s` rejection are two halves of one control: with a permissive
47+
verifier, normalising on the signer prevents nothing (the twin of an AMA
48+
signature still verifies under AMA) and costs conformance. The NIST prime
49+
curves now default to neither half; secp256k1 keeps both (INVARIANT-28,
50+
unchanged). `tests/test_nistp_curves.py::test_low_s_is_a_property_of_the_sign_verify_pair`
51+
asserts the four-way truth table directly.
52+
53+
- **New `ama_nistp_ecdsa_sign_ex` / `_sign_raw_ex` with policy flags.** Every
54+
combination of {deterministic, hedged} x {DER, raw} x {RFC 6979 `s`, low `s`}
55+
is now reachable through one entry point; unknown flag bits are rejected
56+
rather than ignored. The previous API made hedged+raw raise purely because a
57+
fourth function had not been written.
58+
59+
- **`ama_nist_curve_t` renumbered to 256 / 384 / 521** (was 0 / 1 / 2). A dense
60+
index made `0` — the value an uninitialised or forgotten field holds — mean
61+
"P-256". Found by the new INVARIANT-35 suite on its first run. The values also
62+
no longer collide with `ama_ml_kem_param_set_t` or `ama_ml_dsa_param_set_t`,
63+
so a call routed to the wrong family is refused rather than resolved. This is
64+
a source-and-ABI change to an API that has not shipped in a release.
65+
66+
- **`NativeBackendUnavailableError`** is now the single type for "the native
67+
backend is not present", replacing 36 bare `RuntimeError` raises across
68+
`pqc_backends.py`. `PQCUnavailableError` is now a subclass, so every existing
69+
`except PQCUnavailableError` and `except RuntimeError` handler is unaffected.
70+
2271
### Added
2372

73+
- **INVARIANT-35 — a selector must never resolve weaker than it was asked.**
74+
INVARIANT-7 governs the availability axis (no backend, no operation); nothing
75+
governed the *selection* axis until the library grew nine selectable security
76+
levels across three families. A selector that maps an unrecognised value onto
77+
a neighbour produces working code, valid signatures and successful handshakes
78+
at a level nobody chose, and never surfaces. Enforced by
79+
`tests/test_selector_strictness.py` (41 tests), which derives its list of
80+
selectors from the modules rather than a hand-written literal, drives each
81+
with plausible near-misses including every *other* family's valid values, and
82+
asserts the C side returns `0` / `NULL` rather than another set's size.
83+
84+
2485
- **NIST prime curves P-256 / P-384 / P-521 — ECDSA and ECDH.** New native
2586
implementation `src/c/ama_nistp.c` (curve parameters from SP 800-186, ECDSA
2687
from FIPS 186-5, ECDH from SP 800-56A §5.7.1.2, deterministic nonces from

INVARIANTS.md

Lines changed: 139 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,69 +1245,152 @@ parsed block.
12451245

12461246
---
12471247

1248-
## INVARIANT-34 — ECDSA Low-`s` Policy Is Per-Curve and Declared
1249-
1250-
**Statement.** Every AMA ECDSA signer, on every curve, emits only the low
1251-
representative (`s <= (n-1)/2`). Verification policy is **per curve** and each
1252-
choice is stated in the header, in the tests, and here:
1253-
1254-
- **secp256k1** rejects a high `s` by default (INVARIANT-28), selectable via
1255-
`ama_secp256k1_ecdsa_verify_ex(..., AMA_SECP256K1_ECDSA_ALLOW_HIGH_S)`.
1256-
- **P-256 / P-384 / P-521** accept either representative by default, and reject
1257-
the high twin only under `AMA_NISTP_ECDSA_REQUIRE_LOW_S`.
1258-
1259-
The checks that cost no interoperability are unconditional on both curves and
1260-
in both modes: minimal DER only, `r` and `s` strictly in `[1, n-1]` rather than
1248+
## INVARIANT-34 — Low-`s` Is a Property of the Sign/Verify Pair
1249+
1250+
**Statement.** Low-`s` normalisation and high-`s` rejection are **two halves of
1251+
one control**. A curve's default must set both or neither, and any API that
1252+
exposes them must expose both.
1253+
1254+
- **secp256k1** sets both by default: `ama_secp256k1_ecdsa_sign` emits only the
1255+
low representative and `ama_secp256k1_ecdsa_verify` rejects the high twin
1256+
(INVARIANT-28). `AMA_SECP256K1_ECDSA_ALLOW_HIGH_S` relaxes the verifier for
1257+
third-party X9.62 interop.
1258+
- **P-256 / P-384 / P-521** set neither by default: `ama_nistp_ecdsa_sign`
1259+
emits RFC 6979's `s` verbatim and `ama_nistp_ecdsa_verify` accepts either
1260+
representative. `AMA_NISTP_ECDSA_SIGN_LOW_S` and
1261+
`AMA_NISTP_ECDSA_REQUIRE_LOW_S` turn both halves on together.
1262+
1263+
The checks that cost no interoperability are unconditional on every curve in
1264+
every mode: minimal DER only, `r` and `s` strictly in `[1, n-1]` rather than
12611265
reduced into range, and public-key coordinates strictly in `[0, p)`.
12621266

1263-
**Why.** The two curves live in different worlds, and a single global policy is
1264-
wrong for one of them.
1265-
1266-
secp256k1 signatures are identifiers. A blockchain transaction is addressed by
1267-
its signature bytes, so accepting `(r, n - s)` means one transaction has two
1268-
identities — the malleability class that INVARIANT-26 and INVARIANT-28 exist to
1269-
close. AMA controls the signer in every path where it produces secp256k1
1270-
signatures, so strict is free.
1271-
1272-
The NIST prime curves exist here for the opposite reason: to verify signatures
1273-
AMA did *not* produce. X9.62, FIPS 186-5, RFC 3279, TLS, X.509, JWS and WebAuthn
1274-
all permit either `s`, and essentially none of their signers normalise. A
1275-
strict-by-default verifier would reject conformant third-party signatures — it
1276-
would not be "more secure", it would be non-interoperable, defeating the entire
1277-
purpose of adding the curves.
1278-
1279-
The asymmetry is therefore a deliberate decision about *what each curve is for*,
1280-
not an oversight or a weakening. Making it an invariant is what stops a future
1281-
change from "harmonising" the two defaults in either direction without
1282-
re-reading this reasoning.
1283-
1284-
**Enforcement.** In `src/c/ama_nistp.c`, `nistp_ecdsa_sign_core()` negates a
1285-
high `s` unconditionally before encoding, and `nistp_ecdsa_verify_rs()` rejects
1286-
one only when `AMA_NISTP_ECDSA_REQUIRE_LOW_S` is set. The range and
1287-
canonical-coordinate gates (`nistp_scalar_load`, `nistp_load_point`) and the
1288-
strict DER parser (`nistp_der_parse`) are outside the flag entirely.
1289-
`ama_secp256k1.c` is unchanged.
1267+
**Why.** Normalisation on its own prevents nothing. If the verifier accepts
1268+
both representatives, then given any AMA signature `(r, s)` anyone can emit
1269+
`(r, n - s)` and AMA itself will accept it. The signature is malleable
1270+
regardless of what the signer chose. Normalisation only becomes a security
1271+
property when the verifier refuses the twin — the pair is the control, and
1272+
either half alone is a costume.
1273+
1274+
This is not hypothetical: it is the defect this invariant was rewritten to fix.
1275+
The first version of the NIST prime-curve support normalised on the signer and
1276+
verified permissively — the one combination with no security benefit — and paid
1277+
for it in conformance. `ama_nistp_ecdsa_sign` advertised itself as
1278+
"deterministic per RFC 6979" while failing RFC 6979's own Appendix A.2.5 /
1279+
A.2.6 / A.2.7 vectors on every case whose natural `s` came out high, roughly
1280+
half of them. `r` matched everywhere, so the nonce derivation was right and the
1281+
divergence was invisible to every test that existed.
1282+
1283+
It was invisible for a second, worse reason. The "independent" pure-Python
1284+
reference in `tests/test_nistp_curves.py` normalised too, because it was
1285+
written alongside the C code rather than from the specification. Two
1286+
implementations that share an assumption do not check each other. **A reference
1287+
must be derived from the specification only, never from the implementation it
1288+
checks** — that rule is the durable lesson here, and it is why `_ref_sign` now
1289+
takes the policy as a parameter instead of baking one in.
1290+
1291+
The per-curve defaults then follow from what each curve is *for*. secp256k1
1292+
signatures are identifiers — a blockchain transaction is addressed by its
1293+
signature bytes, so two valid encodings means two identities, and AMA controls
1294+
both ends, so strict is free. The NIST prime curves exist here to interoperate
1295+
with signers AMA did not write: X9.62, FIPS 186-5, RFC 3279, TLS, X.509, JWS
1296+
and WebAuthn all permit either `s` and essentially none of their signers
1297+
normalise, so a strict default would reject conformant signatures — not "more
1298+
secure", just non-interoperable, defeating the reason the curves were added.
1299+
1300+
**Enforcement.** In `src/c/ama_nistp.c`, `nistp_ecdsa_sign_core()` takes
1301+
`low_s` as a parameter and `nistp_sign_dispatch()` derives it from
1302+
`AMA_NISTP_ECDSA_SIGN_LOW_S`; unknown flag bits are rejected rather than
1303+
ignored. `nistp_ecdsa_verify_rs()` rejects a high `s` only under
1304+
`AMA_NISTP_ECDSA_REQUIRE_LOW_S`. The range, canonical-coordinate and strict-DER
1305+
gates sit outside both flags. `src/c/ama_secp256k1.c` is unchanged.
12901306

12911307
**Wycheproof consequence, declared.** The secp256k1 divergence policy
1292-
`ecdsa/high-s-rejected` in `wycheproof_vectors/run_wycheproof.py` claims exactly
1293-
72 vectors and is scoped to `ecdsa_secp256k1_sha256_test.json` by filename. The
1294-
three NIST prime-curve suites — 1530 vectors — need **no** divergence policy at
1295-
all, and pass with zero exceptions. That asymmetry in the gate output is the
1296-
visible evidence for this invariant: if someone made the NIST default strict, a
1297-
new uncounted divergence bucket would appear and the gate would go red.
1298-
1299-
**Timing posture.** Signing is constant time with respect to the private key and
1300-
the RFC 6979 nonce on both curves. Verification is variable time by design on
1301-
both — every input is public.
1302-
1303-
**Verification.** `tests/test_nistp_curves.py::
1304-
test_high_s_twin_accepted_by_default_rejected_when_strict` asserts *both*
1305-
directions on all three prime curves: the twin verifies by default and does not
1306-
verify under `require_low_s`. `test_signing_always_emits_low_s` asserts the
1307-
signer half over freshly generated keys.
1308+
`ecdsa/high-s-rejected` claims exactly 72 vectors and is scoped by filename.
1309+
The three NIST prime-curve suites — 1530 vectors — need no divergence policy at
1310+
all and pass with zero exceptions. That asymmetry in the gate output is the
1311+
visible evidence for this invariant: making the NIST default strict would
1312+
create a new uncounted divergence bucket and turn the gate red.
1313+
1314+
**Timing posture.** Signing is constant time with respect to the private key
1315+
and the RFC 6979 nonce on both curves; the normalisation is a conditional
1316+
negation of a value that is about to be published. Verification is variable
1317+
time by design on both — every input is public.
1318+
1319+
**Verification.**
1320+
`tests/test_nistp_curves.py::test_rfc6979_published_vectors` replays all 18
1321+
in-scope vectors from RFC 6979 Appendix A.2.5/A.2.6/A.2.7 (vendored under
1322+
`tests/kat/rfc6979/`), asserting the RFC's own public key and both signature
1323+
components, and fails if the corpus ever stops containing a high-`s` case —
1324+
without one it could no longer detect silent normalisation.
1325+
`test_rfc6979_vectors_reject_low_s_normalisation` pins the opposite direction,
1326+
so the trade-off is a fact in CI rather than a claim in a docstring.
1327+
`test_low_s_is_opt_in_and_default_is_rfc6979_verbatim` requires the default to
1328+
produce at least one high `s` over 24 signatures.
1329+
`test_low_s_is_a_property_of_the_sign_verify_pair` asserts the four-way truth
1330+
table directly. `test_ecdsa_matches_rfc6979_reference` runs the
1331+
specification-derived reference under *both* policies.
13081332
`tests/test_secp256k1_ecdsa_low_s_policy.py` holds the secp256k1 half unchanged.
13091333

13101334
---
13111335

1336+
## INVARIANT-35 — A Selector Must Never Resolve Weaker Than It Was Asked
1337+
1338+
**Statement.** Any argument that names an algorithm, curve, parameter set or
1339+
security level **must** resolve to exactly what was named or fail. It must
1340+
never fall back to a default, round to a neighbour, or return a plausible
1341+
answer for an input it did not recognise.
1342+
1343+
Concretely, for every selector in the library:
1344+
1345+
- an unrecognised value raises (Python) or returns `NULL` / `0` (C);
1346+
- a size or capability query for an unrecognised value returns `0` or `NULL`,
1347+
never the size of some other parameter set;
1348+
- no selector has a "default" branch that maps unknown input onto a real
1349+
choice.
1350+
1351+
**Why.** INVARIANT-7 covers the *availability* axis: no backend, no operation.
1352+
This is the *selection* axis, and nothing covered it until the library grew
1353+
enough parameter sets for it to matter. As of the NIST prime curves and the
1354+
FIPS 203/204 parameter-set work there are nine selectable levels across three
1355+
families, plus SLH-DSA's two — an integer or a string away from each other.
1356+
1357+
The failure this prevents is quiet and total. A selector that maps an
1358+
unrecognised `"ML-KEM-192"` onto ML-KEM-512, or a mistyped curve id onto
1359+
P-256, produces working code, valid signatures and successful handshakes at a
1360+
security level nobody chose and no test asserts. Unlike a missing backend, it
1361+
never surfaces: the caller believes it asked for category 5 and got category 1,
1362+
and every downstream artefact is well-formed. A downgrade that reports success
1363+
is worse than a hard failure, because only the hard failure gets fixed.
1364+
1365+
The rule is deliberately absolute rather than "must not resolve *weaker*". A
1366+
selector cannot know which direction is weaker for a given caller — P-521 is
1367+
not simply "stronger" than P-256 for someone whose peer only speaks P-256 —
1368+
and a rule that requires that judgement invites a fallback that argues it got
1369+
the direction right. Resolve exactly, or refuse.
1370+
1371+
**Enforcement.** In C: `nistp_lookup()`, `kyber_params_for()`,
1372+
`dil_params_for()` and `slhdsa_params_for()` each end in `default: return NULL`
1373+
and every public size/name query propagates that as `0` / `NULL`. In Python:
1374+
`_param_set_id()` (shared by `_ml_kem_id` / `_ml_dsa_id`) and
1375+
`_nistp_curve_id()` raise `ValueError` on any unrecognised value, and reject
1376+
`bool` explicitly — `True` is an `int` in Python and would otherwise index a
1377+
selector table.
1378+
1379+
Name aliases are permitted and are not a violation: `"secp256r1"`,
1380+
`"prime256v1"` and `"P-256"` denote the same curve, and `"Dilithium3"` denotes
1381+
ML-DSA-65. An alias resolves to the thing it names. What is forbidden is
1382+
resolving something that names *nothing*.
1383+
1384+
**Verification.** `tests/test_selector_strictness.py` enumerates every selector
1385+
in the library and drives each with the same battery of unrecognised inputs —
1386+
neighbouring-but-invalid integers, plausible-but-wrong names, `bool`, `None`,
1387+
negative values, and the empty string — asserting a raise every time. It also
1388+
asserts the C side returns `0` / `NULL` rather than another set's answer, and
1389+
that the alias tables resolve only to sets that actually exist. Adding a
1390+
selector without adding it to that enumeration fails the test, because the test
1391+
derives its list from the modules rather than from a hand-written literal.
1392+
1393+
---
1394+
13121395
_Maintained by Steel Security Advisors LLC._
13131396
_Last updated: 2026-07-27_
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
bcde7df97d18f5d41402b970a65b4050c70eba6690551b30b301d85d1eca2f35
1+
6ded65965672e4522afc74d154aba84dbc88f7257e0044226896bfa21f6412d1

ama_cryptography/_integrity_signature.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515

1616
# SHA3-256 digest of the package's .py files at build time (raw 32 bytes,
1717
# hex-encoded for embeddability).
18-
INTEGRITY_DIGEST_HEX = "bcde7df97d18f5d41402b970a65b4050c70eba6690551b30b301d85d1eca2f35"
18+
INTEGRITY_DIGEST_HEX = "6ded65965672e4522afc74d154aba84dbc88f7257e0044226896bfa21f6412d1"
1919

2020
# Ephemeral build-time Ed25519 public key (raw 32 bytes, hex-encoded).
21-
INTEGRITY_PUBKEY_HEX = "61227627013653b6ba224b13fbaeebcfb0f94516a79c148dbf4ece8befd637ec"
21+
INTEGRITY_PUBKEY_HEX = "781b363cd77e64639d202b05f3b861de1d7ee573db99a4d3a2fa1eb4fdaff79c"
2222

2323
# Ed25519 signature over the raw digest above (raw 64 bytes, hex-encoded).
24-
INTEGRITY_SIGNATURE_HEX = "5dbc6756de8fc6c95ce11889d3dd5a06547f72775fa47eb8bf0abf72674165110debd0327daa071a6863ab2bb4ef2e9b2db3dd5ef09e8bf09dcfc617d291fd0c"
24+
INTEGRITY_SIGNATURE_HEX = "3071ac8037c8fbe50195a17b82b5eea64fc5dcce475e12c02bf1ec651496385dd636d24df98ebecf19878752f80e65dc3359ec8ffc70621d518b23de0460fd02"
2525

2626
# Build metadata — informational only, not part of the integrity contract.
2727
BUILD_PIPELINE_VERSION = "1"

ama_cryptography/exceptions.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,38 @@ class AmaCryptographyError(Exception):
4747
pass
4848

4949

50-
class PQCUnavailableError(AmaCryptographyError, RuntimeError):
50+
class NativeBackendUnavailableError(AmaCryptographyError, RuntimeError):
51+
"""
52+
Raised when an operation needs the native C backend and it is not present.
53+
54+
This is INVARIANT-7's failure mode expressed as a type: the library refuses
55+
to operate rather than substituting anything. It is not specific to
56+
post-quantum work — the NIST prime curves, secp256k1 and the classical
57+
primitives raise it for the same reason.
58+
59+
``PQCUnavailableError`` is a subclass, so existing ``except
60+
PQCUnavailableError`` sites keep working and ``except RuntimeError``
61+
continues to catch every case. Catch this class when the question is
62+
"is the native library present", and the subclass when the answer needs
63+
to distinguish which family was asked for.
64+
65+
To resolve, build the native C library:
66+
cmake -B build -DAMA_USE_NATIVE_PQC=ON && cmake --build build
67+
"""
68+
69+
pass
70+
71+
72+
class PQCUnavailableError(NativeBackendUnavailableError):
5173
"""
5274
Raised when post-quantum cryptography is required but unavailable.
5375
5476
This exception indicates that a PQC operation was requested but the
5577
native C backend is not available.
5678
57-
Inherits from AmaCryptographyError (catch-all root) and RuntimeError
58-
(backward compatibility with existing tests and code that expects this
59-
exception hierarchy).
79+
Inherits from NativeBackendUnavailableError (and transitively from
80+
AmaCryptographyError and RuntimeError), so every pre-existing handler
81+
shape keeps working unchanged.
6082
6183
To resolve, build the native C library:
6284
cmake -B build -DAMA_USE_NATIVE_PQC=ON && cmake --build build
@@ -157,6 +179,7 @@ class AmaHSMUnavailableError(AmaCryptographyError, RuntimeError):
157179
__all__ = [
158180
"AmaCryptographyError",
159181
"SecurityWarning",
182+
"NativeBackendUnavailableError",
160183
"PQCUnavailableError",
161184
"QuantumSignatureUnavailableError",
162185
"QuantumSignatureRequiredError",

0 commit comments

Comments
 (0)