Ctrl K
ComunidadGuías…

internal/crypto/envelope_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 TestEnvelopeEncryptDecrypt(t *testing.T) {
	svc, err := NewService(testKey())
	require.NoError(t, err)

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

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

	// First byte should be version marker
	assert.Equal(t, envelopeVersionWithKeyID, ciphertext[0])

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

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

	svc, err := NewServiceFromKeyring(keyring)
	require.NoError(t, err)

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

	assert.Equal(t, envelopeVersionWithKeyID, ciphertext[0])
	assert.Equal(t, byte(len("new")), ciphertext[1])
	assert.Equal(t, "new", string(ciphertext[2:2+len("new")]))
}

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

	svc, err := NewServiceFromKeyring(keyring)
	require.NoError(t, err)

	legacyCiphertext := []byte{envelopeVersion, 0x00, 0x00}
	_, err = svc.EnvelopeDecrypt(legacyCiphertext)
	require.Error(t, err)
	assert.Contains(t, err.Error(), "unknown envelope version")
}

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

	svc, err := NewServiceFromKeyring(keyring)
	require.NoError(t, err)

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

	ciphertext[1] = byte(len("old"))
	copy(ciphertext[2:], []byte("old"))

	_, err = svc.EnvelopeDecrypt(ciphertext)
	require.Error(t, err)
	assert.Contains(t, err.Error(), "unknown key id")
}

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

	// Encrypting empty plaintext should work
	ciphertext, err := svc.EnvelopeEncrypt([]byte{})
	require.NoError(t, err)

	decrypted, err := svc.EnvelopeDecrypt(ciphertext)
	require.NoError(t, err)
	assert.Empty(t, decrypted)
}

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

	// 1 MB payload
	plaintext := make([]byte, 1<<20)
	for i := range plaintext {
		plaintext[i] = byte(i % 256)
	}

	ciphertext, err := svc.EnvelopeEncrypt(plaintext)
	require.NoError(t, err)

	decrypted, err := svc.EnvelopeDecrypt(ciphertext)
	require.NoError(t, err)
	assert.Equal(t, plaintext, decrypted)
}

func TestEnvelopeEncrypt_DifferentCiphertexts(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)

	// Different DEKs + nonces -> 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 TestEnvelopeDecrypt_WrongKEK(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 data"))
	require.NoError(t, err)

	// Decrypting with a different KEK should fail (DEK unwrap fails)
	_, err = svc2.EnvelopeDecrypt(ciphertext)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "unwrap DEK")
}

func TestEnvelopeDecrypt_Tampered(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 (data portion)
	ciphertext[len(ciphertext)-1] ^= 0xFF

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

func TestEnvelopeDecrypt_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 TestEnvelopeDecrypt_BadVersion(t *testing.T) {
	svc, err := NewService(testKey())
	require.NoError(t, err)

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

	// Corrupt version byte
	ciphertext[0] = 0xFF

	_, err = svc.EnvelopeDecrypt(ciphertext)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "unknown envelope version")
}

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

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

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

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

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

	// Craft an invalid blob with version=0x01 but bad edek_len
	bad := make([]byte, envelopeMinSize)
	bad[0] = envelopeVersion
	bad[1] = 0xFF // edek_len = 0x00FF = 255, way larger than remaining data
	bad[2] = 0x00

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