Skip to content

Instantly share code, notes, and snippets.

@RichardSlater
Created April 2, 2025 09:14
Show Gist options
  • Save RichardSlater/34a8b641ef12006ce69004ef8a03688d to your computer and use it in GitHub Desktop.
Save RichardSlater/34a8b641ef12006ce69004ef8a03688d to your computer and use it in GitHub Desktop.
Post-Quantum Block Ciphers in Go
package main
import (
"log"
"crypto/mlkem"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"io"
"crypto/rand"
"fmt"
)
func main() {
alice := Person{name: "Alice"}
encapsulationKey,err := alice.GenerateKey()
if err != nil {
log.Fatal(err)
}
bob := Person{name: "Bob"}
ciphertext,err := bob.Encapsulate(encapsulationKey)
if err != nil {
log.Fatal(err)
}
err = alice.Decapsulate(ciphertext)
if err != nil {
log.Fatal(err)
}
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)
}
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(cypertext []byte) error {
dk, err := mlkem.NewDecapsulationKey768(p.dk)
if err != nil {
return err
}
sharedSecret,err := dk.Decapsulate(cypertext)
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
}
type Person struct {
name string // Name of the person.
dk []byte // The person's decapsulation key. This is only set if the person calls GenerateKey.
sharedSecret []byte // The shared secret between the person and another person.
}
// cite: https://medium.com/@ajitem/cryptographic-enhancements-in-go-1-24-post-quantum-readiness-more-5b60374f8b8f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment