Files
crypto/mlkem/ct/dudect/encaps_ct.go
zeekay a67021c726 mlkem/ct/dudect: harness for Keygen + Encaps + Decaps CT
Empirical constant-time analysis harness mirroring the Pulsar
pack at ~/work/lux/pulsar/ct/dudect/:

  keygen_ct.go + dudect_keygen.c   - cgo bridge + C main loop
  encaps_ct.go + dudect_encaps.c   - encaps + tape
  decaps_ct.go + dudect_decaps.c   - the CT-critical routine

CT population:
  - All classes are VALID invocations of the routine under test
  - Decaps: both classes are VALID ciphertexts on a pool of valid
    encryptions under the same secret key. Any timing difference
    is a real ciphertext-content signal (FO-K timing attack
    surface).

All three cgo shared libraries build clean:
  GOWORK=off go build -buildmode=c-shared -tags mlkem_keygen_ct
  GOWORK=off go build -buildmode=c-shared -tags mlkem_encaps_ct
  GOWORK=off go build -buildmode=c-shared -tags mlkem_decaps_ct

Submission-grade run pending (GATE-1 in CRYPTOGRAPHER-SIGN-OFF.md).
Expected verdict for all three routines is no leakage detected
because Lux wraps cloudflare/circl which is BoringSSL/libjade-grade CT.
2026-05-19 08:42:02 -07:00

68 lines
1.4 KiB
Go

// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
//go:build mlkem_encaps_ct
// encaps_ct.go -- cgo bridge exposing ML-KEM-768 Encaps to the C
// dudect harness in dudect_encaps.c.
//
// CT POPULATION:
// Both dudect classes are VALID Encaps invocations under the same
// public key, differing in the per-call random tape:
// class A: encaps with fixed (zero) random tape
// class B: encaps with random tape
//
// Build:
// GOWORK=off go build -buildmode=c-shared \
// -o libmlkem_encaps.{so,dylib} ./encaps_ct.go
package main
/*
#cgo arm64 CFLAGS: -include ${SRCDIR}/dudect_compat.h
#include <stdint.h>
#include <stddef.h>
*/
import "C"
import (
"bytes"
"crypto/rand"
"unsafe"
"github.com/luxfi/crypto/mlkem"
)
const kEncapsRandSize = 32
var (
eMlkemPK *mlkem.PublicKey
)
//export mlkem_encaps_ct_setup
func mlkem_encaps_ct_setup() C.int {
pk, _, err := mlkem.GenerateKeyPair(rand.Reader, mlkem.MLKEM768)
if err != nil {
return 1
}
eMlkemPK = pk
return 0
}
//export mlkem_encaps_ct_input_size
func mlkem_encaps_ct_input_size() C.size_t {
return C.size_t(kEncapsRandSize)
}
//export mlkem_encaps_ct
func mlkem_encaps_ct(data *C.uint8_t) {
if eMlkemPK == nil {
return
}
src := unsafe.Slice((*byte)(unsafe.Pointer(data)), kEncapsRandSize)
r := bytes.NewReader(src)
_, _, _ = eMlkemPK.Encapsulate(r)
}
func main() {}