package mlkem
import (
"crypto/internal/fips140"
"crypto/internal/fips140/drbg"
"crypto/internal/fips140/sha3"
"crypto/internal/fips140/subtle"
"errors"
)
const (
n = 256
q = 3329
encodingSize12 = n * 12 / 8
encodingSize11 = n * 11 / 8
encodingSize10 = n * 10 / 8
encodingSize5 = n * 5 / 8
encodingSize4 = n * 4 / 8
encodingSize1 = n * 1 / 8
messageSize = encodingSize1
SharedKeySize = 32
SeedSize = 32 + 32
)
const (
k = 3
CiphertextSize768 = k *encodingSize10 + encodingSize4
EncapsulationKeySize768 = k *encodingSize12 + 32
)
const (
k1024 = 4
CiphertextSize1024 = k1024 *encodingSize11 + encodingSize5
EncapsulationKeySize1024 = k1024 *encodingSize12 + 32
)
type DecapsulationKey768 struct {
d [32 ]byte
z [32 ]byte
ρ [32 ]byte
h [32 ]byte
encryptionKey
decryptionKey
}
func (dk *DecapsulationKey768 ) Bytes () []byte {
var b [SeedSize ]byte
copy (b [:], dk .d [:])
copy (b [32 :], dk .z [:])
return b [:]
}
func (dk *DecapsulationKey768 ) EncapsulationKey () *EncapsulationKey768 {
return &EncapsulationKey768 {
ρ : dk .ρ ,
h : dk .h ,
encryptionKey : dk .encryptionKey ,
}
}
type EncapsulationKey768 struct {
ρ [32 ]byte
h [32 ]byte
encryptionKey
}
func (ek *EncapsulationKey768 ) Bytes () []byte {
b := make ([]byte , 0 , EncapsulationKeySize768 )
return ek .bytes (b )
}
func (ek *EncapsulationKey768 ) bytes (b []byte ) []byte {
for i := range ek .t {
b = polyByteEncode (b , ek .t [i ])
}
b = append (b , ek .ρ [:]...)
return b
}
type encryptionKey struct {
t [k ]nttElement
a [k * k ]nttElement
}
type decryptionKey struct {
s [k ]nttElement
}
func GenerateKey768 () (*DecapsulationKey768 , error ) {
dk := &DecapsulationKey768 {}
return generateKey (dk )
}
func generateKey(dk *DecapsulationKey768 ) (*DecapsulationKey768 , error ) {
var d [32 ]byte
drbg .Read (d [:])
var z [32 ]byte
drbg .Read (z [:])
kemKeyGen (dk , &d , &z )
if err := fips140 .PCT ("ML-KEM PCT" , func () error { return kemPCT (dk ) }); err != nil {
panic (err )
}
fips140 .RecordApproved ()
return dk , nil
}
func GenerateKeyInternal768 (d , z *[32 ]byte ) *DecapsulationKey768 {
dk := &DecapsulationKey768 {}
kemKeyGen (dk , d , z )
return dk
}
func NewDecapsulationKey768 (seed []byte ) (*DecapsulationKey768 , error ) {
dk := &DecapsulationKey768 {}
return newKeyFromSeed (dk , seed )
}
func newKeyFromSeed(dk *DecapsulationKey768 , seed []byte ) (*DecapsulationKey768 , error ) {
if len (seed ) != SeedSize {
return nil , errors .New ("mlkem: invalid seed length" )
}
d := (*[32 ]byte )(seed [:32 ])
z := (*[32 ]byte )(seed [32 :])
kemKeyGen (dk , d , z )
if err := fips140 .PCT ("ML-KEM PCT" , func () error { return kemPCT (dk ) }); err != nil {
panic (err )
}
fips140 .RecordApproved ()
return dk , nil
}
func kemKeyGen(dk *DecapsulationKey768 , d , z *[32 ]byte ) {
dk .d = *d
dk .z = *z
g := sha3 .New512 ()
g .Write (d [:])
g .Write ([]byte {k })
G := g .Sum (make ([]byte , 0 , 64 ))
ρ , σ := G [:32 ], G [32 :]
dk .ρ = [32 ]byte (ρ )
A := &dk .a
for i := byte (0 ); i < k ; i ++ {
for j := byte (0 ); j < k ; j ++ {
A [i *k +j ] = sampleNTT (ρ , j , i )
}
}
var N byte
s := &dk .s
for i := range s {
s [i ] = ntt (samplePolyCBD (σ , N ))
N ++
}
e := make ([]nttElement , k )
for i := range e {
e [i ] = ntt (samplePolyCBD (σ , N ))
N ++
}
t := &dk .t
for i := range t {
t [i ] = e [i ]
for j := range s {
t [i ] = polyAdd (t [i ], nttMul (A [i *k +j ], s [j ]))
}
}
H := sha3 .New256 ()
ek := dk .EncapsulationKey ().Bytes ()
H .Write (ek )
H .Sum (dk .h [:0 ])
}
func kemPCT(dk *DecapsulationKey768 ) error {
ek := dk .EncapsulationKey ()
c , K := ek .Encapsulate ()
K1 , err := dk .Decapsulate (c )
if err != nil {
return err
}
if subtle .ConstantTimeCompare (K , K1 ) != 1 {
return errors .New ("mlkem: PCT failed" )
}
return nil
}
func (ek *EncapsulationKey768 ) Encapsulate () (ciphertext , sharedKey []byte ) {
var cc [CiphertextSize768 ]byte
return ek .encapsulate (&cc )
}
func (ek *EncapsulationKey768 ) encapsulate (cc *[CiphertextSize768 ]byte ) (ciphertext , sharedKey []byte ) {
var m [messageSize ]byte
drbg .Read (m [:])
fips140 .RecordApproved ()
return kemEncaps (cc , ek , &m )
}
func (ek *EncapsulationKey768 ) EncapsulateInternal (m *[32 ]byte ) (ciphertext , sharedKey []byte ) {
cc := &[CiphertextSize768 ]byte {}
return kemEncaps (cc , ek , m )
}
func kemEncaps(cc *[CiphertextSize768 ]byte , ek *EncapsulationKey768 , m *[messageSize ]byte ) (c , K []byte ) {
g := sha3 .New512 ()
g .Write (m [:])
g .Write (ek .h [:])
G := g .Sum (nil )
K , r := G [:SharedKeySize ], G [SharedKeySize :]
c = pkeEncrypt (cc , &ek .encryptionKey , m , r )
return c , K
}
func NewEncapsulationKey768 (encapsulationKey []byte ) (*EncapsulationKey768 , error ) {
ek := &EncapsulationKey768 {}
return parseEK (ek , encapsulationKey )
}
func parseEK(ek *EncapsulationKey768 , ekPKE []byte ) (*EncapsulationKey768 , error ) {
if len (ekPKE ) != EncapsulationKeySize768 {
return nil , errors .New ("mlkem: invalid encapsulation key length" )
}
h := sha3 .New256 ()
h .Write (ekPKE )
h .Sum (ek .h [:0 ])
for i := range ek .t {
var err error
ek .t [i ], err = polyByteDecode [nttElement ](ekPKE [:encodingSize12 ])
if err != nil {
return nil , err
}
ekPKE = ekPKE [encodingSize12 :]
}
copy (ek .ρ [:], ekPKE )
for i := byte (0 ); i < k ; i ++ {
for j := byte (0 ); j < k ; j ++ {
ek .a [i *k +j ] = sampleNTT (ek .ρ [:], j , i )
}
}
return ek , nil
}
func pkeEncrypt(cc *[CiphertextSize768 ]byte , ex *encryptionKey , m *[messageSize ]byte , rnd []byte ) []byte {
var N byte
r , e1 := make ([]nttElement , k ), make ([]ringElement , k )
for i := range r {
r [i ] = ntt (samplePolyCBD (rnd , N ))
N ++
}
for i := range e1 {
e1 [i ] = samplePolyCBD (rnd , N )
N ++
}
e2 := samplePolyCBD (rnd , N )
u := make ([]ringElement , k )
for i := range u {
u [i ] = e1 [i ]
for j := range r {
u [i ] = polyAdd (u [i ], inverseNTT (nttMul (ex .a [j *k +i ], r [j ])))
}
}
μ := ringDecodeAndDecompress1 (m )
var vNTT nttElement
for i := range ex .t {
vNTT = polyAdd (vNTT , nttMul (ex .t [i ], r [i ]))
}
v := polyAdd (polyAdd (inverseNTT (vNTT ), e2 ), μ )
c := cc [:0 ]
for _ , f := range u {
c = ringCompressAndEncode10 (c , f )
}
c = ringCompressAndEncode4 (c , v )
return c
}
func (dk *DecapsulationKey768 ) Decapsulate (ciphertext []byte ) (sharedKey []byte , err error ) {
if len (ciphertext ) != CiphertextSize768 {
return nil , errors .New ("mlkem: invalid ciphertext length" )
}
c := (*[CiphertextSize768 ]byte )(ciphertext )
return kemDecaps (dk , c ), nil
}
func kemDecaps(dk *DecapsulationKey768 , c *[CiphertextSize768 ]byte ) (K []byte ) {
fips140 .RecordApproved ()
m := pkeDecrypt (&dk .decryptionKey , c )
g := sha3 .New512 ()
g .Write (m [:])
g .Write (dk .h [:])
G := g .Sum (make ([]byte , 0 , 64 ))
Kprime , r := G [:SharedKeySize ], G [SharedKeySize :]
J := sha3 .NewShake256 ()
J .Write (dk .z [:])
J .Write (c [:])
Kout := make ([]byte , SharedKeySize )
J .Read (Kout )
var cc [CiphertextSize768 ]byte
c1 := pkeEncrypt (&cc , &dk .encryptionKey , (*[32 ]byte )(m ), r )
subtle .ConstantTimeCopy (subtle .ConstantTimeCompare (c [:], c1 ), Kout , Kprime )
return Kout
}
func pkeDecrypt(dx *decryptionKey , c *[CiphertextSize768 ]byte ) []byte {
u := make ([]ringElement , k )
for i := range u {
b := (*[encodingSize10 ]byte )(c [encodingSize10 *i : encodingSize10 *(i +1 )])
u [i ] = ringDecodeAndDecompress10 (b )
}
b := (*[encodingSize4 ]byte )(c [encodingSize10 *k :])
v := ringDecodeAndDecompress4 (b )
var mask nttElement
for i := range dx .s {
mask = polyAdd (mask , nttMul (dx .s [i ], ntt (u [i ])))
}
w := polySub (v , inverseNTT (mask ))
return ringCompressAndEncode1 (nil , w )
}
The pages are generated with Golds v0.7.3-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .