Cryptographic Enhancements in Go 1.24: Post-Quantum Readiness & More
Go 1.24 introduces significant improvements to its cryptographic capabilities, including support for post-quantum cryptography, new hashing…
Go 1.24 introduces significant improvements to its cryptographic capabilities, including support for post-quantum cryptography, new hashing…
Cryptographic Enhancements in Go 1.24: Post-Quantum Readiness & More
Go 1.24 introduces significant improvements to its cryptographic capabilities, including support for post-quantum cryptography, new hashing and key derivation functions, and enhancements for FIPS 140–3 compliance. These updates strengthen Go’s security posture and future-proof applications against emerging threats.
Post-Quantum Cryptography: crypto/mlkem
One of the most notable enhancements in Go 1.24 is the crypto/mlkem package, which provides ML-KEM-768 and ML-KEM-1024 post-quantum key encapsulation mechanisms based on the Kyber algorithm — recently standardized in FIPS 203.
Below is an example demonstrating how to use these new features. The code defines a Person struct that stores a decapsulation key (dk) and a sharedSecret:
type Person struct {
name string
dk []byte
sharedSecret []byte
}
func (p *Person) GenerateKey() ([]byte, error) {
dk, err := mlkem.GenerateKey768()
if err != nil {
return nil, err
}
p.dk = dk.Bytes()
return dk.EncapsulationKey().Bytes(), nil
}
func (p *Person) Encapsulate(encapsulationKey []byte) ([]byte, error) {
ek, err := mlkem.NewEncapsulationKey768(encapsulationKey)
if err != nil {
log.Fatal(err)
}
sharedSecret, ciphertext := ek.Encapsulate()
p.sharedSecret = sharedSecret
return ciphertext, nil
}
func (p *Person) Decapsulate(ciphertext []byte) error {
dk, err := mlkem.NewDecapsulationKey768(p.dk)
if err != nil {
return err
}
sharedSecret, err := dk.Decapsulate(ciphertext)
if err != nil {
return err
}
p.sharedSecret = sharedSecret
return nil
}
func (p *Person) Encrypt(plaintext string) (string, error) {
block, err := aes.NewCipher(p.sharedSecret)
if err != nil {
return "", err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
nonce := make([]byte, gcm.NonceSize())
_, err = io.ReadFull(rand.Reader, nonce)
if err != nil {
return "", err
}
ciphertext := gcm.Seal(nil, nonce, []byte(plaintext), nil)
return base64.RawStdEncoding.EncodeToString(append(nonce, ciphertext...)), nil
}
func (p *Person) Decrypt(ciphertext string) (string, error) {
block, err := aes.NewCipher(p.sharedSecret)
if err != nil {
return "", err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
cipherTextBytes, err := base64.RawStdEncoding.DecodeString(ciphertext)
if err != nil {
return "", err
}
nonceSize := gcm.NonceSize()
nonce, cipherTextBytes := cipherTextBytes[:nonceSize], cipherTextBytes[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, cipherTextBytes, nil)
if err != nil {
return "", err
}
return string(plaintext), nil
}It includes methods for generating a key (GenerateKey), encapsulating a key to produce a ciphertext (Encapsulate), decapsulating the ciphertext (Decapsulate), and encrypting / decrypting messages with AES-GCM (Encrypt / Decrypt).
A Story of Secure Communication
Imagine Alice and Bob, two individuals who want to securely exchange messages. They decide to use ML-KEM-768 to establish a shared secret over an untrusted network.
1. Alice Generates Her Key Pair
Alice generates a decapsulation key (private key) and an encapsulation key (public key). She then shares her encapsulation key with Bob.
alice := Person{name: "Alice"}
encapsulationKey, err := alice.GenerateKey()
if err != nil {
log.Fatal(err)
}2. Bob Uses the Key to Encrypt a Shared Secret
Bob receives Alice’s encapsulation key and uses it to generate a shared secret along with an encrypted ciphertext.
bob := Person{name: "Bob"}
ciphertext, err := bob.Encapsulate(encapsulationKey)
if err != nil {
log.Fatal(err)
}3. Alice Decapsulates the Shared Secret
Alice takes the ciphertext from Bob and decapsulates it using her private key, retrieving the shared secret.
err = alice.Decapsulate(ciphertext)
if err != nil {
log.Fatal(err)
}4. Encrypting & Decrypting Messages
Now that both Alice and Bob share the same secret, they can securely exchange encrypted messages.
encrypted, err := alice.Encrypt("Hello, Bob!")
if err != nil {
log.Fatal(err)
}
decrypted, err := bob.Decrypt(encrypted)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Decrypted message: %s\n", decrypted) // Output: Hello, Bob!This demonstrates how ML-KEM-768 can be used for secure post-quantum key exchange.
New Cryptographic Functions
1. crypto/hkdf: Key Derivation Function
Go 1.24 introduces crypto/hkdf, implementing HMAC-based Extract-and-Expand Key Derivation Function (HKDF), as defined in RFC 5869.
package main
import (
"crypto/hkdf"
"crypto/rand"
"crypto/sha3"
"fmt"
"log"
)
func main() {
hash := sha3.New256
keyLen := hash().Size()
secret := make([]byte, 128)
if _, err := rand.Read(secret); err != nil {
log.Fatalln(err)
}
salt := make([]byte, hash().Size())
if _, err := rand.Read(salt); err != nil {
log.Fatalln(err)
}
info := "seed for encryption"
key1, err := hkdf.Key(hash, secret, salt, info, keyLen)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("Key 1: %x\n", key1)
psKey, err := hkdf.Extract(hash, secret, salt)
if err != nil {
log.Fatalln(err)
}
key2, err := hkdf.Expand(hash, psKey, info, keyLen)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("Key 2: %x\n", key2)
fmt.Println("Keys are equal:", string(key1) == string(key2))
}2. crypto/pbkdf2: Password-Based Key Derivation
PBKDF2 (Password-Based Key Derivation Function 2) is now available as crypto/pbkdf2, based on RFC 8018.
package main
import (
"crypto/pbkdf2"
"crypto/rand"
"crypto/sha3"
"fmt"
"log"
)
func main() {
hash := sha3.New256
keyLen := hash().Size()
salt := make([]byte, 16)
if _, err := rand.Read(salt); err != nil {
log.Fatalln(err)
}
key, err := pbkdf2.Key(hash, "super-secure-password", []byte("salt"), 4096, keyLen)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("Derived key: %x\n", key)
}3. crypto/sha3: SHA-3 Hashing Support
Go 1.24 adds a built-in crypto/sha3 package, implementing SHA-3 and extendable-output functions (XOFs) as defined in FIPS 202.
package main
import (
"crypto/sha3"
"fmt"
)
func main() {
hash := sha3.Sum256([]byte("Hello, World!"))
fmt.Printf("SHA3-256: %x\n", hash)
}FIPS 140–3 Compliance in Go 1.24
FIPS 140–3 (Federal Information Processing Standard) compliance is a requirement for cryptographic modules used in government and regulated industries. Go 1.24 introduces a FIPS-compliant cryptographic module, which can be enabled via the GOFIPS140 environment variable.
export GOFIPS140=1When enabled, Go will automatically select FIPS-approved cryptographic algorithms for secure communication.
Conclusion
The cryptographic enhancements in Go 1.24 provide quantum-resistant security, improved key derivation methods, and FIPS 140–3 compliance. These features make Go a stronger choice for building secure applications in an evolving cryptographic landscape.
Stay tuned for the next post, where we explore the experimental testing/synctest package for advanced concurrency testing.