We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ECDH can establish a shared secret between two asymmetric key pairs. (key agreement.)
We can use this ECDH shared secret as a secret key of TOTP to obtain common time-based ephemeral value between two parties.
A value that can be used as; a common SALT for hashing, cryptographic key for symmetric-key encryption and etc.
Here's a working pseudo code:
package main import ( "fmt" "log" "crypto/ecdh" "crypto/rand" "github.com/KEINOS/go-totp/totp" "github.com/zeebo/blake3" ) func main() { // ----------------------------------------------------------------------------- // Generate ECDH key pair using curve25519 (X25519) // ----------------------------------------------------------------------------- // Agreed curve to use between Alice and Bob paramCommon := ecdh.X25519() // Alice ECDH key pair alicePrivKey, alicePubKey, err := newECDH(paramCommon) panicOnErr(err) // Bob ECDH key pair bobPrivKey, bobPubKey, err := newECDH(paramCommon) panicOnErr(err) fmt.Printf("Alice Priv: %x\n", alicePrivKey.Bytes()) fmt.Printf("Alice Pub : %x\n", alicePubKey.Bytes()) fmt.Printf("Bob Priv : %x\n", bobPrivKey.Bytes()) fmt.Printf("Bob Pub : %x\n", bobPubKey.Bytes()) // ----------------------------------------------------------------------------- // Calculate shared secret // ----------------------------------------------------------------------------- // (we assume that public keys were safely exchanged/retrieved and trustworthy) // Alice shared secret aliceSecret, err := alicePrivKey.ECDH(bobPubKey) panicOnErr(err) // Bob shared secret bobSecret, err := bobPrivKey.ECDH(alicePubKey) panicOnErr(err) fmt.Printf("Alice's Shared Secret: %x (len: %v)\n", aliceSecret, len(aliceSecret)) fmt.Printf("Bob's Shared Secret : %x (len: %v)\n", bobSecret, len(bobSecret)) // ----------------------------------------------------------------------------- // Hash the ECDH shared secret to 128 byte length using constant context as // salt and use it as the secret of TOTP key // ----------------------------------------------------------------------------- const ctxSalt = "example.com shared TOTP secret v1" // Alice TOTP secret key aliceTOTPSec := hash1024(aliceSecret, ctxSalt) // Bob TOTP secret key bobTOTPSec := hash1024(aliceSecret, ctxSalt) fmt.Printf("Alice TOTP sec key: %x...%x (len: %v)\n", aliceTOTPSec[:16], aliceTOTPSec[112:], len(aliceTOTPSec)) fmt.Printf("Bob TOTP sec key : %x...%x (len: %v)\n", bobTOTPSec[:16], bobTOTPSec[112:], len(bobTOTPSec)) // ----------------------------------------------------------------------------- // Instanticate totp.Key object using the converted TOTP secret key above // ----------------------------------------------------------------------------- // Alice TOTP key aliceKey, err := newTOTP("Example.com", "[email protected]", aliceTOTPSec) panicOnErr(err) // Bob TOTP key bobKey, err := newTOTP("Example.com", "[email protected]", bobTOTPSec) panicOnErr(err) // ----------------------------------------------------------------------------- // Generate TOTP pass code // ----------------------------------------------------------------------------- // Alice generates 6 digits passcode (valid for 30 seconds) alicePass, err := aliceKey.PassCode() panicOnErr(err) // Bob generates 6 digits passcode (valid for 30 seconds) bobPass, err := bobKey.PassCode() panicOnErr(err) fmt.Println("Alice pass:", alicePass) fmt.Println("Bob pass :", bobPass) // ----------------------------------------------------------------------------- // Validate CODE between alice and bob // ----------------------------------------------------------------------------- // Alice validates the passcode from Bob if aliceKey.Validate(bobPass) { fmt.Println("Alice: Passcode is valid") } // Bob validates the passcode from Alice if bobKey.Validate(alicePass) { fmt.Println("Bob : Passcode is valid") } // Output: // Alice Priv: 07b0ff9dc25884eb20af588269697001f9a580f96a7d318f21e0aa466f22da0a // Alice Pub : bbf54e526eb5ffd40e2e4791b2715eef8f51fd8f1817c9b7a1d9739a038b0647 // Bob Priv : 418c8e9102f8d695bec35be99f53eda380f9d7990a5447bc66dff87226135c16 // Bob Pub : 0b0f1cccc640e8ef08a0043b37d3e8f7a4fc4d027ccb433b053cb18b8f43bc66 // Alice's Shared Secret: b9c22821ff75038ffa4fa2dddfc4af06c3797df7b92f87ade36952c515dab424 (len: 32) // Bob's Shared Secret : b9c22821ff75038ffa4fa2dddfc4af06c3797df7b92f87ade36952c515dab424 (len: 32) // Alice TOTP sec key: 5fad6143564261a75e9cedeeb7b9f653...790e6bcf035d552c2817ed60dd9440a5 (len: 128) // Bob TOTP sec key : 5fad6143564261a75e9cedeeb7b9f653...790e6bcf035d552c2817ed60dd9440a5 (len: 128) // Alice pass: 49641015 // Bob pass : 49641015 // Alice: Passcode is valid // Bob : Passcode is valid } func newECDH(curveType ecdh.Curve) (*ecdh.PrivateKey, *ecdh.PublicKey, error) { privKey, err := curveType.GenerateKey(rand.Reader) if err != nil { return nil, nil, err } pubKey := privKey.PublicKey() return privKey, pubKey, nil } // hash1024 hashes totpSec with ctx to 128 byte (1024 bit) length. // // ctx (context strings) must be hardcoded constants, and the recommended format // is "[application] [commit timestamp] [purpose]", // // e.g., "example.com 2019-12-25 16:18:03 session tokens v1". func hash1024(totpSec []byte, ctx string) []byte { const outLen = 128 outHash := make([]byte, outLen) blake3.DeriveKey( ctx, // context totpSec, // material outHash, ) return outHash } func newTOTP(issuer, accountName string, totpSec []byte) (*totp.Key, error) { totpKey, err := totp.GenerateKeyCustom(totp.Options{ Issuer: issuer, AccountName: accountName, Algorithm: totp.Algorithm("SHA256"), // use SHA256 for HMAC Period: 30, // valid for 30 sec SecretSize: 128, // len TOTP key Skew: 0, // ± 0 time tolerance Digits: totp.DigitsEight, // 8 digits pass code }) if err != nil { return nil, err } totpKey.Secret = totp.Secret(totpSec) return totpKey, nil } func panicOnErr(err error) { if err != nil { log.Fatal(err) } }
View it online @ Go Playground
Related issue: Feature: Consistent secret key from a seed #11
The text was updated successfully, but these errors were encountered:
feat: #37 ECDH support for TOTP secret creation
75564c7
We can not implement ECDH unless we bump-up our go.mod Go version higher than 1.20.
https://github.com/KEINOS/go-totp/actions/runs/8887607444/job/24403155510#step:9:11
Sorry, something went wrong.
9383003
No branches or pull requests
ECDH can establish a shared secret between two asymmetric key pairs. (key agreement.)
We can use this ECDH shared secret as a secret key of TOTP to obtain common time-based ephemeral value between two parties.
A value that can be used as; a common SALT for hashing, cryptographic key for symmetric-key encryption and etc.
Here's a working pseudo code:
View it online @ Go Playground
Related issue: Feature: Consistent secret key from a seed #11
The text was updated successfully, but these errors were encountered: