Ctrl K
ComunidadGuías…

internal/crypto/crypto_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package crypto

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func testKey() []byte {
	// 32 bytes of deterministic test key
	return []byte("01234567890123456789012345678901")
}

func otherTestKey() []byte {
	return []byte("abcdefghijklmnopqrstuvwxyz012345")
}

func TestNewKeyring_PrimaryAndLookup(t *testing.T) {
	keyring, err := NewKeyring([]Key{
		{ID: "old", Material: testKey()},
		{ID: "new", Material: otherTestKey()},
	}, "new")
	require.NoError(t, err)

	assert.Equal(t, "new", keyring.primaryID)
	assert.Equal(t, []string{"old", "new"}, keyring.order)
}

func TestNewKeyring_RejectsUnsafeKeyID(t *testing.T) {
	_, err := NewKeyring([]Key{{ID: "old.key", Material: testKey()}}, "old.key")
	require.Error(t, err)
	assert.Contains(t, err.Error(), "key id")
}

func TestNewService_NilKey(t *testing.T) {
	svc, err := NewService(nil)
	require.NoError(t, err)
	assert.False(t, svc.Enabled())
}

func TestNewService_ValidKey(t *testing.T) {
	svc, err := NewService(testKey())
	require.NoError(t, err)
	assert.True(t, svc.Enabled())
}

func TestNewService_WrongKeyLength(t *testing.T) {
	_, err := NewService([]byte("too-short"))
	require.Error(t, err)
	assert.Contains(t, err.Error(), "32 bytes")
}

func TestEncryptDecrypt(t *testing.T) {
	svc, err := NewService(testKey())
	require.NoError(t, err)

	plaintext := []byte("hello, secret headers!")
	ciphertext, err := svc.EnvelopeEncrypt(plaintext)
	require.NoError(t, err)

	// Ciphertext should be different from plaintext
	assert.NotEqual(t, plaintext, ciphertext)

	// Decrypt should recover original
	decrypted, err := svc.EnvelopeDecrypt(ciphertext)
	require.NoError(t, err)
	assert.Equal(t, plaintext, decrypted)
}

func TestEncryptDecrypt_DifferentNonces(t *testing.T) {
	svc, err := NewService(testKey())
	require.NoError(t, err)

	plaintext := []byte("same input")
	ct1, err := svc.EnvelopeEncrypt(plaintext)
	require.NoError(t, err)
	ct2, err := svc.EnvelopeEncrypt(plaintext)
	require.NoError(t, err)

	// Two encryptions of the same plaintext should produce different ciphertext
	assert.NotEqual(t, ct1, ct2)

	// Both should decrypt correctly
	d1, err := svc.EnvelopeDecrypt(ct1)
	require.NoError(t, err)
	d2, err := svc.EnvelopeDecrypt(ct2)
	require.NoError(t, err)
	assert.Equal(t, d1, d2)
}

func TestDecrypt_TamperedData(t *testing.T) {
	svc, err := NewService(testKey())
	require.NoError(t, err)

	ciphertext, err := svc.EnvelopeEncrypt([]byte("sensitive"))
	require.NoError(t, err)

	// Tamper with last byte
	ciphertext[len(ciphertext)-1] ^= 0xFF

	_, err = svc.EnvelopeDecrypt(ciphertext)
	assert.Error(t, err)
}

func TestDecrypt_TooShort(t *testing.T) {
	svc, err := NewService(testKey())
	require.NoError(t, err)

	_, err = svc.EnvelopeDecrypt([]byte{envelopeVersionWithKeyID})
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "too short")
}

func TestNoEncryptionKey(t *testing.T) {
	svc, err := NewService(nil)
	require.NoError(t, err)

	_, err = svc.EnvelopeEncrypt([]byte("data"))
	assert.ErrorIs(t, err, ErrNoEncryptionKey)

	_, err = svc.EnvelopeDecrypt([]byte("data"))
	assert.ErrorIs(t, err, ErrNoEncryptionKey)
}

func TestEncryptDecryptJSON(t *testing.T) {
	svc, err := NewService(testKey())
	require.NoError(t, err)

	original := map[string]string{
		"Authorization": "Bearer sk-secret-token",
		"X-Api-Key":     "key-12345",
	}

	ciphertext, err := svc.EncryptJSON(original)
	require.NoError(t, err)

	var decrypted map[string]string
	err = svc.DecryptJSON(ciphertext, &decrypted)
	require.NoError(t, err)

	assert.Equal(t, original, decrypted)
}

func TestParseKey(t *testing.T) {
	tests := []struct {
		name    string
		input   string
		wantNil bool
		wantErr bool
	}{
		{"empty returns nil", "", true, false},
		{"valid 64 hex chars", "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", false, false},
		{"invalid hex", "not-hex-at-all!", false, true},
		{"wrong length", "0123456789abcdef", false, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			key, err := ParseKey(tt.input)
			if tt.wantErr {
				assert.Error(t, err)
				return
			}
			require.NoError(t, err)
			if tt.wantNil {
				assert.Nil(t, key)
			} else {
				assert.Len(t, key, 32)
			}
		})
	}
}

func TestDecrypt_WrongKey(t *testing.T) {
	svc1, err := NewService(testKey())
	require.NoError(t, err)

	svc2, err := NewService([]byte("different-key-0123456789abcdefgh"))
	require.NoError(t, err)

	ciphertext, err := svc1.EnvelopeEncrypt([]byte("secret"))
	require.NoError(t, err)

	// Decrypting with a different key should fail
	_, err = svc2.EnvelopeDecrypt(ciphertext)
	assert.Error(t, err)
}

func TestEncryptString_DecryptString(t *testing.T) {
	svc, err := NewService(testKey())
	require.NoError(t, err)

	// Normal string
	ct, err := svc.EncryptString("whsec_abc123")
	require.NoError(t, err)
	assert.NotNil(t, ct)

	decrypted, err := svc.DecryptString(ct)
	require.NoError(t, err)
	assert.Equal(t, "whsec_abc123", decrypted)

	// Empty string returns nil
	ct, err = svc.EncryptString("")
	require.NoError(t, err)
	assert.Nil(t, ct)

	decrypted, err = svc.DecryptString(nil)
	require.NoError(t, err)
	assert.Equal(t, "", decrypted)
}

func TestGenerateKey(t *testing.T) {
	hexKey, rawKey, err := GenerateKey()
	require.NoError(t, err)
	assert.Len(t, rawKey, 32)
	assert.Len(t, hexKey, 64)

	// Should be usable to create a service
	svc, err := NewService(rawKey)
	require.NoError(t, err)
	assert.True(t, svc.Enabled())

	// Hex should round-trip
	parsed, err := ParseKey(hexKey)
	require.NoError(t, err)
	assert.Equal(t, rawKey, parsed)
}