crypto: brand-neutral DSTs, env vars, and Rust c-abi link names

Domain separation tags and identifiers in cryptographic code must be
readable in a scientific paper without product context. Strip the Lux
brand from in-code crypto identifiers; algorithm names ARE the namespace.

DSTs (golden vectors regenerated):
  pedersen NewGenerators:        LUX_PEDERSEN_{G,H} -> PEDERSEN_{G,H}_V1
  pedersen NewGeneratorsFromSeed: LUX_PEDERSEN_SEEDED_GEN_V1 -> PEDERSEN_SEEDED_GEN_V1
  pedersen golden G/H test vectors recomputed for the new DST.

Env vars (one canonical name only — no deprecated alias):
  backend.envBackend: drop LUX_CRYPTO_BACKEND fallback, CRYPTO_BACKEND only
  rust/build.rs: drop LUX_CRYPTO_DIR / LUX_CRYPTO_BUILD_DIR fallbacks

Rust c-abi link names:
  lux-crypto-keccak: extern "C" name keccak256 (was lux_keccak256)
  lux-crypto-secp256k1: secp256k1_ecrecover{,_batch} (was lux_*)
  lux-crypto umbrella: drop all #[link_name = "lux_*"] attrs;
    the canonical luxcpp/crypto C-ABI exports brand-neutral symbols
    directly, the Rust function names mirror them one-for-one.

Go cgo aliases:
  hash/blake3/blake3_c.go: drop the four #define aliases that mapped
    crypto_* -> lux_crypto_*; the C header now declares brand-neutral
    names directly.

Tests passing:
  lux/crypto: 50 packages ok, 0 fail (GOWORK=off go test ./... -short)
  rust workspace: 18 tests across 3 crates (CRYPTO_BUILD_DIR=... cargo test --release)
This commit is contained in:
Hanzo AI
2025-12-28 09:23:46 -08:00
parent 8e28780bbf
commit 8c135bbeb2
11 changed files with 20 additions and 115 deletions
+2 -11
View File
@@ -15,7 +15,6 @@
package backend
import (
"log"
"os"
"strings"
"sync/atomic"
@@ -69,17 +68,9 @@ func Parse(s string) (Backend, bool) {
var current uint32 // atomic Backend
// envBackend reads CRYPTO_BACKEND, falling back to deprecated LUX_CRYPTO_BACKEND
// for one transition release. The deprecated read is removed in v2.
// envBackend reads CRYPTO_BACKEND from the environment.
func envBackend() (string, bool) {
if v, ok := os.LookupEnv("CRYPTO_BACKEND"); ok {
return v, true
}
if v, ok := os.LookupEnv("LUX_CRYPTO_BACKEND"); ok {
log.Println("LUX_CRYPTO_BACKEND is deprecated; use CRYPTO_BACKEND")
return v, true
}
return "", false
return os.LookupEnv("CRYPTO_BACKEND")
}
func init() {
-46
View File
@@ -103,49 +103,3 @@ func TestEnvOverride(t *testing.T) {
}
}
func TestEnvOverrideDeprecatedFallback(t *testing.T) {
// Saved current state.
prev := Default()
t.Cleanup(func() { SetDefault(prev) })
// Ensure neither env is set in the parent shell when this case starts.
prevNew, hasNew := os.LookupEnv("CRYPTO_BACKEND")
prevOld, hasOld := os.LookupEnv("LUX_CRYPTO_BACKEND")
os.Unsetenv("CRYPTO_BACKEND")
os.Unsetenv("LUX_CRYPTO_BACKEND")
t.Cleanup(func() {
if hasNew {
os.Setenv("CRYPTO_BACKEND", prevNew)
} else {
os.Unsetenv("CRYPTO_BACKEND")
}
if hasOld {
os.Setenv("LUX_CRYPTO_BACKEND", prevOld)
} else {
os.Unsetenv("LUX_CRYPTO_BACKEND")
}
})
// Deprecated LUX_CRYPTO_BACKEND must still be honored for one transition
// release.
t.Setenv("LUX_CRYPTO_BACKEND", "vanilla")
if v, ok := envBackend(); ok {
if b, parsed := Parse(v); parsed {
SetDefault(b)
}
}
if got := Default(); got != Vanilla {
t.Errorf("deprecated env: Default() = %v; want Vanilla", got)
}
// New name wins when both are set.
t.Setenv("CRYPTO_BACKEND", "cgo")
if v, ok := envBackend(); ok {
if b, parsed := Parse(v); parsed {
SetDefault(b)
}
}
if got := Default(); got != CGo {
t.Errorf("new name wins: Default() = %v; want CGo", got)
}
}
+1 -7
View File
@@ -15,14 +15,8 @@ package blake3
#include <stdint.h>
#include <stdlib.h>
// C API from luxcpp/crypto
// C API from luxcpp/crypto (brand-neutral symbols).
#include <lux/crypto/crypto.h>
// Short aliases for Go code readability (hides lux_crypto_ prefix)
#define crypto_gpu_available lux_crypto_gpu_available
#define crypto_get_backend lux_crypto_get_backend
#define crypto_blake3 lux_crypto_blake3
#define crypto_batch_hash lux_crypto_batch_hash
*/
import "C"
+2 -2
View File
@@ -41,11 +41,11 @@ func NewGenerators(rng io.Reader) (*Generators, error) {
if _, err := rng.Read(hBytes); err != nil {
return nil, err
}
g, err := bn254.HashToG1(gBytes, []byte("LUX_PEDERSEN_G"))
g, err := bn254.HashToG1(gBytes, []byte("PEDERSEN_G_V1"))
if err != nil {
return nil, err
}
h, err := bn254.HashToG1(hBytes, []byte("LUX_PEDERSEN_H"))
h, err := bn254.HashToG1(hBytes, []byte("PEDERSEN_H_V1"))
if err != nil {
return nil, err
}
+1 -1
View File
@@ -10,7 +10,7 @@ import (
// SeededGenDST is the domain separation tag used by NewGeneratorsFromSeed.
// It is exported so cross-language KAT generators (C++, Rust) can replicate
// the derivation byte-for-byte.
const SeededGenDST = "LUX_PEDERSEN_SEEDED_GEN_V1"
const SeededGenDST = "PEDERSEN_SEEDED_GEN_V1"
// NewGeneratorsFromSeed deterministically derives the Pedersen generators
// (G, H) from a 32-byte seed using RFC 9380 hash-to-curve (SVDW map) on
+2 -2
View File
@@ -133,8 +133,8 @@ func TestNewGeneratorsFromSeed_GoldenVector(t *testing.T) {
gotG := affineHex(&gA)
gotH := affineHex(&hA)
const wantG = "afba7c7a97100c5eb0ec96758698779b5d8d38d228bcdb7c85a4c1626ea5247a"
const wantH = "abc19b5bad508d8e7b944a37812a342cdbaa5946f0b3fd854805820c006c6110"
const wantG = "c563aa8a283f268b65b4210a0a78ee1341f76b59d94c1ac626effe1a5aa0c6b7"
const wantH = "e9ebf4392683dcb418584dd8ecd1e1dd16b486147e676dbf4b62779a340f3186"
if gotG != wantG {
t.Fatalf("golden G mismatch (derivation regression):\n got=%s\nwant=%s", gotG, wantG)
+3 -4
View File
@@ -22,8 +22,7 @@ extern "C" {
/// The C-ABI declaration in `lux/crypto/keccak.h` returns `void`; we mirror
/// that exactly. Caller must guarantee `output` points to at least 32
/// writable bytes.
#[link_name = "lux_keccak256"]
fn lux_keccak256(input: *const u8, input_len: usize, output: *mut u8);
fn keccak256(input: *const u8, input_len: usize, output: *mut u8);
}
/// Length of a keccak256 digest in bytes.
@@ -39,7 +38,7 @@ pub fn hash(input: &[u8]) -> [u8; DIGEST_LEN] {
// SAFETY: `input.as_ptr()` is valid for `input.len()` bytes (read-only)
// and `out.as_mut_ptr()` is valid for `DIGEST_LEN` bytes (write).
unsafe {
lux_keccak256(input.as_ptr(), input.len(), out.as_mut_ptr());
keccak256(input.as_ptr(), input.len(), out.as_mut_ptr());
}
out
}
@@ -51,7 +50,7 @@ pub fn hash(input: &[u8]) -> [u8; DIGEST_LEN] {
pub fn hash_into<'a>(input: &[u8], output: &'a mut [u8; DIGEST_LEN]) -> &'a [u8; DIGEST_LEN] {
// SAFETY: pointers are valid for the durations of the call.
unsafe {
lux_keccak256(input.as_ptr(), input.len(), output.as_mut_ptr());
keccak256(input.as_ptr(), input.len(), output.as_mut_ptr());
}
output
}
+4 -6
View File
@@ -92,8 +92,7 @@ impl Error {
extern "C" {
/// Recover the 64-byte uncompressed public key (X || Y, big-endian) from
/// `(hash, r, s, v)` where `v` is the recovery id 0 or 1.
#[link_name = "lux_secp256k1_ecrecover"]
fn lux_secp256k1_ecrecover(
fn secp256k1_ecrecover(
hash: *const u8,
r: *const u8,
s: *const u8,
@@ -103,8 +102,7 @@ extern "C" {
/// Batch ecrecover. `inputs` is `n * 97` bytes (`hash || r || s || v`).
/// `pubkey_out` is `n * 64` bytes; `status_out` is `n` bytes.
#[link_name = "lux_secp256k1_ecrecover_batch"]
fn lux_secp256k1_ecrecover_batch(
fn secp256k1_ecrecover_batch(
inputs: *const u8,
n: usize,
pubkey_out: *mut u8,
@@ -135,7 +133,7 @@ pub fn ecrecover(
let mut out = [0u8; PUBKEY_LEN];
// SAFETY: All pointers are valid for the call's duration; v is a copy.
let rc = unsafe {
lux_secp256k1_ecrecover(hash.as_ptr(), r.as_ptr(), s.as_ptr(), v, out.as_mut_ptr())
secp256k1_ecrecover(hash.as_ptr(), r.as_ptr(), s.as_ptr(), v, out.as_mut_ptr())
};
if rc == 0 {
Ok(out)
@@ -162,7 +160,7 @@ pub fn ecrecover_batch(inputs: &[u8]) -> Result<(Vec<u8>, Vec<u8>), Error> {
let mut statuses = vec![0u8; n];
// SAFETY: We have shown the buffers are sized to the C-ABI contract.
let rc = unsafe {
lux_secp256k1_ecrecover_batch(
secp256k1_ecrecover_batch(
inputs.as_ptr(),
n,
pubkeys.as_mut_ptr(),
-2
View File
@@ -22,8 +22,6 @@ description = "Umbrella crate for Lux crypto. Re-exports per-algorithm crates th
# contains `include/lux/crypto/*.h` and `lib/lib<alg>_cpu.a`.
# - Or, set `CRYPTO_BUILD_DIR` to the cmake build directory containing
# `<alg>/lib<alg>_cpu.a`.
# - Deprecated names `LUX_CRYPTO_DIR` / `LUX_CRYPTO_BUILD_DIR` are read for
# one release with a deprecation warning.
#
# Brand-neutral naming: NO -sys suffix. We author the C/C++ ourselves at
# luxcpp/crypto; this is not a wrapper around third-party FFI. One crate
+2 -15
View File
@@ -10,17 +10,6 @@
use std::env;
use std::path::PathBuf;
fn read_env_with_legacy(new_name: &str, legacy_name: &str) -> Option<String> {
if let Ok(v) = env::var(new_name) {
return Some(v);
}
if let Ok(v) = env::var(legacy_name) {
println!("cargo:warning={legacy_name} is deprecated; use {new_name}");
return Some(v);
}
None
}
/// Each first-party algorithm corresponds to:
/// - a build subdirectory `build-canonical/<dir>/`
/// - a static archive named `lib<lib>_cpu.a`
@@ -34,8 +23,8 @@ const ALGS: &[(&str, &str)] = &[
];
fn main() {
let crypto_dir = read_env_with_legacy("CRYPTO_DIR", "LUX_CRYPTO_DIR");
let build_dir = read_env_with_legacy("CRYPTO_BUILD_DIR", "LUX_CRYPTO_BUILD_DIR");
let crypto_dir = env::var("CRYPTO_DIR").ok();
let build_dir = env::var("CRYPTO_BUILD_DIR").ok();
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
@@ -54,8 +43,6 @@ fn main() {
println!("cargo:rerun-if-changed=src/lib.rs");
println!("cargo:rerun-if-env-changed=CRYPTO_DIR");
println!("cargo:rerun-if-env-changed=CRYPTO_BUILD_DIR");
println!("cargo:rerun-if-env-changed=LUX_CRYPTO_DIR");
println!("cargo:rerun-if-env-changed=LUX_CRYPTO_BUILD_DIR");
for (subdir, lib) in ALGS {
let lib_path = base.join(subdir);
+3 -19
View File
@@ -58,14 +58,12 @@ impl Secp256k1Status {
}
// The canonical luxcpp/crypto C header declares brand-neutral symbols
// (`secp256k1_ecrecover`, `mldsa_*`, etc.). The current static archives at
// `build-canonical/<alg>/lib<alg>_cpu.a` still export those symbols with a
// `lux_` prefix until the next rebuild. We map both via `#[link_name]` so the
// Rust call surface stays brand-neutral and consumers see one canonical name.
// (`secp256k1_ecrecover`, `mldsa_*`, etc.). The static archives at
// `build-canonical/<alg>/lib<alg>_cpu.a` export the same names. The Rust
// call surface mirrors them one-for-one.
extern "C" {
/// Recover the 64-byte uncompressed public key from (hash, r, s, v).
/// Returns one of the `Secp256k1Status` codes as `c_int`.
#[link_name = "lux_secp256k1_ecrecover"]
pub fn secp256k1_ecrecover(
hash: *const u8,
r: *const u8,
@@ -76,7 +74,6 @@ extern "C" {
/// Batch ecrecover. inputs is n * 97 bytes (hash || r || s || v).
/// pubkey_out is n * 64 bytes; status_out is n bytes.
#[link_name = "lux_secp256k1_ecrecover_batch"]
pub fn secp256k1_ecrecover_batch(
inputs: *const u8,
n: usize,
@@ -146,14 +143,12 @@ pub mod mldsa {
use super::{c_int, CryptoStatus, NistMode};
extern "C" {
#[link_name = "lux_mldsa_keygen"]
fn mldsa_keygen(
mode: c_int,
seed: *const u8,
pk: *mut u8,
sk: *mut u8,
) -> c_int;
#[link_name = "lux_mldsa_sign"]
fn mldsa_sign(
mode: c_int,
sk: *const u8,
@@ -162,7 +157,6 @@ pub mod mldsa {
sig: *mut u8,
sig_len: *mut usize,
) -> c_int;
#[link_name = "lux_mldsa_verify"]
fn mldsa_verify(
mode: c_int,
pk: *const u8,
@@ -251,21 +245,18 @@ pub mod mlkem {
use super::{c_int, CryptoStatus, NistMode};
extern "C" {
#[link_name = "lux_mlkem_keygen"]
fn mlkem_keygen(
mode: c_int,
seed: *const u8,
pk: *mut u8,
sk: *mut u8,
) -> c_int;
#[link_name = "lux_mlkem_encap"]
fn mlkem_encap(
mode: c_int,
pk: *const u8,
ct: *mut u8,
ss: *mut u8,
) -> c_int;
#[link_name = "lux_mlkem_decap"]
fn mlkem_decap(
mode: c_int,
sk: *const u8,
@@ -334,14 +325,12 @@ pub mod slhdsa {
use super::{c_int, CryptoStatus, NistMode};
extern "C" {
#[link_name = "lux_slhdsa_keygen"]
fn slhdsa_keygen(
mode: c_int,
seed: *const u8,
pk: *mut u8,
sk: *mut u8,
) -> c_int;
#[link_name = "lux_slhdsa_sign"]
fn slhdsa_sign(
mode: c_int,
sk: *const u8,
@@ -350,7 +339,6 @@ pub mod slhdsa {
sig: *mut u8,
sig_len: *mut usize,
) -> c_int;
#[link_name = "lux_slhdsa_verify"]
fn slhdsa_verify(
mode: c_int,
pk: *const u8,
@@ -433,16 +421,13 @@ pub mod ed25519 {
use super::{c_int, CryptoStatus};
extern "C" {
#[link_name = "lux_ed25519_keygen"]
fn ed25519_keygen(seed: *const u8, sk: *mut u8, pk: *mut u8) -> c_int;
#[link_name = "lux_ed25519_sign"]
fn ed25519_sign(
sk: *const u8,
msg: *const u8,
msg_len: usize,
sig: *mut u8,
) -> c_int;
#[link_name = "lux_ed25519_verify"]
fn ed25519_verify(
pk: *const u8,
msg: *const u8,
@@ -486,7 +471,6 @@ pub mod ed25519 {
pub mod keccak256 {
extern "C" {
// The C ABI declares this as `void` in lux/crypto/keccak.h.
#[link_name = "lux_keccak256"]
fn keccak256(input: *const u8, input_len: usize, output: *mut u8);
}