This commit is contained in:
Peter D. Gray 2024-10-17 09:59:19 -04:00 committed by scgbckbone
parent b2f6e290d1
commit 3d4336bab7

View File

@ -2,14 +2,17 @@
How to support [RFC 6238](https://www.rfc-editor.org/rfc/rfc6238)
TOTP (Time based One Time Password) 2FA check, on our little embedded
device without a clock?
device without a real-time clock?
Solution: Store the preshared secret in the Colcard, and send that
Solution: Store the pre-shared secret in the Coldcard, and send that
securely to a trusted webserver which knows the time and can do a
fancy UX. The backend accepts the numeric code and reveals a secret
that can be used back on the Coldcard to authorize an action.
## History / Background
For the Mk4, the secret is 8 digit numeric code to be entered,
for the COLDCARD Q, it is a QR code to be scanned.
### History / Background
The HSM feature uses HOTP tokens, which do not require a backend,
but are not as robust as time-based tokens.
@ -22,40 +25,44 @@ but we may find other uses for it.
- Web backend has a ECC keypair, with pubkey known to CC firmware releases.
- Usual 2fa base32 secret is picked by CC and stored in CC (so that server is stateless)
- CC creates URL encrypted to the pubkey of server, containing args:
- shared secret
- the good nonce (16 bytes, or only 8 decimal digits for Mk4) to be provided back
- shared secret for TOTP (same value as held in user's phone)
- the response nonce (16 bytes, or 8 digits for Mk4) to be revealed to the user
on successful auth
- flag if Q model, so can provide a QR to be scanned in that case (rather than digits)
- some text label for what's being approved [presented to user so they can pick
correct 2fa shared secret]
- above is all encrypted in the URL.
- some text label for what's being approved, which is presented to user so they can pick
correct 2fa shared secret.
- above is all encrypted in transit, and only the server can decrypt
- user is sent to that encrypted URL using NFC tap on the Coldcard
- user arrives at server:
- shown label [which also indicates the server can be trusted, since only it could decrypt it]
- prompt for 6 digits from authenticator app
- does [RFC 6238](https://www.rfc-editor.org/rfc/rfc6238) 2FA check
- does [RFC 6238](https://www.rfc-editor.org/rfc/rfc6238) 2FA check using current time
- checks using current time and the shared secret provided by CC, fails if wrong.
- time based failure: offer retry (they typed too slow / minor clock drift)
- can offer to retry, but also do some rate limiting (only one attempt per 30-sec period)
- server will store very recent responses so attacker cannot get two codes
in any 30sec period (ie. blocks immediate reuse of same URL)
- if Q, show a QR code to be scanned, with the full nonce
- for non-Q system, a 8-digit decimal value is given: user has to enter that into the Coldcard
- web site shows instructions about what to do next on product.
- until a valid code is given, user is stuck here
- when valid token received:
- if Q, show a QR code to be scanned, with the full nonce
- for non-Q system, a 8-digit decimal value is given: user has to enter that into the Coldcard
- web site shows instructions about what to do next on product.
## From Coldcard PoV
- makes complex encrypted URL, waits for number back (or QR)
- makes complex encrypted URL, which contains a nonce it wants, waits for that nonce back (or QR)
- it's either the nonce from the URL, or fail
- if the good nonce, then we know the server knows our shared secret and we
are trusting it actually verify the 2FA token.
- if the right nonce, then we know the server knows the decryption key, and we
are trusting it actually verify the 2FA token properly.
## Encryption - Simple ECDH
- CC picks a secp256k1 keypair, generates compressed pubkey
- multiplies that by server's known public key, sha256(that coordinate) is the session key
- multiplies that private key by server's known public key
- apply sha256(resulting coordinate) => the session key
- apply AES-256-CTR over URL contents (ascii text)
- prepend 33 bytes of pubkey, and base64url encode the result
- prepend 33 bytes of pubkey, and base64url encode all of it
- full url is: `https://coldcard.com/2fa?{base64 encoded binary}`
## Trust Issues
@ -66,6 +73,8 @@ but we may find other uses for it.
- Only we can run the server, because the private key is company-secret.
- MiTM and network snoopers get nothing because HTTPS is used and only your browser
can see the nonce, and only after you've given the right digits.
- Coinkite server could skip the 2FA checks and just give you the answer
you want to type into the Coldcard. Again, you have to trust us on that.
## URL Format