Date: 2026-08-04 Status: verified
Is the GitHub alias switck (github.com/switck/libngu) actually doc-hex
(Peter D. Gray, Coinkite/Coldcard), on the basis of GPG signatures over git commits?
Yes — cryptographically proven. 58 commits authored as
Switck <69336248+switck@users.noreply.github.com> carry valid GPG signatures
made with doc-hex's personal key, the same key that signs doc-hex's
Peter D. Gray <peter@conalgo.com> commits in the same repo, interleaved in
time. The switck GitHub account has no GPG key of its own.
doc-hex's GitHub-published keyring (https://github.com/doc-hex.gpg) contains exactly one primary key:
- Primary:
A2DCD558C2BE5D7C(rsa2048, created 2013-05-28, expired 2023-05-28), fingerprintA004 C9BC E217 ABE9 341C D81A A2DC D558 C2BE 5D7C- UID:
Peter D. Gray (Personal) <peter@conalgo.com> - UID:
Peter Gray <peter@ripeapps.com>
- UID:
- Signing subkey:
F0E6CC6AFC16CF7B(rsa2048, same dates)
switck's GitHub keyring (https://github.com/switck.gpg) is empty
("This user hasn't uploaded any GPG keys"). GitHub therefore shows switck's
signed commits as unverifiable/unattributed — but the signatures are in the
git objects and verify fine against doc-hex's public key.
Verified with git verify-commit against an isolated keyring holding
doc-hex's, scgbckbone's, and GitHub web-flow's public keys:
| Signing key | Key belongs to | Commit author identity | Count | Result |
|---|---|---|---|---|
F0E6CC6AFC16CF7B |
doc-hex (subkey) | Switck | 56 | valid sig (key expired) |
A2DCD558C2BE5D7C |
doc-hex (primary, used directly) | Switck | 2 | valid sig (key expired) |
F0E6CC6AFC16CF7B |
doc-hex (subkey) | Peter D. Gray | 19 | valid sig (key expired) |
4AEE18F83AFDEB23 |
GitHub web-flow (web UI merges) | Switck | 8 | valid (GitHub-signed, uninformative) |
B5690EEEBB952194 |
GitHub web key | scgbckbone | 1 | valid (GitHub-signed, uninformative) |
A940F7604E73741B |
scgbckbone (scaag <scgbckbone@proton.me>) |
scgbckbone | 1 | valid |
58 Switck-authored commits are signed by doc-hex's personal key (56 via the
signing subkey, 2 — 999bdb41, f7711c5, 2024-03-30 — by the primary key
directly, i.e. he signed with the master key itself). Example verification:
$ git verify-commit 28aa2927d5f7
gpg: Signature made Thu 29 Sep 2022 09:31:09 AM EDT
gpg: using RSA key D9766C79E77B0198D66975BDF0E6CC6AFC16CF7B
gpg: Good signature from "Peter D. Gray (Personal) <peter@conalgo.com>" [expired]
$ git log -1 --format='%an <%ae>' 28aa2927d5f7
Switck <69336248+switck@users.noreply.github.com>
(The key's 2023-05-28 expiry is why a bare git log shows status E and why
git verify-commit exits non-zero; the signatures are cryptographically good —
gpg reports Good signature — and only the private-key holder could produce
them, including the two 2024 commits signed after expiry.)
Commits signed by subkey F0E6CC6AFC16CF7B, grouped by month:
Switck 2020-10 (14) <- repo created 2020-10-15, "Initial commit" d4922f6
Switck 2021-01 (15)
Peter D. Gray 2021-02 (5)
Switck 2021-02 (23) <- same key, same month, both names
Peter D. Gray 2021-03 (6)
Peter D. Gray 2021-07 (3)
Peter D. Gray 2021-09 (3)
Peter D. Gray 2021-10 (1)
Peter D. Gray 2022-03 (1)
Switck 2022-09 (4)
Both names were used interchangeably, signed by one key, within the same weeks.
- Coldcard production firmware depends on switck/libngu:
Coldcard/firmwaremaster.gitmodulespullsexternal/libngufromhttps://github.com/switck/libngu.git. Coinkite ships hardware wallets whose crypto library lives under the switck account. https://github.com/Coldcard/firmware/blob/master/.gitmodules - doc-hex == Coinkite == Peter D. Gray: GitHub profile lists "Coinkite
Inc.", coinkite.com, Toronto; key UID is
peter@conalgo.com. doc-hex's older accountpeter-conalgoappears in old Coldcard submodule URLs (peter-conalgo/trezor-cryptoin switck/firmware's.gitmodules). https://github.com/doc-hex - doc-hex maintains switck/libngu in public: opens issues ("AES is slow"
#12), answers issues as maintainer (#8: "I'll see if I can fix this import
issues and give a PR"), pushes PR commits that
switckmerges (PR #17, "switck merged 4 commits ... doc-hex added 4 commits"). switck/libngu#12, switck/libngu#17 - switck account profile: created 2020-08-07 (user id 69336248), name
"Switck", company "NaN", location "Cyberspace", bio
mx@switck.com; 5 repos: forks of Coldcard/firmware, Coldcard-adjacent bech32 + micropython, plus two originals (libngu, pandora-shar). The account exists solely inside the Coldcard/Coinkite ecosystem. https://api.github.com/users/switck - Merge commit
f7711c5(signed by doc-hex primary key) message: "Merge branch 'master' of git-switck:switck/libngu" — the local ssh remote alias on the signing machine is namedgit-switck, i.e. one person operating the switck remote from doc-hex's key-holding machine. - Third-party write-up treats switck/libngu as a Coldcard project component: Wizardsardine's Coldcard RNG-vuln analysis: "Three public repositories are involved: the firmware itself, Coldcard/firmware; the cryptographic library it calls into, switck/libngu; and the MicroPython fork it runs on, Coldcard/micropython." https://wizardsardine.com/blog/coldcard-rng-vulnerability/
scgbckbone <scgbckbone@proton.me>— separate, real contributor (own keyA940F7604E73741B, uid "scaag"). All recent commits (2023–2026, e.g.cf1988a2026-05-25, the anchor in the URL under analysis) are authored by scgbckbone and unsigned; committer isSwitckvia GitHub noreply, i.e. rebases/merges done through the switck account. Not part of the identity question.avirgovi <avirgovi@cisco.com>— 2 unsigned commits, 2022-04. Unrelated.
- Rebase artifact ("doc-hex merely rebased/re-signed a real switck's
commits"): ruled out on mechanism + four empirical tests (2026-08-04):
- Mechanism: a commit signature covers the whole commit object (tree,
parents, author, committer, timestamps, message). Rebase changes
parents/committer/time → old signatures are destroyed, never carried
over;
rebase --gpg-signre-signs with the rebaser's key at rebase time. Signatures cannot "travel" across a rebase. - Committer field: rebase rewrites committer to the rebaser's configured
identity. All 58 signed Switck commits have committer =
Switck <69336248+switck@users.noreply.github.com>, never Peter D. Gray. For doc-hex to have produced these by rebasing, his git would have to be configured as Switck — which is the alias itself, not an alternative to it. - Author-date vs commit-date: rebase preserves author date and stamps a new commit date. All 77 doc-hex-key-signed commits (58 Switck + 19 Peter) have author timestamp == commit timestamp (delta 0s for 76/77, 34s for one amend). No rebase happened on any signed commit.
- Signature timestamps: GPG signature time == commit time to the second on all sampled commits spanning 2020-10-15 → 2024-03-30 (e.g. d4922f6 both 2020-10-15 09:34:17 -0400; f7711c5 commit 12:05:23, sig 12:05:26). A batch re-sign at rebase time would cluster all sig timestamps at one moment; instead each sig is contemporaneous with its own commit across 3.5 years.
- Internal contrast set: the repo contains real rebases — 29/34 unsigned scgbckbone-authored commits (committer=Switck) have author/commit date gaps >1h (up to 8 months, e.g. 65343de authored 2025-07-11, committed 2026-03-12) — and they are UNSIGNED. History shows what rebasing looks like in this repo; the signed Switck commits match none of it.
- No patch-id duplicates among all 123 commits (no re-signing-batch residue). Residual escape hatch ("doc-hex rebased a real switck's work while impersonating the switck identity and re-signing each commit within seconds of its original author date, for 4 years") is self-erasing: it requires doc-hex to operate the switck identity continuously, which is the conclusion, and it explains the data less simply.
- Mechanism: a commit signature covers the whole commit object (tree,
parents, author, committer, timestamps, message). Rebase changes
parents/committer/time → old signatures are destroyed, never carried
over;
- Key sharing between two people: doc-hex could theoretically have given another person his private key. Nothing supports this: the key UID is personal ("Peter D. Gray (Personal)"), it is the only key on doc-hex's GitHub account since long before switck existed, switck published no key of its own, and the repo is production infrastructure for doc-hex's employer's flagship product. Occam: one person, two aliases.
- Forged signatures: infeasible — signatures verify against a key published on doc-hex's GitHub since before the switck account was created; forging would require the private key (which collapses to the key-sharing case).
- Why the alias exists at all (inference, not sourced): libngu is Coldcard's crypto library; hosting it under a pseudonymous personal account rather than the Coldcard org keeps it at arm's length from the corporate brand while the same developer (Gray) maintains it. Motive is speculation; identity is not.
git clone https://github.com/switck/libngu && cd libngu
export GNUPGHOME=$(mktemp -d)
curl -sL https://github.com/doc-hex.gpg | gpg --import
git log --format='%H|%an|%ae|%G?|%GK' # census
git verify-commit 28aa2927d5f7208c5e124d141f4a85e0425ac9e3 # Switck-authored, doc-hex key
git verify-commit f7711c5522f432e9827fabe3c5c1366068485cac # Switck-authored, doc-hex PRIMARY key
curl -s https://github.com/switck.gpg # "hasn't uploaded any GPG keys"Note: git verify-commit exits non-zero on these because the key is expired
(gpg EXPKEYSIG); check for "Good signature" in output rather than exit code.
- https://github.com/switck/libngu — repo under analysis (commit graph, signatures)
- https://github.com/doc-hex.gpg — doc-hex's published key: primary A2DCD558C2BE5D7C + subkey F0E6CC6AFC16CF7B, UID Peter D. Gray peter@conalgo.com
- https://github.com/switck.gpg — empty keyring for switck
- https://api.github.com/users/switck — account created 2020-08-07, id 69336248
- https://api.github.com/users/doc-hex — "Coinkite Inc.", coinkite.com, Toronto
- https://github.com/Coldcard/firmware/blob/master/.gitmodules — production submodule → switck/libngu
- switck/libngu#12, switck/libngu#8, switck/libngu#17 — doc-hex acting as libngu maintainer
- https://wizardsardine.com/blog/coldcard-rng-vulnerability/ — third party treats switck/libngu as Coldcard's crypto library
- switck.com domain registration history (bio: mx@switck.com) — likely privacy-shielded; not checked.
- Whether the same doc-hex key signs commits in
switck/pandora-shar(other original switck repo) — not checked here. - Wayback Machine history of libngu's repo home (was it ever under doc-hex/ or coinkite/?
doc-hex/libngucurrently 404s). - Search divergence note: Kagi surfaced GitHub-internal pages (issues/PRs); Yandex surfaced the Wizardsardine analysis + noise. No substantive disagreement between engines on this topic.
Great find. This also leads to a plausible trigger hypothesis: On 7 July 2026, three weeks before the vulnerable wallets were drained, @audrism published https://arxiv.org/abs/2607.06194, announcing a dataset enabling PGP-signature identity resolution over the global commit corpus. The dataset maps signed Git commits to signing keys and author identities across GitHub.
If @doc-hex / @switck became aware that @audrism's dataset existed, he would have known the clock was ticking and that key-to-author mapping would soon expose that the CTO was self-reviewing and interacting on social media and GitHub with his own sock puppet, which is the account that introduced the vulnerability.
The paper mentions that the dataset will be published soon but doesn't give a specific release date. If this hypothesis holds, the attacker had some three weeks of preparation and enumeration time.