| 1 | package mail |
| 2 | |
| 3 | import ( |
| 4 | "encoding/base64" |
| 5 | "testing" |
| 6 | ) |
| 7 | |
| 8 | // The derivation is structurally pinned: AWS's algorithm prefixes a |
| 9 | // one-byte version (0x04) to a 32-byte HMAC-SHA256, so every password |
| 10 | // decodes to exactly 33 bytes starting 0x04. A change that breaks either |
| 11 | // invariant produces credentials SES rejects at auth time — hours later, |
| 12 | // in someone else's deployment. |
| 13 | func TestSMTPPasswordShape(t *testing.T) { |
| 14 | // wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY is AWS's own published |
| 15 | // documentation example secret access key, not a credential — the |
| 16 | // golden output below is pinned to this exact value. |
| 17 | got := SMTPPassword("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "eu-west-1") |
| 18 | raw, err := base64.StdEncoding.DecodeString(got) |
| 19 | if err != nil { |
| 20 | t.Fatalf("password is not base64: %v", err) |
| 21 | } |
| 22 | if len(raw) != 33 { |
| 23 | t.Fatalf("decoded length = %d, want 33", len(raw)) |
| 24 | } |
| 25 | if raw[0] != 0x04 { |
| 26 | t.Fatalf("version byte = %#x, want 0x04", raw[0]) |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | // Region is part of the signing chain, so the same secret yields a |
| 31 | // DIFFERENT password per region. This is why §8's multi-region delivery |
| 32 | // must derive per region rather than reuse one string. |
| 33 | func TestSMTPPasswordIsRegionScoped(t *testing.T) { |
| 34 | secret := "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" |
| 35 | if SMTPPassword(secret, "eu-west-1") == SMTPPassword(secret, "us-east-1") { |
| 36 | t.Fatal("password did not change with region") |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | func TestSMTPPasswordIsDeterministic(t *testing.T) { |
| 41 | secret := "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" |
| 42 | if SMTPPassword(secret, "eu-west-1") != SMTPPassword(secret, "eu-west-1") { |
| 43 | t.Fatal("derivation is not deterministic") |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | // A golden value produced by AWS's own reference implementation, not by |
| 48 | // this package. The property tests above cannot tell a correct chain from |
| 49 | // a wrong-but-well-formed one (transpose region and service and every |
| 50 | // property still holds), so this is the only test here with an independent |
| 51 | // oracle. Regenerate it from the SES Developer Guide, never from our code. |
| 52 | func TestSMTPPasswordGolden(t *testing.T) { |
| 53 | const want = "BMW5RDrXmmVs0lV7GpI4oLkHXpZ4stDsk6q91z1g38Pk" |
| 54 | got := SMTPPassword("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "eu-west-1") |
| 55 | if got != want { |
| 56 | t.Fatalf("SMTPPassword = %q, want %q", got, want) |
| 57 | } |
| 58 | } |
| 59 | |