oficina / docs Public

Clone
git clone https://amadan.net/oficina/docs

Plain git — no account needed to clone.

Download

Download this file

1package docs
2
3import (
4 "bytes"
5 "encoding/base64"
6 "errors"
7 "testing"
8
9 "github.com/google/uuid"
10)
11
12func TestContentCipherAuthenticatesRowIdentity(t *testing.T) {
13 c, err := newContentCipher(base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{41}, 32)), "production")
14 if err != nil {
15 t.Fatal(err)
16 }
17 guid := uuid.NewString()
18 body := []byte("private body not shared with another document")
19 sealed, err := c.seal(contentSnapshotKind, guid, 42, 4, body)
20 if err != nil {
21 t.Fatal(err)
22 }
23 got, err := c.open(contentSnapshotKind, guid, 42, 4, sealed)
24 if err != nil || !bytes.Equal(got, body) {
25 t.Fatalf("round trip: %q, %v", got, err)
26 }
27 if bytes.Contains(sealed, body) {
28 t.Fatal("ciphertext contains plaintext")
29 }
30 for _, tc := range []struct {
31 name string
32 kind byte
33 guid string
34 seq int64
35 abi uint32
36 }{
37 {"document", contentSnapshotKind, uuid.NewString(), 42, 4},
38 {"sequence", contentSnapshotKind, guid, 43, 4},
39 {"kind", contentUpdateKind, guid, 42, 4},
40 {"abi", contentSnapshotKind, guid, 42, 5},
41 } {
42 t.Run(tc.name, func(t *testing.T) {
43 if _, err := c.open(tc.kind, tc.guid, tc.seq, tc.abi, sealed); !errors.Is(err, errContentAuthentication) {
44 t.Fatalf("authentication: %v", err)
45 }
46 })
47 }
48 sealed[len(sealed)-1] ^= 1
49 if _, err := c.open(contentSnapshotKind, guid, 42, 4, sealed); !errors.Is(err, errContentAuthentication) {
50 t.Fatalf("tamper: %v", err)
51 }
52 for n := 0; n < c.aead.NonceSize()+c.aead.Overhead(); n++ {
53 if _, err := c.open(contentSnapshotKind, guid, 42, 4, sealed[:n]); !errors.Is(err, errContentAuthentication) {
54 t.Fatalf("truncation %d: %v", n, err)
55 }
56 }
57}
58
59func TestContentCipherKeysAndNonces(t *testing.T) {
60 for _, encoded := range []string{"", "not-base64", base64.StdEncoding.EncodeToString([]byte("short")), base64.StdEncoding.EncodeToString(developmentContentKey[:])} {
61 if _, err := newContentCipher(encoded, "production"); !errors.Is(err, errContentKey) {
62 t.Fatalf("invalid production key accepted: %v", err)
63 }
64 }
65 a, err := newContentCipher("", "development")
66 if err != nil {
67 t.Fatal(err)
68 }
69 b, err := newContentCipher(base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{42}, 32)), "development")
70 if err != nil {
71 t.Fatal(err)
72 }
73 guid := uuid.NewString()
74 seen := make(map[string]bool)
75 for i := 0; i < 100; i++ {
76 sealed, err := a.seal(contentUpdateKind, guid, 1, 0, []byte("body"))
77 if err != nil {
78 t.Fatal(err)
79 }
80 nonce := string(sealed[:a.aead.NonceSize()])
81 if seen[nonce] {
82 t.Fatal("nonce repeated")
83 }
84 seen[nonce] = true
85 if _, err := b.open(contentUpdateKind, guid, 1, 0, sealed); !errors.Is(err, errContentAuthentication) {
86 t.Fatalf("wrong key: %v", err)
87 }
88 }
89 if _, err := a.seal(contentUpdateKind, "broken-guid", 1, 0, nil); !errors.Is(err, errContentAuthentication) {
90 t.Fatalf("invalid guid: %v", err)
91 }
92}
93