Skip to content

Instantly share code, notes, and snippets.

@jamesob
Last active August 4, 2026 17:33
Show Gist options
  • Select an option

  • Save jamesob/ca9b4ca384969b4cfd62813419854d69 to your computer and use it in GitHub Desktop.

Select an option

Save jamesob/ca9b4ca384969b4cfd62813419854d69 to your computer and use it in GitHub Desktop.
switck == doc-hex

switck == doc-hex: proven by GPG commit signatures

Date: 2026-08-04 Status: verified

Question

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?

Answer

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.

Evidence

The key

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), fingerprint A004 C9BC E217 ABE9 341C D81A A2DC D558 C2BE 5D7C
    • UID: Peter D. Gray (Personal) <peter@conalgo.com>
    • UID: Peter Gray <peter@ripeapps.com>
  • 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.

Signature census, switck/libngu @ master (123 commits, 87 signed)

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.)

Temporal interleaving of the two identities under one key

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.

Corroborating (non-cryptographic) evidence

  • Coldcard production firmware depends on switck/libngu: Coldcard/firmware master .gitmodules pulls external/libngu from https://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 account peter-conalgo appears in old Coldcard submodule URLs (peter-conalgo/trezor-crypto in 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 switck merges (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 named git-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/

What the other identities in the log are

  • scgbckbone <scgbckbone@proton.me> — separate, real contributor (own key A940F7604E73741B, uid "scaag"). All recent commits (2023–2026, e.g. cf1988a 2026-05-25, the anchor in the URL under analysis) are authored by scgbckbone and unsigned; committer is Switck via 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.

Alternative hypotheses, considered

  • Rebase artifact ("doc-hex merely rebased/re-signed a real switck's commits"): ruled out on mechanism + four empirical tests (2026-08-04):
    1. 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-sign re-signs with the rebaser's key at rebase time. Signatures cannot "travel" across a rebase.
    2. 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.
    3. 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.
    4. 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.
    5. 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.
    6. 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.
  • 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.

Reproduction

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.

Sources

Open questions / follow-ups

  • 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/libngu currently 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.
@geldot

geldot commented Aug 4, 2026

Copy link
Copy Markdown

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment