diff --git a/crypto/bridge.cpp b/crypto/bridge.cpp index a775f7d..4c088a1 100644 --- a/crypto/bridge.cpp +++ b/crypto/bridge.cpp @@ -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(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" diff --git a/crypto/bridge.h b/crypto/bridge.h index 3ba9b2c..3fe4780 100644 --- a/crypto/bridge.h +++ b/crypto/bridge.h @@ -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 diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index f084460..ae2595a 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -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") + } +} diff --git a/crypto/keygen.go b/crypto/keygen.go new file mode 100644 index 0000000..c503f9f --- /dev/null +++ b/crypto/keygen.go @@ -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 +}