90 lines
2.5 KiB
Go
90 lines
2.5 KiB
Go
|
|
package secretbox
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/base64"
|
||
|
|
"errors"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
)
|
||
|
|
|
||
|
|
func testCodec(t *testing.T) *Codec {
|
||
|
|
t.Helper()
|
||
|
|
c, err := New("test-key", base64.StdEncoding.EncodeToString(make([]byte, 32)))
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
return c
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestCodecRoundTripUsesRandomNonce(t *testing.T) {
|
||
|
|
c := testCodec(t)
|
||
|
|
one, err := c.Encrypt(42, "api_key", "xai", "top-secret")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
two, err := c.Encrypt(42, "api_key", "xai", "top-secret")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if one == two {
|
||
|
|
t.Fatal("ciphertexts must differ")
|
||
|
|
}
|
||
|
|
if !strings.HasPrefix(one, "enc:v1:test-key:") {
|
||
|
|
t.Fatalf("unexpected prefix: %q", one)
|
||
|
|
}
|
||
|
|
got, err := c.Decrypt(42, "api_key", "xai", one)
|
||
|
|
if err != nil || got != "top-secret" {
|
||
|
|
t.Fatalf("round trip = %q, %v", got, err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestCodecAADAndTamperingFail(t *testing.T) {
|
||
|
|
c := testCodec(t)
|
||
|
|
value, err := c.Encrypt(42, "api_key", "xai", "top-secret")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
parts := strings.Split(value, ":")
|
||
|
|
payload, err := base64.RawURLEncoding.DecodeString(parts[3])
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
payload[len(payload)-1] ^= 0x01
|
||
|
|
tampered := strings.Join([]string{parts[0], parts[1], parts[2], base64.RawURLEncoding.EncodeToString(payload)}, ":")
|
||
|
|
for _, tc := range []struct {
|
||
|
|
name string
|
||
|
|
uid int64
|
||
|
|
field string
|
||
|
|
provider string
|
||
|
|
value string
|
||
|
|
}{
|
||
|
|
{name: "uid", uid: 43, field: "api_key", provider: "xai", value: value},
|
||
|
|
{name: "field", uid: 42, field: "exa_api_key", provider: "xai", value: value},
|
||
|
|
{name: "provider", uid: 42, field: "api_key", provider: "other", value: value},
|
||
|
|
{name: "tamper", uid: 42, field: "api_key", provider: "xai", value: tampered},
|
||
|
|
} {
|
||
|
|
t.Run(tc.name, func(t *testing.T) {
|
||
|
|
if _, err := c.Decrypt(tc.uid, tc.field, tc.provider, tc.value); !errors.Is(err, ErrInvalidCiphertext) {
|
||
|
|
t.Fatalf("error = %v", err)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestCodecCompatibilityAndValidation(t *testing.T) {
|
||
|
|
c := testCodec(t)
|
||
|
|
got, err := c.Decrypt(1, "api_key", "xai", "legacy-plaintext")
|
||
|
|
if err != nil || got != "legacy-plaintext" {
|
||
|
|
t.Fatalf("legacy = %q, %v", got, err)
|
||
|
|
}
|
||
|
|
if _, err := c.Decrypt(1, "api_key", "xai", "enc:v2:key:data"); !errors.Is(err, ErrUnsupportedVersion) {
|
||
|
|
t.Fatalf("version error = %v", err)
|
||
|
|
}
|
||
|
|
if _, err := c.Decrypt(1, "api_key", "xai", "enc:v1:other:data"); !errors.Is(err, ErrInvalidCiphertext) {
|
||
|
|
t.Fatalf("key ID error = %v", err)
|
||
|
|
}
|
||
|
|
if _, err := New("key", base64.StdEncoding.EncodeToString(make([]byte, 31))); !errors.Is(err, ErrInvalidConfig) {
|
||
|
|
t.Fatalf("key size error = %v", err)
|
||
|
|
}
|
||
|
|
}
|