This repository has been archived by the owner on Jul 16, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 151
/
grpc_client.go
226 lines (199 loc) · 7.11 KB
/
grpc_client.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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Client for communicating with the Key Server.
// Implements verification and convenience functions.
package grpcc
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"log"
"time"
"github.com/google/keytransparency/core/client/ctlog"
"github.com/google/keytransparency/core/client/kt"
"github.com/google/keytransparency/core/signatures"
"github.com/google/keytransparency/core/tree/sparse"
tv "github.com/google/keytransparency/core/tree/sparse/verifier"
"github.com/google/keytransparency/core/vrf"
"github.com/golang/protobuf/proto"
"golang.org/x/net/context"
"google.golang.org/grpc"
"github.com/google/keytransparency/core/proto/ctmap"
tpb "github.com/google/keytransparency/core/proto/keytransparency_v1_types"
spb "github.com/google/keytransparency/impl/proto/keytransparency_v1_service"
)
const (
// Each page contains pageSize profiles. Each profile contains multiple
// keys. Assuming 2 keys per profile (each of size 2048-bit), a page of
// size 16 will contain about 8KB of data.
pageSize = 16
// The default capacity used when creating a profiles list in
// ListHistory.
defaultListCap = 10
// TODO: Public keys of trusted monitors.
)
var (
// ErrRetry occurs when an update request has been submitted, but the
// results of the udpate are not visible on the server yet. The client
// must retry until the request is visible.
ErrRetry = errors.New("update not present on server yet")
// ErrIncomplete occurs when the server indicates that requested epochs
// are not available.
ErrIncomplete = errors.New("incomplete account history")
// Vlog is the verbose logger. By default it outputs to /dev/null.
Vlog = log.New(ioutil.Discard, "", 0)
)
// Client is a helper library for issuing updates to the key server.
// Client Responsibilities
// - Trust Model:
// - - Trusted Monitors
// - - Verify last X days
// - Gossip - What is the current value of the root?
// - - Gossip advancement: advance state between current and server.
// - Sender queries - Do queries match up against the gossip root?
// - - List trusted monitors.
// - Key Owner
// - - Periodically query own keys. Do they match the private keys I have?
// - - Sign key update requests.
type Client struct {
cli spb.KeyTransparencyServiceClient
vrf vrf.PublicKey
kt *kt.Verifier
CT ctlog.Verifier
RetryCount int
RetryDelay time.Duration
}
// New creates a new client.
func New(mapID string, client spb.KeyTransparencyServiceClient, vrf vrf.PublicKey, verifier signatures.Verifier, log ctlog.Verifier) *Client {
return &Client{
cli: client,
vrf: vrf,
kt: kt.New(vrf, tv.New([]byte(mapID), sparse.CONIKSHasher), verifier, log),
CT: log,
RetryCount: 1,
RetryDelay: 3 * time.Second,
}
}
// GetEntry returns an entry if it exists, and nil if it does not.
func (c *Client) GetEntry(ctx context.Context, userID string, opts ...grpc.CallOption) (*tpb.Profile, *ctmap.MapHead, error) {
e, err := c.cli.GetEntry(ctx, &tpb.GetEntryRequest{
UserId: userID,
}, opts...)
if err != nil {
return nil, nil, err
}
if err := c.kt.VerifyGetEntryResponse(ctx, userID, e); err != nil {
return nil, nil, err
}
// Empty case.
if e.GetCommitted() == nil {
return nil, e.GetSmh().GetMapHead(), nil
}
profile := new(tpb.Profile)
if err := proto.Unmarshal(e.GetCommitted().Data, profile); err != nil {
return nil, nil, fmt.Errorf("Error unmarshaling profile: %v", err)
}
return profile, e.GetSmh().GetMapHead(), nil
}
func min(x, y int32) int32 {
if x < y {
return x
}
return y
}
// ListHistory returns a list of profiles starting and ending at given epochs.
// It also filters out all identical consecutive profiles.
func (c *Client) ListHistory(ctx context.Context, userID string, start, end int64, opts ...grpc.CallOption) (map[*ctmap.MapHead]*tpb.Profile, error) {
currentProfile := new(tpb.Profile)
profiles := make(map[*ctmap.MapHead]*tpb.Profile)
for start <= end {
resp, err := c.cli.ListEntryHistory(ctx, &tpb.ListEntryHistoryRequest{
UserId: userID,
Start: start,
PageSize: min(int32((end-start)+1), pageSize),
}, opts...)
if err != nil {
return nil, err
}
for i, v := range resp.GetValues() {
Vlog.Printf("Processing entry for %v, epoch %v", userID, start+int64(i))
err = c.kt.VerifyGetEntryResponse(ctx, userID, v)
if err != nil {
return nil, err
}
var profile tpb.Profile
if v.GetCommitted() != nil {
if err := proto.Unmarshal(v.GetCommitted().Data, &profile); err != nil {
return nil, err
}
}
// Compress profiles that are equal through time. All
// nil profiles before the first profile are ignored.
if proto.Equal(currentProfile, &profile) {
continue
}
// Append the slice and update currentProfile.
profiles[v.GetSmh().GetMapHead()] = &profile
currentProfile = &profile
}
if resp.NextStart == 0 {
return nil, ErrIncomplete // No more data.
}
start = resp.NextStart // Fetch the next block of results.
}
return profiles, nil
}
// Update creates an UpdateEntryRequest for a user, attempt to submit it multiple
// times depending on RetryCount.
func (c *Client) Update(ctx context.Context, userID string, profile *tpb.Profile, signers []signatures.Signer, authorizedKeys []*tpb.PublicKey, opts ...grpc.CallOption) (*tpb.UpdateEntryRequest, error) {
getResp, err := c.cli.GetEntry(ctx, &tpb.GetEntryRequest{UserId: userID}, opts...)
if err != nil {
return nil, err
}
Vlog.Printf("Got current entry...")
if err := c.kt.VerifyGetEntryResponse(ctx, userID, getResp); err != nil {
return nil, err
}
req, err := kt.CreateUpdateEntryRequest(getResp, c.vrf, userID, profile, signers, authorizedKeys)
if err != nil {
return nil, err
}
err = c.Retry(ctx, req)
// Retry submitting until an incluion proof is returned.
for i := 0; err == ErrRetry && i < c.RetryCount; i++ {
time.Sleep(c.RetryDelay)
err = c.Retry(ctx, req)
}
return req, err
}
// Retry will take a pre-fabricated request and send it again.
func (c *Client) Retry(ctx context.Context, req *tpb.UpdateEntryRequest) error {
Vlog.Printf("Sending Update request...")
updateResp, err := c.cli.UpdateEntry(ctx, req)
if err != nil {
return err
}
Vlog.Printf("Got current entry...")
// Validate response.
if err := c.kt.VerifyGetEntryResponse(ctx, req.UserId, updateResp.GetProof()); err != nil {
return err
}
// Check if the response is a replay.
if got, want := updateResp.GetProof().GetLeafProof().LeafData, req.GetEntryUpdate().GetUpdate().KeyValue.Value; !bytes.Equal(got, want) {
return ErrRetry
}
return nil
// TODO: Update previous entry pointer
}