Repository navigation
feat(circuits)!: per-platform circuits output id and handle nodes - #17
Open
SupremaLex wants to merge 37 commits into
Open
SupremaLex wants to merge 37 commits into
SupremaLex wants to merge 37 commits into
Conversation
A new circuit for X: the bearer link, plus the id and handle the identity
response now commits instead of revealing. It opens both values as X sent
them, checks them against X's rules, folds the handle byte by byte from the
same witness that opened its commitment, and outputs
idNode = SHA256("libid.x.user-id" || id)
handleNode = SHA256("libid.x.handle" || fold(handle))
72 public inputs: the two bearer commitments as bytes, then the id and
handle commitments and the two nodes as 16-byte big-endian halves.
lib/identity holds what the identity circuits share: commitment openings,
the tagged hash, halves, and the handle and id rules, which refuse rather
than trim. Its constants and one test per vector (src/table.nr) are
generated from libid-contracts' handles.json, the table Solidity, Rust and
TypeScript run; build.sh writes that table's SHA-256 beside the artifacts.
scripts/identity-link-witness.py turns the witness libid-rs emits into a
Prover.toml; the committed one is the libid-rs X fixture (handle Alice_1).
77,505 gates (bearer-link: 41,799). The verifier accepts the proof on anvil
for 916,542 gas and rejects a flipped bit.
Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
GitHub gets the relation bearer-link-x states for X: the bearer link, plus
the committed id (a JSON integer's digits, no leading zero) and login
opened as GitHub sent them, the login folded under GitHub's rules, and
idNode = SHA256("libid.github.user-id" || id)
handleNode = SHA256("libid.github.handle" || fold(login))
out through the same 72 public inputs. 79,445 gates; the verifier accepts
on anvil for 916,422 gas and rejects a flipped bit.
The shared bearer-link circuit goes: no platform proves under it any more.
scripts/identity-link-witness.py reads table.nr constants whole, since
nargo fmt wraps the long ones, and build.sh does the same for the table
digest. The committed Prover.toml is libid-rs' GitHub fixture (OctoCat).
BREAKING CHANGE: bearer-link and BearerLinkHonkVerifier are removed; X
proves under bearer-link-x and GitHub under bearer-link-github.
Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
oidc-google checks the signed email against the Google rules (charset,
one `@` not at an edge, at most 62 bytes, zero tail), folds it, and
exposes handleNode = SHA256("libid.google.handle" || folded address) at
public inputs 36-37 in place of the packed email bytes. The count stays 57,
so exp and the modulus keep their offsets.
The id node takes the uniform tag: SHA256("libid.google.user-id" || sub).
The sub's byte rules are lib/identity's check_id under the Google id rules,
which also refuses a backslash. Both hashes are lib/identity's tagged_hash.
scripts/google-fixture-witness.py writes the committed witness from a
seeded RSA-2048 key, so the token is reproducible; its email is
Fixture@Example.com, mixed case, so the fold is exercised. 199,738 gates
(was 182,952); the verifier accepts on anvil for 937,931 gas and rejects a
flipped bit.
The X and GitHub circuits' header comments now say where public inputs
68-71 come from: the payload's nodes, which the proof binds.
BREAKING CHANGE: public inputs 34-37 are the id and handle nodes; the
email is no longer public and the user id tag is libid.google.user-id.
Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
The table's SHA-256 was written at the artifacts root, which release.yml never packages, so the table identity did not ship. build.sh now copies lib/identity/src/table.nr into every circuit that depends on lib/identity as handles-table.nr, with handles.json.sha256 beside it. release.yml tars and hashes each circuit directory whole, so both files reach the tarball and manifest.json. The root file is gone; nothing read it. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
A `*.secret.json` identity-link witness holds a live bearer, and so does the Prover.toml written from it. identity-link-witness.py now refuses to write such a witness to stdout, refuses an --out path git tracks (checked before the file is opened), and writes --out with mode 0600, set before any byte. A commitment that does not open names the witness entry that failed; no message prints a value. MAX_BEARER_LEN is read from lib/identity/src/lib.nr and the tags and buffer sizes from table.nr through scripts/identity_table.py, so the script restates none of them. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
google-fixture-witness.py reads the Google tags and the address and sub buffer sizes from lib/identity's generated table instead of restating them, and names where DIGEST comes from. CI regenerates the witness into a temp file and diffs it against the committed Prover.toml; the key is seeded, so the output is deterministic. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
bearer-link-x and bearer-link-github repeated the same relation. It now lives in lib/identity as identity_link, generic over the tag and buffer sizes; each main passes its inputs through in order with its platform's tags and rules. Public inputs, gate counts (77505, 79445) and vk_hash are unchanged. The two bearer commitments fail with their own messages, "token bearer commitment mismatch" and "identity bearer commitment mismatch", in place of the shared "hash commit mismatch"; verify_hash_commit, whose only callers were these, is gone. bearer-link-x gains a test for the token label. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
constrain_bearer and hash_commit moved into lib/identity without the tests the retired bearer-link circuit carried. They return in src/tests.nr: an empty bearer, CR, LF, DEL and a byte above 0x7e refused, a dirty tail and a length past the buffer refused, the shortest (one 0x20) and longest (128 x 0x7e) bearers accepted, and hashlib vectors for hash_commit at 4 bytes and at both bearer extremes. table.nr states each platform's lengths twice, as buffer sizes and in the rules; a test asserts MAX_HANDLE_* == HANDLE_RULES_*.max_len and MAX_ID_* == ID_RULES_*.max_len for X, GitHub and Google. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
The sub-byte tests left with assert_sub_byte; they return against check_id under ID_RULES_GOOGLE: 0x1f, 0x7f, 0x80, `"` and `\` refused, and every other byte of 0x20..0x7e accepted. EMAIL_MAX and SUB_MAX are MAX_HANDLE_GOOGLE and MAX_ID_GOOGLE, and the tag lengths are the generated tags' len(), so the circuit restates none of the table. Gate count (199738) and vk_hash are unchanged. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
gen-verifier.sh's usage and the README's verifier list name all three circuits; the key-history paragraphs state how the current keys relate to v0.5.0's. The README and lib/identity cite libid-contracts' handle table at its new path, solidity/contracts/handles/handles.json, and the README notes identity_link and the secret-witness rules of identity-link-witness.py. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…ened A symlink or a missing directory raised a bare OSError traceback; the script now exits with the reason and what to pass instead. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…racts Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…epo-qualified banner) Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…ain.nr google-fixture-witness.py restated SIGNING_INPUT_MAX, PAYLOAD_JSON_MAX, AUDIENCE_MAX, NUM_LIMBS and MOD_BITS. identity_table.py now reads a plain `global` as well as a `pub global`, from any given source, and the script takes the five from circuits/oidc-google/src/main.nr. The regenerated witness is byte-identical. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…he synthetic ones
identity-link-witness.py treated a witness as secret only when its file
was named *.secret.json. It now reads the form: a session file with an
`identity_link_witness` member is libid-rs' synthetic fixture; a bare
witness (`platform`, `token_bearer`, ... at the top level) is a real
capture and secret whatever its name.
For a secret witness --out must end in .toml and lie outside every git
work tree, or be matched by that work tree's ignore rules
(`git check-ignore`, which never matches a tracked file); stdout is
refused. After writing it the script prints, on stderr, how to prove
it from outside the repo: umask 077 and
`nargo execute -p /abs/stem /abs/stem`, which reads stem.toml and
writes the solved witness to stem.gz instead of target/<pkg>.gz.
The README documents that and that the id and handle blinders link the
signed record to the account for as long as it exists, not only until
the bearer is revoked.
The two synthetic witnesses are vendored, wrapped as session files, in
fixtures/{x,github}-identity-link-witness.json from libID-contracts'
ceremony session fixtures. The generated Prover.toml now names its
source in a header comment; both committed ones are regenerated (header
only). CI regenerates them and diffs, and checks the guard: a bare
witness to stdout or to an unignored path in the work tree exits
non-zero, and one outside it is written 0600 with nothing on stdout.
Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
build.sh wrote a per-circuit handles.json.sha256 that copied table.nr's TABLE_SHA256 label rather than hashing anything, and nothing read it: libID-contracts vendors a release by comparing handles-table.nr itself (regen-identity-handles.py --compare-noir). The file is gone from the build, the release comment and the README; handles-table.nr stays. No release has shipped it. contracts.ref pins the libID-contracts commit whose handles.json the table must match. A new handles-table job checks that commit out and runs its regen-identity-handles.py --compare-noir against lib/identity/src/table.nr; it needs only Python. The README says how to move the pin. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…er links
lib/identity gains a testing module with blinder() and padded(), which
the two bearer-link circuits' tests now import instead of each keeping
its own. Both tests' Case carries bearer_len, id_len and handle_len, and
run() passes them through, so the two suites have the same structure.
No main calls the module, so no circuit's constraints change.
lib/identity also tests that check_id refuses a nonzero byte past the
id ("id tail must be zero-padded").
Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…ails The circuit's address check is normalize_handle under HANDLE_RULES_GOOGLE: the fixture address folds, an address holding `"` is refused, and a nonzero byte past the address or past the `sub` is refused. The table's refused Google vectors already cover `"` and `\` in a handle and in a `sub` (handle_google_46/47, id_google_19/20). padded_sub gives way to lib/identity's padded. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
The vk section's account of how the keys differ from v0.5.0's, and the note that the sources were once extracted from the monorepo and formatted, are history; the vk table and the fmt check stay. The witness section says how nargo writes the solved witness under the umask. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
identity-link-witness.py now gives its "neither a session file nor a bare witness" error for a JSON array or scalar too. The README and google-fixture-witness.py spell the repository libID-contracts. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…fail closed on where it goes identity-link-witness.py decides a witness's secrecy and destination this way: - A session file is synthetic only when its `provenance` is an object with `"synthetic": true`; the two vendored fixtures carry it. A bare witness and a session file without the marker are secret. - A secret --out counts as inside a git work tree unless git states "not a git repository" for its directory and no parent directory holds a `.git` entry. A repository git cannot read (dubious ownership, a broken .git) is refused unless check-ignore matches the path, which it cannot do there. Git runs under LC_ALL=C so its message is matched untranslated. - An --out stem containing `.` is refused: nargo's `-p x.secret` reads x.toml. - Only `x` and `github` are accepted, before any table constant is read; identity_table.constant escapes the name it searches for. - The printed instructions quote every path, scope `umask 077` to each command's subshell, read the package name from the circuit's Nargo.toml, and add the `bb prove` step for the solved witness. The CI guard checks an unmarked session file and dubious ownership (GIT_TEST_ASSUME_DIFFERENT_OWNER=1) as well. The README's example uses `mktemp -d`, which works on macOS too. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…table.py Both witness scripts encoded a 32-byte value as the circuits' [high, low] halves and zero-padded a value into its buffer, each with its own copy. identity_table.py now holds one of each: `halves` returns the two integers and each script keeps its own text form (decimal for the bearer links, 0x hex for oidc-google), and `padded` refuses a value longer than its buffer with SystemExit, naming the input. All three Prover.toml files regenerate byte-identical. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…he merge order when the pin is gone
At the libID-contracts commit contracts.ref pins, the job now also
checks that fixtures/{x,github}-identity-link-witness.json hold the
`identity_link_witness` members of the ceremony session fixtures (the
provenance marker aside), and that google-fixture-witness.py's DIGEST is
their `authorization_digest`.
The pin is fetched with plain git instead of actions/checkout, so a
commit libID-contracts no longer serves fails with the fix: merge the
libID-contracts change first, then point contracts.ref at its commit on
main. The README states that order.
Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…testing `blinder` and `padded` were `pub mod testing` in libid_identity, the library every circuit's main calls, so a main could import them. They now live in their own package, libid_identity_testing, with no dependencies; `blinder` takes its width from the call site, so BLINDER_LEN is not restated. lib/identity's tests use `blinder(0)` in place of their own counting_blinder, which built the same bytes. Nargo has no test-only dependencies, so the circuits and lib/identity declare the package as an ordinary dependency and import it in their test sections only. lib/identity therefore lists a test package among its dependencies; it is not re-exported, and a package reaches it only by declaring it. The alternative, a private copy of `padded` in lib/identity's tests, would keep a second copy of the builder. All three vk_hash values are unchanged. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…-google oidc-google hashed its id and handle nodes with its own user_id_digest and handle_digest wrappers over tagged_hash, then split them into halves by hand. lib/identity now has node_halves(tag, value, len), the node as the public inputs carry it, and both identity_link and oidc-google call it where they computed the node before. The id digest tests check node_halves under the Google tag against the same hashlib vectors, written as halves. The helper does not run check_id or normalize_handle: the callers keep those where they were. A helper that runs the check too moves either the check or the node hash relative to the commitment and claim asserts, and that reorders the circuit: bearer-link-x's vk_hash changed with the check-inclusive helper called at the check site and called after the commitments. With node_halves all three vk_hash values are unchanged. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
The README lists what pins v0.6.0's ABIs and must move: libID's browser prover (ts/packages/ceremony on libID's main, pinned to v0.6.0, which proves the shared bearer_link circuit and v0.6.0's oidc_google ABI) and libID-contracts' verifiers, which move to the same release. oidc-google's public-input comment no longer claims REQ-PLAT-16B's list exactly: the id and handle nodes stand in place of its `userId` digest and raw `email`. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…eption A safe.directory entry in the runner's git config makes git skip the ownership check, and the guard's dubious-ownership case then passes through the ordinary in-work-tree refusal, which the fail-open script passed too. The case now runs with GIT_CONFIG_GLOBAL and GIT_CONFIG_SYSTEM at /dev/null, and the step fails first unless git reports dubious ownership. Against the previous script the step fails with "a bare witness was written into a work tree git refused to read". Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…y::testing `blinder` and `padded` live in a `testing` module of libid_identity instead of a separate lib/identity-testing package, so no Nargo.toml declares a second path dependency for tests alone. No circuit's main calls them; the three vk_hash values are unchanged. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
oidc-google drops the `sub` and address tests the generated table already runs (`"`, `\`, a tab, the fold, a quote in the address) and keeps DEL, a non-ASCII byte and the printable sweep. The zero-tail case for a handle runs once in lib/identity's tests instead of in each circuit. bearer-link-github keeps the underscore test, which shows HANDLE_RULES_GITHUB is the selected rule set, and drops the doubled hyphen the table covers. tests.nr keeps one control-byte bearer test (CR, REQ-COMMON-37) and one above-range test (DEL). Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
…o tails buy identity_link's doc in lib.nr is the one statement of the relation; the bearer-link mains name the platform's endpoints, the public-input layout, and point to it. lib.nr's header no longer repeats the node formulas. sha256_var hashes only the first `len` bytes, so the zero-tail checks in check_id, normalize_handle and oidc-google's address and `sub` do not make a node or commitment unique; they make the padded witness canonical. The comments say so. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
identity-link-witness.py: a secret witness (anything not marked synthetic) requires --out, is written with mode 0600 through an O_NOFOLLOW open and fchmod before any byte, and never goes to stdout. The git work-tree probing, the dubious-ownership case, the nargo file-name rule and the printed proving commands are gone; the README section and the CI guard say what remains: write it outside the repo, delete it after proving. The CI guard checks one bare witness: refused on stdout, 0600 under --out. google-fixture-witness.py holds the fixture key as constants N, E, D, the key the seeded generator produced, instead of regenerating it with a SHA-256 stream and Miller-Rabin; it checks that D inverts E mod N. The DIGEST provenance is stated once. identity_table.toml_array is the one Prover.toml array writer; the bearer-link script quotes every value, the Google script only strings, as before. All three committed Prover.toml regenerate byte-identical. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Every circuit takes its rules and tags from lib/identity's table, so build.sh copies it unconditionally instead of grepping each Nargo.toml for a libid_identity dependency. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
… order The consumers that move with these circuits belong in the PR and the release notes, not in the README. The contracts.ref merge order is four lines; the drift-check description is unchanged. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
table.nr now holds only the platform tags and rules the circuits compile against, and what build.sh ships as handles-table.nr (55 lines, from 1011). The vector tests are generated into table_tests.nr beside it, assert through node_halves against two Field literals, and build their witnesses with testing::padded from string literals. contracts.ref moves to the libID-contracts commit whose generator writes both files. CI compares table.nr with --compare-noir and table_tests.nr with --compare-noir-tests. The bytecode, the vks and the Solidity verifiers are byte-identical (vk_hash bearer-link-x 0x1f09866b..., bearer-link-github 0x1168344f..., oidc-google 0x2b2c5f9b...); the ACIR json's source hash moves with lib.nr. lib/identity still runs 82 tests. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
libid-rs' capture_ceremony writes no private witness, so nothing
produces a secret witness for this script to handle. It now accepts
only a session file marked "provenance": {"synthetic": true} and
refuses anything else with a one-line error; output goes to stdout or
--out as plain text.
Drop the secret-witness path with it: the 0600/O_NOFOLLOW writer, the
secret header and stdout refusal, the CI "Secret witness guard" step
and the README "Proving a real capture" section.
The committed bearer-link Prover.toml files regenerate byte-identical.
Assisted-by: Claude Opus 5.5
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
This was referenced Oct 9, 2026
Each comment states what the code does, with at most one line of reason. The design rationale stays in the PR description. Assisted-by: Claude Opus 5.5 Signed-off-by: SupremaLex <georglutsenko@gmail.com>
SupremaLex
marked this pull request as ready for review
October 9, 2026 14:56
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of the hashed-identities change: a claim puts no plaintext user id, handle or email on chain. The circuits open the committed id and handle, check them against the platform's rules, and output tagged SHA-256 nodes. Two per-platform bearer-link circuits replace the shared
bearer-link, andoidc-googleoutputs nodes in place of thesubdigest and the packed email. Companion PRs: libid-org/libID-rs#35 and libid-org/libID-contracts#87.What changes
bearer-link-xandbearer-link-githubEach circuit:
idNode = SHA256("libid.<p>.user-id" || id)andhandleNode = SHA256("libid.<p>.handle" || fold(handle)).The rules are compiled into each circuit, so a proof cannot cross platforms. The relation is written once, as
identity_linkinlib/identity; each main selects its platform's constants.Public inputs, 72 each:
idNodehandleNodeoidc-google"or\, so the email the circuit reads cannot run past its own JSON string into another member.libid.google.user-id. Thesubrefuses"and\.SHA256(aud), 34-35idNode, 36-37handleNode, 38exp, 39-56 modulus (18 limbs).lib/identityCommitment opening, the tagged hash,
node_halves, and the handle and id rules. Atestingmodule holds the test witness builders; no main calls it.Generated table
lib/identity/src/table.nrholds the platform tags and rules.table_tests.nrholds one test perhandles.jsonvector. Both are generated from libID-contracts'solidity/contracts/handles/handles.json.scripts/build.shships the table with every circuit ashandles-table.nr.contracts.refPins the libID-contracts commit the table must match. A new CI job, Handle table, fetches that commit and runs its
regen-identity-handles.py --compare-noirand--compare-noir-tests. It also checks the vendored identity-link fixtures and the Google fixture's digest against libID-contracts' session fixtures.Witnesses
scripts/identity-link-witness.pywrites a bearer-linkProver.tomlfrom a fixture marked"provenance": {"synthetic": true}, and refuses anything else.scripts/google-fixture-witness.pywrites the Google witness from a fixed test key, with a mixed-case email.Prover.tomland fails on a difference.Spec
oidc-google's public inputs do not match REQ-PLAT-16B's list: the id and handle nodes stand where its
userIddigest and rawemailare. The spec needs the matching update.Boundaries
handles.jsonin libID-contracts is the one source of rules and tags. This repo holds a generated copy, checked against it in CI.Measured
Gates from
bb gates; prove time is nativebb prove; verify gas is libID-contracts' forge gas snapshot,verifyas a call.0x1f09866b4c8feca602a2d394a5c8c924d3b8d964696f0214252ab97c9213775d0x1168344f46c63b1fa251c174f4b4d4e7c104cf65ab38d36fcbcf66108e6267700x2b2c5f9b3301f9ba7b6d69db7baaebc124b09959b7fb7ae87734ecb56667a488Consumers that must move
These circuits replace v0.6.0's ABIs.
bearer_link(two public inputs) becomesbearer_link_xandbearer_link_github.oidc_google'suser_id_hashandemail_packedbecomeid_nodeandhandle_node.ts/packages/ceremony) must commit the identity response's id and handle as their own ranges, as libid-rs does; pass their openings and nodes to the platform's bearer-link circuit; takeoidc_google's node inputs; and pin the first release that ships these circuits.handles.jsonatcontracts.refis the table they are built from.None of these circuits is released yet.
Privacy
The circuits never put the id, handle,
subor address on chain in plaintext. That is not secrecy: a node is an unsalted tagged SHA-256, so anyone can hash a guess and compare. In the bearer-link flows the byte lengths are public. GitHub ids are sequential, so a GitHub id node is in effect public.Breaking changes
bearer-linkandBearerLinkHonkVerifierare removed.bearer-link-x/BearerLinkXHonkVerifierandbearer-link-github/BearerLinkGithubHonkVerifierreplace them.Verification
Ran
nargo fmt --check, shellcheck,nargo test(lib/identity 82, bearer_link_x 8, bearer_link_github 5, oidc_google 12), witness regeneration, a full build andcheck-verifiers.sh. Each verifier accepts its witness's proof on anvil and rejects it with one bit flipped.scripts/build.shunder the pinned toolchain gives the gate counts and vk hashes above.Synthetic
ceremony_fixtures(handlesAlice_1,OctoCat); Google from a fixed test key.Not run
check-verifiers.shfails if port 8545 is in use, because its anvil binds HTTP beside the IPC socket.Before merge
contracts.ref(87f7a0d) is a commit on libID-contracts'feat/hashed-identitiesbranch. It must move to the merged commit on libID-contracts' main; the Handle table job fails once the branch is deleted.Open questions
[a-z0-9.+-_@], at most 62 bytes. Workspace addresses with an apostrophe or more than 62 bytes have no handle node and cannot bind. Widening later moves no existing node, but needs a new vk and verifier, and a longer cap costs gates. Accept this for the first release?Stack
#18 is stacked on this PR. It changes the bearer-link layout to 12 public inputs, caps the GitHub bearer at 47 bytes, and changes all three vks. The 72-input layout above does not ship on its own.