internal/crypto/envelope_test.go
Rama main · réplica de lectura del repositorio original.
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)
}