package keypair_test import ( "bytes" "crypto/ed25519" "crypto/rand" "encoding/base64" "testing" "galaxy/core/keypair" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestGenerateProducesIndependentCopies(t *testing.T) { t.Parallel() priv, pub, err := keypair.Generate(rand.Reader) require.NoError(t, err) require.Len(t, priv, ed25519.PrivateKeySize) require.Len(t, pub, ed25519.PublicKeySize) // Mutating the returned slices must not affect a fresh call. priv[0] ^= 0xFF pub[0] ^= 0xFF priv2, pub2, err := keypair.Generate(rand.Reader) require.NoError(t, err) assert.NotEqual(t, priv[:8], priv2[:8]) assert.NotEqual(t, pub[:8], pub2[:8]) } func TestGenerateIsDeterministicForFixedSeed(t *testing.T) { t.Parallel() seed := bytes.Repeat([]byte{0x42}, ed25519.SeedSize) priv1, pub1, err := keypair.Generate(bytes.NewReader(seed)) require.NoError(t, err) priv2, pub2, err := keypair.Generate(bytes.NewReader(seed)) require.NoError(t, err) assert.Equal(t, priv1, priv2) assert.Equal(t, pub1, pub2) } func TestGenerateRejectsNilReader(t *testing.T) { t.Parallel() _, _, err := keypair.Generate(nil) require.Error(t, err) } func TestSignRoundTrip(t *testing.T) { t.Parallel() priv, pub, err := keypair.Generate(rand.Reader) require.NoError(t, err) message := []byte("ui-core-roundtrip") signature, err := keypair.Sign(priv, message) require.NoError(t, err) assert.Len(t, signature, ed25519.SignatureSize) assert.True(t, keypair.Verify(pub, message, signature)) assert.False(t, keypair.Verify(pub, []byte("tampered"), signature)) tampered := append([]byte(nil), signature...) tampered[0] ^= 0xFF assert.False(t, keypair.Verify(pub, message, tampered)) } func TestSignRejectsInvalidPrivateKey(t *testing.T) { t.Parallel() _, err := keypair.Sign([]byte("short"), []byte("message")) require.ErrorIs(t, err, keypair.ErrInvalidPrivateKey) } func TestVerifyRejectsInvalidLengths(t *testing.T) { t.Parallel() priv, pub, err := keypair.Generate(rand.Reader) require.NoError(t, err) signature, err := keypair.Sign(priv, []byte("message")) require.NoError(t, err) assert.False(t, keypair.Verify(pub[:8], []byte("message"), signature)) assert.False(t, keypair.Verify(pub, []byte("message"), signature[:8])) } func TestMarshalUnmarshalPublicKeyRoundTrip(t *testing.T) { t.Parallel() _, pub, err := keypair.Generate(rand.Reader) require.NoError(t, err) encoded, err := keypair.MarshalPublicKey(pub) require.NoError(t, err) require.NotEmpty(t, encoded) // Encoding must be base64 StdEncoding to match docs/ARCHITECTURE.md ยง15. expected := base64.StdEncoding.EncodeToString(pub) assert.Equal(t, expected, encoded) decoded, err := keypair.UnmarshalPublicKey(encoded) require.NoError(t, err) assert.Equal(t, pub, decoded) } func TestMarshalPublicKeyRejectsInvalidLength(t *testing.T) { t.Parallel() _, err := keypair.MarshalPublicKey([]byte("short")) require.ErrorIs(t, err, keypair.ErrInvalidPublicKey) } func TestUnmarshalPublicKeyRejectsBadEncoding(t *testing.T) { t.Parallel() _, err := keypair.UnmarshalPublicKey("%%%not-base64%%%") require.ErrorIs(t, err, keypair.ErrInvalidPublicKeyEncoding) } func TestUnmarshalPublicKeyRejectsWrongLength(t *testing.T) { t.Parallel() _, err := keypair.UnmarshalPublicKey(base64.StdEncoding.EncodeToString([]byte("short"))) require.ErrorIs(t, err, keypair.ErrInvalidPublicKey) } func TestPublicKeyFromPrivate(t *testing.T) { t.Parallel() priv, pub, err := keypair.Generate(rand.Reader) require.NoError(t, err) derived, err := keypair.PublicKeyFromPrivate(priv) require.NoError(t, err) assert.Equal(t, pub, derived) _, err = keypair.PublicKeyFromPrivate([]byte("short")) require.ErrorIs(t, err, keypair.ErrInvalidPrivateKey) }