Skip to content

Instantly share code, notes, and snippets.

@brianonn
Last active January 11, 2025 01:02
Show Gist options
  • Save brianonn/45e80ec580a583ce68f9ae7969ed27a0 to your computer and use it in GitHub Desktop.
Save brianonn/45e80ec580a583ce68f9ae7969ed27a0 to your computer and use it in GitHub Desktop.
Create a 256-bit API key and secret key from random data using Blake2 hash and displayed in base32 and base56. Includes PBKDF2 for DB
package main
import (
"crypto/rand"
"encoding/base32"
"fmt"
"log"
"github.com/btcsuite/btcutil/base58"
"golang.org/x/crypto/blake2b"
)
const (
// input block bytesize for generating API keys.
// Must have at least as much entropy as desired in the output bit size
// entropy = inputBytes * 8
inputBytes = 256
// how many bits in the output API key
apiKeyBits = 256
)
func main() {
// Generate "inputSize" bytes of random data
randomData := make([]byte, inputBytes)
_, err := rand.Read(randomData)
if err != nil {
log.Fatalf("Failed to generate random data of length %d: %v", inputBytes, err)
}
// Compute a 256-bit BLAKE2 hash
hasher, err := blake2b.New256(nil) // nil = no key
if err != nil {
log.Fatalf("Failed to create BLAKE2b hasher: %v", err)
}
hasher.Write(randomData)
hash := hasher.Sum(nil) // hash is 256-bits (32 bytes)
// use as much of the hash as we need for
// generating the API key size that we want
hashByteSize := apiKeyBits / 8
hashBytes := hash[:hashByteSize]
// Convert the input hashBytes to Base32
base32Encoded := base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(hashBytes)
// Convert the input hashBytes to Base58
base58Encoded := base58.Encode(hashBytes)
fmt.Printf("Random Data (first 16 bytes): %x...\n", randomData[:16])
fmt.Printf("BLAKE2b Hash (256-bit): %x\n", hash)
fmt.Printf("Base32 Encoded: %s\n", base32Encoded)
fmt.Printf("Base58 Encoded: %s\n", base58Encoded)
}
package main
import (
"crypto/rand"
"crypto/sha256"
"encoding/base32"
"fmt"
"log"
"github.com/btcsuite/btcutil/base58"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/pbkdf2"
)
const (
// input block bytesize for generating API keys.
// Must have at least as much entropy as desired in the output
inputSizeBytes = 256
// how many bits in the output API key
apiKeyBits = 128
// how many bits in the output API Secret key
apiSecretKeyBits = 256
)
// get random data from the host PRNG or TRNG
func getRandomBytes(size int) []byte {
// Generate "size" bytes of random data
randomData := make([]byte, size)
_, err := rand.Read(randomData)
if err != nil {
log.Fatalf("Failed to generate random data of length %d: %v", size, err)
}
return randomData
}
// get a BLAKE2b-256 hash of input random data
func getBlake2bHash(input []byte, bits int) []byte {
// Compute a 256-bit BLAKE2 hash
hasher, err := blake2b.New256(nil) // nil = no key
if err != nil {
log.Fatalf("Failed to create BLAKE2b hasher: %v", err)
}
hasher.Write(input)
hash := hasher.Sum(nil) // hash is 256-bits (32 bytes)
// return the bytes we need for the requested number of bits
return hash[:(bits / 8)]
}
// convert byte slice to base32 and base58
func convertToBases(hash []byte) (string, string) {
// Convert the input to base32
b32 := base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(hash)
// Convert the input to base58
b58 := base58.Encode(hash)
return b32, b58
}
func printRandom(usePBKDF2 bool, bits int) {
randomData := getRandomBytes(inputSizeBytes)
hash := getBlake2bHash(randomData, bits)
// fmt.Printf(" Random Data (first 16 bytes): %x...\n", randomData[:16])
// fmt.Printf(" BLAKE2b Hash (256-bit): %x\n", hash)
// Convert to base32 and base58
base32Encoded, base58Encoded := convertToBases(hash)
fmt.Printf(" Base32 Encoded: %s\n", base32Encoded)
fmt.Printf(" Base58 Encoded: %s\n", base58Encoded)
if usePBKDF2 {
fmt.Printf("\nAPI SECRET PBKDF2 (%d bits) - STORE THIS AND THE PARAMETERS\n", bits)
// Derive a secret hash using PBKDF2 with SHA-256
hashfunc := sha256.New
iterations := 600_000 // 2024 recommendation
keylen := bits / 8 // size of key result we want
// Generate 32-byte random salt
salt := getRandomBytes(32)
hash = pbkdf2.Key(hash, salt, iterations, keylen, hashfunc)
// Convert to base32 and base58
base32Encoded, base58Encoded := convertToBases(hash)
_, b58salt := convertToBases(salt)
fmt.Printf(" Base32 Encoded KDF Hash: %s\n", base32Encoded)
fmt.Printf(" Base58 Encoded KDF Hash %s\n", base58Encoded)
fmt.Printf(" Base58 Encoded Salt: %s\n", b58salt)
fmt.Printf(" Iterations: %d\n", iterations)
fmt.Printf(" Keylen: %d\n", keylen)
fmt.Printf(" Hash Func: SHA-256\n")
}
}
func printRandomKey(bits int) {
printRandom(false, bits)
}
func printRandomKeyWithPBKDF2(bits int) {
printRandom(true, bits)
}
func main() {
fmt.Printf("API KEY (%d bits) - STORE THIS KEY\n", apiKeyBits)
printRandomKey(apiKeyBits)
fmt.Println()
fmt.Printf("API SECRET (%d bits) - DO NOT STORE THIS SECRET KEY\n", apiSecretKeyBits)
printRandomKeyWithPBKDF2(apiSecretKeyBits)
}
@brianonn
Copy link
Author

Example output:

API KEY (128 bits) - STORE THIS KEY
  Base32 Encoded: 7GWCXUCIZN7WKVTR7WPHJG7NRE
  Base58 Encoded: XqBEu4raYX9Jk48TmYZ99i

API SECRET (256 bits) - DO NOT STORE THIS SECRET KEY
  Base32 Encoded: JSPTIXL4E4Q3LD5RDH5RVLSVG4UW3YUQGHAIF3CDVQJUCEBAC7LA
  Base58 Encoded: 6A6ncFopsiejVMagjZnXamWEBXdoQAegvPRQSRk7Xdnd

API SECRET PBKDF2 (256 bits) - STORE THIS AND THE PARAMETERS
  Base32 Encoded KDF Hash: HVJ2COEBOJVCCDUJRSDCPGHQRJQ4VIHW755JGAMQQTOEMSSAZNFQ
  Base58 Encoded KDF Hash 58PqGNML9z2up3ZZaUFsR7EyZi8yiEdKCv8sheLHizD8
  Base58 Encoded Salt: EaTZamLKggP59CDPhFZnwutohnUHubtg5wRUzqBe8czY
  Iterations: 600000
  Keylen: 32
  Hash Func: SHA-256

@brianonn
Copy link
Author

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