blob: b0a0e90352b5ea6fb4ec7c6b99346e17bc5d98e9 [file] [log] [blame]
Louis Collard70c29b42019-02-26 18:38:25 +08001// Copyright 2019 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "u2fd/util.h"
6
Louis Collardf5a8c3d2019-02-25 12:02:18 +08007#include <array>
Louis Collard70c29b42019-02-26 18:38:25 +08008#include <string>
Louis Collardf5a8c3d2019-02-25 12:02:18 +08009#include <utility>
Louis Collard70c29b42019-02-26 18:38:25 +080010#include <vector>
11
12#include <base/logging.h>
13#include <crypto/scoped_openssl_types.h>
14#include <openssl/bn.h>
15#include <openssl/ecdsa.h>
16#include <openssl/sha.h>
Louis Collardf5a8c3d2019-02-25 12:02:18 +080017#include <openssl/x509.h>
Louis Collard70c29b42019-02-26 18:38:25 +080018
Yicheng Li1bbf5292021-01-25 17:19:56 -080019#include "u2fd/tpm_vendor_cmd.h"
20
Louis Collard70c29b42019-02-26 18:38:25 +080021namespace u2f {
22namespace util {
23
24template <>
25void AppendToVector(const std::vector<uint8_t>& from,
26 std::vector<uint8_t>* to) {
27 to->insert(to->end(), from.begin(), from.end());
28}
29
30template <>
31void AppendToVector(const std::string& from, std::vector<uint8_t>* to) {
32 to->insert(to->end(), from.begin(), from.end());
33}
34
35void AppendSubstringToVector(const std::string& from,
36 int start,
37 int length,
38 std::vector<uint8_t>* to) {
39 to->insert(to->end(), from.begin() + start, from.begin() + start + length);
40}
41
Yicheng Lif5949a02020-03-27 11:34:41 -070042std::vector<uint8_t> ToVector(const std::string& str) {
43 std::vector<uint8_t> vect;
44 util::AppendToVector(str, &vect);
45 return vect;
46}
47
Louis Collard70c29b42019-02-26 18:38:25 +080048base::Optional<std::vector<uint8_t>> SignatureToDerBytes(const uint8_t* r,
49 const uint8_t* s) {
Daniel Kurtz540ff0e2019-07-15 19:55:52 -060050 crypto::ScopedBIGNUM sig_r(BN_new()), sig_s(BN_new());
Louis Collard70c29b42019-02-26 18:38:25 +080051 crypto::ScopedECDSA_SIG sig(ECDSA_SIG_new());
Daniel Kurtz540ff0e2019-07-15 19:55:52 -060052 if (!sig_r || !sig_s || !sig) {
53 LOG(ERROR) << "Failed to allocate ECDSA_SIG or BIGNUM.";
54 return base::nullopt;
55 }
Tom Hughes0f7203b2020-08-24 18:29:15 -070056 if (!BN_bin2bn(r, 32, sig_r.get()) || !BN_bin2bn(s, 32, sig_s.get())) {
Daniel Kurtz540ff0e2019-07-15 19:55:52 -060057 LOG(ERROR) << "Failed to convert ECDSA_SIG parameters to BIGNUM";
58 return base::nullopt;
59 }
Louis Collard70c29b42019-02-26 18:38:25 +080060
Daniel Kurtz540ff0e2019-07-15 19:55:52 -060061 if (!ECDSA_SIG_set0(sig.get(), sig_r.release(), sig_s.release())) {
Louis Collard70c29b42019-02-26 18:38:25 +080062 LOG(ERROR) << "Failed to initialize ECDSA_SIG";
63 return base::nullopt;
64 }
65
66 int sig_len = i2d_ECDSA_SIG(sig.get(), nullptr);
67
68 std::vector<uint8_t> signature(sig_len);
69 uint8_t* sig_ptr = &signature[0];
70
71 if (i2d_ECDSA_SIG(sig.get(), &sig_ptr) != sig_len) {
72 LOG(ERROR) << "DER encoding returned unexpected length";
73 return base::nullopt;
74 }
75
76 return signature;
77}
78
Yicheng Li1bbf5292021-01-25 17:19:56 -080079bool DoSoftwareAttest(const std::vector<uint8_t>& data_to_sign,
80 std::vector<uint8_t>* attestation_cert,
81 std::vector<uint8_t>* signature) {
82 crypto::ScopedEC_KEY attestation_key = util::CreateAttestationKey();
83 if (!attestation_key) {
84 return false;
85 }
86
87 base::Optional<std::vector<uint8_t>> cert_result =
88 util::CreateAttestationCertificate(attestation_key.get());
89 base::Optional<std::vector<uint8_t>> attest_result =
90 util::AttestToData(data_to_sign, attestation_key.get());
91
92 if (!cert_result.has_value() || !attest_result.has_value()) {
93 // These functions are never expected to fail.
94 LOG(ERROR) << "U2F software attestation failed.";
95 return false;
96 }
97
98 *attestation_cert = std::move(*cert_result);
99 *signature = std::move(*attest_result);
100 return true;
101}
102
Louis Collardf5a8c3d2019-02-25 12:02:18 +0800103crypto::ScopedEC_KEY CreateAttestationKey() {
104 crypto::ScopedEC_KEY key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
105 EC_KEY_set_asn1_flag(key.get(), OPENSSL_EC_NAMED_CURVE);
106
107 if (!key || !EC_KEY_generate_key(key.get())) {
108 LOG(ERROR) << "Failed to generate U2F attestation key.";
109 return nullptr;
110 } else {
111 return key;
112 }
113}
114
115base::Optional<std::vector<uint8_t>> AttestToData(
116 const std::vector<uint8_t>& data, EC_KEY* attestation_key) {
117 std::vector<uint8_t> digest = Sha256(data);
118
119 std::vector<uint8_t> signature(ECDSA_size(attestation_key));
120 unsigned int signature_length;
121
122 if (!ECDSA_sign(0 /* type: ignored by OpenSSL */, &digest[0], digest.size(),
123 signature.data(), &signature_length, attestation_key)) {
124 LOG(ERROR) << "Failed to sign data using U2F attestation key";
125 return base::nullopt;
126 }
127
128 signature.resize(signature_length);
129 return signature;
130}
131
132namespace {
133
134template <typename C>
135crypto::ScopedOpenSSL<X509, X509_free> ParseX509(const C& container) {
136 const unsigned char* parse_ptr =
137 static_cast<const unsigned char*>(&container[0]);
138
139 crypto::ScopedOpenSSL<X509, X509_free> cert(
140 d2i_X509(nullptr /* create and return new X509 struct */, &parse_ptr,
141 container.size()));
142
143 if (!cert) {
144 LOG(ERROR) << "Failed to parse X509 certificate.";
145 }
146
147 return cert;
148}
149
150base::Optional<std::vector<uint8_t>> DerEncodeCertificate(X509* cert) {
151 int cert_size = i2d_X509(cert, nullptr);
152 if (cert_size < 0) {
153 LOG(ERROR) << "Failed to DER-encode X509 certficate, error: " << cert_size;
154 return base::nullopt;
155 }
156
157 std::vector<uint8_t> cert_der(cert_size);
158 unsigned char* output_ptr = &cert_der[0];
159
160 if (i2d_X509(cert, &output_ptr) != cert_size) {
161 LOG(ERROR) << "X509 DER-encoding returned unexpected size, expected "
162 << cert_size;
163 return base::nullopt;
164 }
165
166 return cert_der;
167}
168
169} // namespace
170
171base::Optional<std::vector<uint8_t>> CreateAttestationCertificate(
172 EC_KEY* attestation_key) {
173 // We use a fixed template for the X509 certificate rather than generating one
174 // using OpenSSL, so that we can ensure that u2fd and cr50 both return
175 // certificates with the same structure.
176 // The array below is generated by the openssl tool from the template in
177 // x509_tmpl.txt.
178 constexpr std::array<unsigned char, 164> cert_template = {
179 0x30, 0x81, 0xA1, 0x30, 0x81, 0x8E, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02,
180 0x01, 0x00, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04,
181 0x03, 0x02, 0x30, 0x0F, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04,
182 0x03, 0x13, 0x04, 0x63, 0x72, 0x35, 0x30, 0x30, 0x22, 0x18, 0x0F, 0x32,
183 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30,
184 0x30, 0x5A, 0x18, 0x0F, 0x32, 0x30, 0x39, 0x39, 0x31, 0x32, 0x33, 0x31,
185 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5A, 0x30, 0x0F, 0x31, 0x0D, 0x30,
186 0x0B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x04, 0x63, 0x72, 0x35, 0x30,
187 0x30, 0x19, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
188 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03,
189 0x02, 0x00, 0x00, 0xA3, 0x17, 0x30, 0x15, 0x30, 0x13, 0x06, 0x0B, 0x2B,
190 0x06, 0x01, 0x04, 0x01, 0x82, 0xE5, 0x1C, 0x02, 0x01, 0x01, 0x04, 0x04,
191 0x03, 0x02, 0x03, 0x08, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE,
192 0x3D, 0x04, 0x03, 0x02, 0x03, 0x02, 0x00, 0x00,
193 };
194
195 crypto::ScopedOpenSSL<X509, X509_free> cert = ParseX509(cert_template);
196 if (!cert) {
197 return base::nullopt;
198 }
199
200 crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
201 if (!pkey || !EVP_PKEY_set1_EC_KEY(pkey.get(), attestation_key)) {
202 LOG(ERROR) << "Failed to create EVP_PKEY";
203 return base::nullopt;
204 }
205
206 if (!X509_set_pubkey(cert.get(), pkey.get()) ||
207 X509_sign(cert.get(), pkey.get(), EVP_sha256()) <=
208 0 /* returns length on succes */) {
209 LOG(ERROR) << "Failed to update X509 pubkey and signature fields";
210 return base::nullopt;
211 }
212
213 return DerEncodeCertificate(cert.get());
214}
215
216bool RemoveCertificatePadding(std::vector<uint8_t>* cert_in) {
217 const unsigned char* cert_start = &cert_in->front();
218 const unsigned char* parse_ptr = cert_start;
219
220 crypto::ScopedOpenSSL<X509, X509_free> cert(
221 d2i_X509(nullptr /* create and return new X509 struct */, &parse_ptr,
222 cert_in->size()));
223
224 if (!cert) {
225 LOG(ERROR) << "Failed to parse X509 certificate.";
226 return false;
227 }
228
229 size_t cert_size = parse_ptr - cert_start;
230
231 if (cert_size > cert_in->size()) {
232 LOG(ERROR) << "Unexpectedly parsed X509 cert larger than input buffer.";
233 return false;
234 }
235
236 cert_in->resize(cert_size);
237 return true;
238}
239
Yicheng Li1bbf5292021-01-25 17:19:56 -0800240base::Optional<std::vector<uint8_t>> GetG2fCert(TpmVendorCommandProxy* proxy) {
241 std::string cert_str;
242 std::vector<uint8_t> cert;
243
244 uint32_t get_cert_status = proxy->GetG2fCertificate(&cert_str);
245
246 if (get_cert_status != 0) {
247 LOG(ERROR) << "Failed to retrieve G2F certificate, status: " << std::hex
248 << get_cert_status;
249 return base::nullopt;
250 }
251
252 util::AppendToVector(cert_str, &cert);
253
254 if (!util::RemoveCertificatePadding(&cert)) {
255 LOG(ERROR) << "Failed to remove padding from G2F certificate ";
256 return base::nullopt;
257 }
258
259 return cert;
260}
261
262std::vector<uint8_t> BuildU2fRegisterResponseSignedData(
263 const std::vector<uint8_t>& app_id,
264 const std::vector<uint8_t>& challenge,
265 const std::vector<uint8_t>& pub_key,
266 const std::vector<uint8_t>& key_handle) {
267 std::vector<uint8_t> signed_data;
268 signed_data.push_back('\0'); // reserved byte
269 util::AppendToVector(app_id, &signed_data);
270 util::AppendToVector(challenge, &signed_data);
271 util::AppendToVector(key_handle, &signed_data);
272 util::AppendToVector(pub_key, &signed_data);
273 return signed_data;
274}
275
Louis Collard70c29b42019-02-26 18:38:25 +0800276} // namespace util
277} // namespace u2f