Last active
January 11, 2025 01:02
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example output: