-
Notifications
You must be signed in to change notification settings - Fork 31
/
row.go
189 lines (154 loc) · 5.26 KB
/
row.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// Steve Phillips / elimisteve
// 2015.02.24
package types
import (
"encoding/json"
"errors"
"fmt"
"log"
"github.com/cryptag/cryptag"
"github.com/elimisteve/fun"
uuid "github.com/nu7hatch/gouuid"
)
// Row represents one piece of data and the tags it has been tagged
// with.
//
// The (untrusted) machine storing a Row can only see its ciphertext,
// nonce, and random strings (RandomTags), where each RandomTag (e.g.,
// "sqmgxo6xg") is a string that CrypTag randomly generates and pairs
// with each plainTag, where a plainTag is a human-readable string
// (e.g., "programming" or "type:file") that is either human-generated
// (like "programming"), auto-generated by CrypTag (like "type:file",
// "id:a91d46c7-45bb-48e4-43d1-642196df15b2", or
// "created:20160710072200"), or auto-generated by a CrypTag app (like
// "type:chatmessage" or "app:cryptagnotes"). (To learn more about
// CrypTag's tagging conventions, see
// <https://github.com/cryptag/cryptag/wiki/Advice,-Conventions,-and-Constraints>)
//
// Each of these mappings from plainTag to RandomTag is stored in a
// TagPair -- the other core CrypTag type.
type Row struct {
// Populated by server
Encrypted []byte `json:"data"`
RandomTags []string `json:"tags"`
// Populated locally
decrypted []byte
plainTags []string
Nonce *[24]byte `json:"nonce"`
}
var (
ErrRowsNotFound = errors.New("No rows found")
)
// NewRow returns a *Row containing/tagged with the passed-in
// plainTags, in addition to a unique ID tag ("id:..."), a timestamp
// tag ("created:created:20170105092731"), and the "all" tag. The
// *Row returned also contains and a new, random, cryptographic nonce.
func NewRow(decrypted []byte, plainTags []string) (*Row, error) {
// TODO: Ensure len(decrypted) < 2GB
// TODO: Ensure that len(decrypted) < 2GB(?)
id, err := uuid.NewV4()
if err != nil {
return nil, fmt.Errorf("Error generating new UUID for Row: %v", err)
}
// TODO(elimisteve): Document `id:`-prefix and related conventions
uuidTag := "id:" + id.String()
created := "created:" + cryptag.TimeStr(cryptag.Now())
// TODO(elimisteve): Use these:
//
// size := "size:" + fmt.Sprintf("%d", len(decrypted))
// approxsize := "approxsize:" + RowSizeCategory(len(decrypted))
// For future queryability-related reasons, UUID must come first!
plainTags = append([]string{uuidTag}, append(plainTags, created, "all")...)
nonce, err := cryptag.RandomNonce()
if err != nil {
return nil, err
}
// TODO(elimisteve): Randomize plainTags[1:len(plainTags)-1] here
row := &Row{decrypted: decrypted, plainTags: plainTags, Nonce: nonce}
return row, nil
}
func NewRowSimple(decrypted []byte, plainTags []string) (*Row, error) {
// TODO: Ensure that len(plainTags) > 0?
nonce, err := cryptag.RandomNonce()
if err != nil {
return nil, err
}
row := &Row{decrypted: decrypted, plainTags: plainTags, Nonce: nonce}
return row, nil
}
// NewRowFromBytes unmarshals b into a new *Row.
func NewRowFromBytes(b []byte) (*Row, error) {
row := &Row{}
if err := json.Unmarshal(b, row); err != nil {
return nil, fmt.Errorf("Error creating new row: `%v`. Input: `%s`", err,
b)
}
if Debug {
log.Printf("Created new Row `%#v` from bytes: `%s`\n", row, b)
}
return row, nil
}
// Decrypted returns row.decrypted, row's (unexported) decrypted data (if any).
func (row *Row) Decrypted() []byte {
return row.decrypted
}
// PlainTags returns row.plaintags, row's (unexported) plain
// (human-entered, human-readable) tags.
func (row *Row) PlainTags() []string {
return row.plainTags
}
// HasRandomTag answers the question, "does row have the random tag randtag?"
func (row *Row) HasRandomTag(randtag string) bool {
return fun.SliceContains(row.RandomTags, randtag)
}
// HasPlainTag answers the question, "does row have the plain tag plain?"
func (row *Row) HasPlainTag(plain string) bool {
return fun.SliceContains(row.plainTags, plain)
}
// Decrypt sets row.decrypted, row.nonce based upon row.Encrypted,
// nonce. The passed-in `decrypt` function will typically be
// bkend.Decrypt, where `bkend` is the backend storing this Row.
func (row *Row) Decrypt(key *[32]byte) error {
if len(row.Encrypted) == 0 {
if Debug {
log.Printf("row.Decrypt: no data to decrypt, returning nil (no error)\n")
}
return nil
}
if key == nil {
if Debug {
log.Printf("nil key passed to row.Decrypt for row `%#v`\n", row)
}
return cryptag.ErrNilKey
}
dec, err := cryptag.Decrypt(row.Encrypted, row.Nonce, key)
if err != nil {
return fmt.Errorf("Error decrypting: %v", err)
}
row.decrypted = dec
return nil
}
// SetPlainTags uses row.RandomTags and pairs to set row.plainTags
func (row *Row) SetPlainTags(pairs TagPairs) error {
matches, err := pairs.WithAllRandomTags(row.RandomTags)
if err != nil {
return err
}
row.plainTags = matches.AllPlain()
if Debug {
log.Printf("row.plainTags set to `%#v`\n", row.plainTags)
}
return nil
}
// Populate sets row.decrypted based on row.Encrypted and
// row.plainTags based on row.RandomTags, thereby populating row with
// plaintext data.
func (row *Row) Populate(key *[32]byte, pairs TagPairs) error {
if err := row.Decrypt(key); err != nil {
return fmt.Errorf("Error decrypting row: %v", err)
}
if err := row.SetPlainTags(pairs); err != nil {
return fmt.Errorf("Error setting row's plain tags: %v", err)
}
return nil
}