SeriesPart 7 of 9 // Go 1.24
GoWriting
Feb 9, 2025
8 min read
Security

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=1

When 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.

By Ajitem Sahasrabuddhe on February 9, 2025.

Series contents