mirror of
https://github.com/luxfi/crypto.git
synced 2026-10-30 16:59:13 +00:00
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.
68 lines
1.4 KiB
Go
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() {}
|