blob: db47b9c501afbc26d9165592afa6e65cd520c469 [file] [log] [blame]
Adam Langley4866a022016-09-01 11:24:21 -07001/* Copyright 2016 The Roughtime Authors.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License. */
14
15#include <string>
16
17#include <stdint.h>
18
Adam Langley4866a022016-09-01 11:24:21 -070019#include <openssl/curve25519.h>
20
21#include "client.h"
Ankur Mittalbde02392017-03-22 17:48:25 -070022#include "logging.h"
Adam Langley4866a022016-09-01 11:24:21 -070023
24namespace roughtime {
25
26std::string CreateRequest(const uint8_t nonce[kNonceLength]) {
27 uint8_t query_bytes[kMinRequestSize];
28 size_t query_len;
29
30 Builder query(query_bytes, sizeof(query_bytes), 2);
31
32 uint8_t* padding;
33
34 static_assert(kTagNONC < kTagPAD, "Tags must be written in order");
Ankur Mittalbde02392017-03-22 17:48:25 -070035 ROUGHTIME_CHECK(query.AddTagData(kTagNONC, nonce, kNonceLength) &&
Adam Langley4866a022016-09-01 11:24:21 -070036 query.AddTag(&padding, kTagPAD, kPaddingLen) &&
37 query.Finish(&query_len));
Ankur Mittalbde02392017-03-22 17:48:25 -070038 ROUGHTIME_CHECK_EQ(query_len, sizeof(query_bytes));
Adam Langley4866a022016-09-01 11:24:21 -070039
40 memset(padding, 0, kPaddingLen);
41
42 return std::string(reinterpret_cast<char*>(query_bytes), query_len);
43}
44
Adam Langleya5d2c832016-09-21 17:10:22 -070045bool ParseResponse(rough_time_t* out_time, uint32_t* out_radius,
Adam Langley4866a022016-09-01 11:24:21 -070046 std::string* out_error,
47 const uint8_t root_public_key[ED25519_PUBLIC_KEY_LEN],
48 const uint8_t* response_bytes, size_t response_len,
49 const uint8_t nonce[kNonceLength]) {
50 *out_time = 0;
51 *out_radius = 0;
52
53 Parser response(response_bytes, response_len);
54 if (!response.is_valid()) {
55 *out_error = "structural error";
56 return false;
57 }
58
59 const uint8_t* cert_bytes;
60 size_t cert_len;
61 if (!response.GetTag(&cert_bytes, &cert_len, kTagCERT)) {
62 *out_error = "no certificate provided";
63 return false;
64 }
65
66 Parser cert(cert_bytes, cert_len);
67 if (!cert.is_valid()) {
68 *out_error = "structural error in certificate";
69 return false;
70 }
71
72 const uint8_t* signature;
73 if (!cert.GetFixedLen(&signature, kTagSIG, ED25519_SIGNATURE_LEN)) {
74 *out_error = "no signature in certificate";
75 return false;
76 }
77
78 const uint8_t* delegation_bytes;
79 size_t delegation_len;
80 if (!cert.GetTag(&delegation_bytes, &delegation_len, kTagDELE)) {
81 *out_error = "no delegation in certificate";
82 return false;
83 }
84
85 std::string signed_message =
86 std::string(kCertContextString, sizeof(kCertContextString)) +
87 std::string(reinterpret_cast<const char*>(delegation_bytes),
88 delegation_len);
89
90 if (!ED25519_verify(reinterpret_cast<const uint8_t*>(signed_message.data()),
91 signed_message.size(), signature, root_public_key)) {
92 *out_error = "bad signature in certificate";
93 return false;
94 }
95
96 const uint8_t* delegated_public_key;
Adam Langleya5d2c832016-09-21 17:10:22 -070097 rough_time_t min_time, max_time;
Adam Langley4866a022016-09-01 11:24:21 -070098 Parser delegation(delegation_bytes, delegation_len);
99 if (!delegation.is_valid() ||
100 !delegation.Get(&min_time, kTagMINT) ||
101 !delegation.Get(&max_time, kTagMAXT) ||
102 !delegation.GetFixedLen(&delegated_public_key, kTagPUBK,
103 ED25519_PUBLIC_KEY_LEN)) {
104 *out_error = "delegation missing required value";
105 return false;
106 }
107
108 if (max_time < min_time) {
109 *out_error = "invalid delegation validity period";
110 return false;
111 }
112
113 const uint8_t* signed_response_bytes;
114 size_t signed_response_len;
115 if (!response.GetTag(&signed_response_bytes, &signed_response_len,
116 kTagSREP)) {
117 *out_error = "no signed response";
118 return false;
119 }
120
121 const uint8_t* response_signature;
122 if (!response.GetFixedLen(&response_signature, kTagSIG,
123 ED25519_SIGNATURE_LEN)) {
124 *out_error = "no signature in response";
125 return false;
126 }
127
128 signed_message =
129 std::string(kContextString, sizeof(kContextString)) +
130 std::string(reinterpret_cast<const char*>(signed_response_bytes),
131 signed_response_len);
132
133 if (!ED25519_verify(reinterpret_cast<const uint8_t*>(signed_message.data()),
134 signed_message.size(), response_signature,
135 delegated_public_key)) {
136 *out_error = "bad signature in response";
137 return false;
138 }
139
140 Parser signed_response(signed_response_bytes, signed_response_len);
141 if (!signed_response.is_valid()) {
142 *out_error = "invalid signed response in response";
143 return false;
144 }
145
146 const uint8_t* root;
Adam Langleya5d2c832016-09-21 17:10:22 -0700147 rough_time_t timestamp;
Adam Langley4866a022016-09-01 11:24:21 -0700148 uint32_t radius;
149 if (!signed_response.GetFixedLen(&root, kTagROOT, kNonceLength) ||
150 !signed_response.Get(&timestamp, kTagMIDP) ||
151 !signed_response.Get(&radius, kTagRADI)) {
152 *out_error = "signed response missing required values";
153 return false;
154 }
155
156 if (timestamp < min_time || max_time < timestamp) {
157 *out_error = "timestamp out of range for delegation";
158 return false;
159 }
160
161 const uint8_t* path;
162 size_t path_len;
163 uint32_t tree_index;
164 if (!response.Get(&tree_index, kTagINDX) ||
165 !response.GetTag(&path, &path_len, kTagPATH)) {
166 *out_error = "response missing required values";
167 return false;
168 }
169
170 uint8_t hash[kNonceLength];
171 HashLeaf(hash, nonce);
172
173 if (path_len % kNonceLength != 0) {
174 *out_error = "tree path is not a multiple of the hash size";
175 return false;
176 }
177
178 for (size_t i = 0; i < path_len; i += kNonceLength) {
179 const bool path_element_is_right = tree_index & 1;
180 if (path_element_is_right) {
181 HashNode(hash, hash, path + i);
182 } else {
183 HashNode(hash, path + i, hash);
184 }
185 tree_index /= 2;
186 }
187
188 if (memcmp(root, hash, kNonceLength) != 0) {
189 *out_error = "calculated tree root doesn't match signed root";
190 return false;
191 }
192
193 *out_time = timestamp;
194 *out_radius = radius;
195
196 return true;
197}
198
199} // namespace roughtime