package seal import ( "bytes" "crypto/aes" "crypto/cipher" "encoding/hex" "sync" "testing" ) func testKey() []byte { b := make([]byte, 32) for i := range b { b[i] = byte(i) } return b } // These derivation vectors predate extraction, from Home's deployed sealer. func TestHistoricalHomeVectors(t *testing.T) { s, err := New(testKey(), "oficina/home") if err != nil { t.Fatal(err) } for purpose, want := range map[string]string{"signin.address": "1c677fc03245425f809a5a8ba5fda280f745ee96babd14ac8a75857ec9c7fe00", "team.name": "8cadd5d2a5921541918563b0046844c371de29743a4b9335759da8c345bfc732", "queue.payload": "2d6b4e963b93489c6a9515469f274516e48014d0bdb01831a9ff65086d0cb999"} { key, err := s.purposeKey(purpose) if err != nil || hex.EncodeToString(key) != want { t.Fatal("historical key changed", purpose, err) } block, err := aes.NewCipher(key) if err != nil { t.Fatal(err) } aead, err := cipher.NewGCM(block) if err != nil { t.Fatal(err) } nonce := bytes.Repeat([]byte{0x5a}, aead.NonceSize()) plaintext := []byte("historical fixture") old := aead.Seal(append([]byte(nil), nonce...), nonce, plaintext, nil) got, err := s.Open(purpose, old) if err != nil || !bytes.Equal(got, plaintext) { t.Fatal("old envelope unreadable", err) } current, err := s.Seal(purpose, plaintext) if err != nil { t.Fatal(err) } got, err = aead.Open(nil, current[:aead.NonceSize()], current[aead.NonceSize():], nil) if err != nil || !bytes.Equal(got, plaintext) { t.Fatal("old reader cannot read new envelope", err) } } } func TestNamespacePurposeKeyAndTamperIsolation(t *testing.T) { s, _ := New(testKey(), "oficina/home") ct, err := s.Seal("pending", []byte("private")) if err != nil { t.Fatal(err) } for _, ns := range []string{"oficina/docs/handoff", "oficina/sheets"} { other, _ := New(testKey(), ns) if _, err := other.Open("pending", ct); err == nil { t.Fatal("namespace crossed") } } if _, err := s.Open("inbox", ct); err == nil { t.Fatal("purpose crossed") } other, _ := New(bytes.Repeat([]byte{99}, 32), "oficina/home") if _, err := other.Open("pending", ct); err == nil { t.Fatal("key crossed") } for i := range ct { bad := append([]byte(nil), ct...) bad[i] ^= 1 if _, err := s.Open("pending", bad); err == nil { t.Fatal("altered envelope accepted") } } for i := 0; i < len(ct); i++ { if _, err := s.Open("pending", ct[:i]); err == nil { t.Fatal("truncated envelope accepted") } } } func TestConstructorCopiesKeyAndRejectsAmbiguousNamespaces(t *testing.T) { key := testKey() s, err := New(key, "oficina/home") if err != nil { t.Fatal(err) } clear(key) expected, _ := New(testKey(), "oficina/home") var wg sync.WaitGroup for i := 0; i < 8; i++ { wg.Add(1) go func() { defer wg.Done() ct, err := s.Seal("parallel", []byte("value")) if err != nil { t.Error(err) return } p, err := expected.Open("parallel", ct) if err != nil || string(p) != "value" { t.Error("caller key mutation changed sealer") } }() } wg.Wait() for _, ns := range []string{"", "a:b", "a\n", "a b", string(bytes.Repeat([]byte{'a'}, 129))} { if _, err := New(testKey(), ns); err == nil { t.Fatal("invalid namespace accepted") } } for _, n := range []int{0, 31, 33} { if _, err := New(make([]byte, n), "oficina/home"); err == nil { t.Fatal("invalid key length accepted") } } }