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) } }