Skip to content
New issue

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

Feature: Consistent secret key from a seed #11

Closed
KEINOS opened this issue Nov 18, 2022 · 5 comments
Closed

Feature: Consistent secret key from a seed #11

KEINOS opened this issue Nov 18, 2022 · 5 comments

Comments

@KEINOS
Copy link
Owner

KEINOS commented Nov 18, 2022

Note: We decided not implement this. But consider ECDH to generate common secret as a secret key for TOTP.

Feature request

An option to generate a consistent secret key from a specified Seed.

opts := totp.Options{
  Issuer:      "Example.com",
  AccountName: "[email protected]",
  Algorithm:   totp.Algorithm("SHA1"),
  Period:      60,
  SecretSize:  20,
  Skew:        0,
  Digits:      totp.Digits(8),
  // If the Seed, Issuer and AccountName are the same, then the secret is the same.
  // If empty, cryptographically secure random secret is generated.
  Seed:        "mySecretSeed",
}

Reason

First of all, this feature request is not intended for those who have lost their TOTP secret key.

TOTP may have greater potential than simply being used for 2FA.

  • Example use cases
    1. Generate a consistent secret key from a private PGP key.
    2. TOTP passcode as a token between APIs.

Current workaround

Overwrite the secret after generation.

Ideas to implement

package main

import (
	"fmt"

	"github.com/zeebo/blake3"
)

func main() {
	SecretSize := 20
	Issuer := "Example.com"
	AccountName := "[email protected]"
	Seed := "mySecretSeed"

	// Hash the value with a blazingly fast cryptographic hash function
	hashByte := blake3.Sum512(
		[]byte(Issuer + AccountName + Seed),
	)

	// Map/compress the hash value to the required secret size
	secret := make([]byte, SecretSize)

	for index, data := range hashByte {
		ite := index % SecretSize
		secret[ite] ^= data
		// fmt.Println(secret, "->", data)
	}

	//fmt.Println("Hash:", hashByte)
	fmt.Printf("Final secret: %x", secret)

	// Output:
	// Final secret: 28152c20c5b6dd6c9a7b29b4f41ed997fc7170c1
}
@KEINOS KEINOS changed the title [Feature]: Consistent secret key from a seed Feature: Consistent secret key from a seed Nov 18, 2022
@KEINOS
Copy link
Owner Author

KEINOS commented Dec 27, 2022

Or generate a shared secret from the issuer and the account user's public and secret key using Diffie–Hellman key exchange.

@KEINOS
Copy link
Owner Author

KEINOS commented Mar 6, 2023

Or generate a shared secret from the issuer and the account user's public and secret key using Diffie–Hellman key exchange.

Currently, this implementation is redundant for a simple usage and takes time.

Let's implement generating a secret via BLAKE3 hash from a private key file first.

@KEINOS
Copy link
Owner Author

KEINOS commented Mar 6, 2023

We got a very useful information on DH key exchange to create a common secret.

$ # Create a publicly agreed parameter to share
$ openssl genpkey -genparam -algorithm DH -out param.pem

$ # Alice creates her public/private key pair
$ openssl genpkey -paramfile param.pem -out alice_priv.pem
$ openssl pkey -in alice_priv.pem -pubout -out alice_pub.pem

$ # Bob creates his public/private key pair
$ openssl genpkey -paramfile param.pem -out bob_priv.pem
$ openssl pkey -in bob_priv.pem -pubout -out bob_pub.pem

$ # Alice and Bob download each other's public key.
$ curl **snip**

$ # Alice generates common secret from her private key and Bob's public key
$ openssl pkeyutl -derive -inkey alice_priv.pem -peerkey bob_pub.pem -out shared_alice_bob.bin
$ base64 shared_alice_bob.bin > shared_alice_bob.txt

$ # Bob generates common secret from his private key and Alice's public key
$ openssl pkeyutl -derive -inkey bob_priv.pem -peerkey alice_pub.pem -out shared_bob_alice.bin
$ base64 shared_bob_alice.bin > shared_bob_alice.txt

$ # At this time, 'shared_alice_bob.txt' and 'shared_bob_alice.txt' are equivalent.

@KEINOS
Copy link
Owner Author

KEINOS commented Apr 26, 2024

We decided not implement this. But consider ECDH to generate common secret as a secret key for TOTP.

@KEINOS
Copy link
Owner Author

KEINOS commented Apr 27, 2024

JFYI

I you want a consistent secret key, just create a byte slice and overwrite the key.

totpKey, err := totp.GenerateKey("example.com", "Alice")
if err != nil {
	log.Fatal(err)
}

totpKey.Secret = totp.Secret(yourConsistentKey)
package main

import (
	"fmt"
	"log"

	"github.com/KEINOS/go-totp/totp"
)

func main() {
	// Generate TOTP key (with rand secret)
	Issuer := "Example.com"
	AccountName := "[email protected]"

	myKey, err := totp.GenerateKey(Issuer, AccountName)
	if err != nil {
		log.Fatal(err)
	}

	// Ensure the key size
	myConstKey := make([]byte, int(myKey.Options.SecretSize))
	myConstValue := []byte("foobar")

	copy(myConstKey, myConstValue)

	// Re-assign consistent value as TOTP secret
	myKey.Secret = totp.Secret(myConstKey[:])

	// Generate 6 digits passcode (valid for 30 seconds)
	passcode, err := myKey.PassCode()
	if err != nil {
		log.Fatal(err)
	}

	// Validate the passcode
	if myKey.Validate(passcode) {
		fmt.Println("Passcode is valid")
	}

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant