feat(crypto): key generation, validation, and secret-to-public derivation

Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
Claude 2026-02-20 18:24:06 +00:00
parent b759645724
commit a68926c45c
No known key found for this signature in database
GPG key ID: AF404715446AEB41
4 changed files with 111 additions and 0 deletions

View file

@ -14,4 +14,29 @@ void bridge_fast_hash(const uint8_t *data, size_t len, uint8_t hash[32]) {
crypto::cn_fast_hash(data, len, reinterpret_cast<char*>(hash));
}
int cn_generate_keys(uint8_t pub[32], uint8_t sec[32]) {
crypto::public_key pk;
crypto::secret_key sk;
crypto::generate_keys(pk, sk);
memcpy(pub, &pk, 32);
memcpy(sec, &sk, 32);
return 0;
}
int cn_secret_to_public(const uint8_t sec[32], uint8_t pub[32]) {
crypto::secret_key sk;
crypto::public_key pk;
memcpy(&sk, sec, 32);
bool ok = crypto::secret_key_to_public_key(sk, pk);
if (!ok) return 1;
memcpy(pub, &pk, 32);
return 0;
}
int cn_check_key(const uint8_t pub[32]) {
crypto::public_key pk;
memcpy(&pk, pub, 32);
return crypto::check_key(pk) ? 0 : 1;
}
} // extern "C"

View file

@ -14,6 +14,11 @@ extern "C" {
// ── Hashing ───────────────────────────────────────────────
void bridge_fast_hash(const uint8_t *data, size_t len, uint8_t hash[32]);
// ── Key Operations ────────────────────────────────────────
int cn_generate_keys(uint8_t pub[32], uint8_t sec[32]);
int cn_secret_to_public(const uint8_t sec[32], uint8_t pub[32]);
int cn_check_key(const uint8_t pub[32]);
#ifdef __cplusplus
}
#endif

View file

@ -28,3 +28,41 @@ func TestFastHash_Good_HelloWorld(t *testing.T) {
t.Fatal("FastHash returned zero hash")
}
}
func TestGenerateKeys_Good_Roundtrip(t *testing.T) {
pub, sec, err := crypto.GenerateKeys()
if err != nil {
t.Fatalf("GenerateKeys: %v", err)
}
if !crypto.CheckKey(pub) {
t.Fatal("generated public key failed CheckKey")
}
pub2, err := crypto.SecretToPublic(sec)
if err != nil {
t.Fatalf("SecretToPublic: %v", err)
}
if pub != pub2 {
t.Fatalf("SecretToPublic mismatch:\n GenerateKeys: %x\n SecretToPublic: %x", pub, pub2)
}
}
func TestCheckKey_Bad_Invalid(t *testing.T) {
// A random 32-byte value is overwhelmingly unlikely to be a valid curve point.
bad := [32]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
if crypto.CheckKey(bad) {
t.Fatal("0xFF...FF should fail CheckKey")
}
}
func TestGenerateKeys_Good_Unique(t *testing.T) {
pub1, _, _ := crypto.GenerateKeys()
pub2, _, _ := crypto.GenerateKeys()
if pub1 == pub2 {
t.Fatal("two GenerateKeys calls returned identical public keys")
}
}

43
crypto/keygen.go Normal file
View file

@ -0,0 +1,43 @@
// SPDX-Licence-Identifier: EUPL-1.2
package crypto
/*
#include "bridge.h"
*/
import "C"
import (
"fmt"
"unsafe"
)
// GenerateKeys creates a new random key pair.
func GenerateKeys() (pub [32]byte, sec [32]byte, err error) {
rc := C.cn_generate_keys(
(*C.uint8_t)(unsafe.Pointer(&pub[0])),
(*C.uint8_t)(unsafe.Pointer(&sec[0])),
)
if rc != 0 {
err = fmt.Errorf("crypto: generate_keys failed (rc=%d)", rc)
}
return
}
// SecretToPublic derives the public key from a secret key.
func SecretToPublic(sec [32]byte) ([32]byte, error) {
var pub [32]byte
rc := C.cn_secret_to_public(
(*C.uint8_t)(unsafe.Pointer(&sec[0])),
(*C.uint8_t)(unsafe.Pointer(&pub[0])),
)
if rc != 0 {
return pub, fmt.Errorf("crypto: secret_to_public failed (rc=%d)", rc)
}
return pub, nil
}
// CheckKey validates that a public key is a valid curve point.
func CheckKey(pub [32]byte) bool {
return C.cn_check_key((*C.uint8_t)(unsafe.Pointer(&pub[0]))) == 0
}