Louis Collard | 70c29b4 | 2019-02-26 18:38:25 +0800 | [diff] [blame] | 1 | // 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 Collard | f5a8c3d | 2019-02-25 12:02:18 +0800 | [diff] [blame] | 7 | #include <array> |
Louis Collard | 70c29b4 | 2019-02-26 18:38:25 +0800 | [diff] [blame] | 8 | #include <string> |
Louis Collard | f5a8c3d | 2019-02-25 12:02:18 +0800 | [diff] [blame] | 9 | #include <utility> |
Louis Collard | 70c29b4 | 2019-02-26 18:38:25 +0800 | [diff] [blame] | 10 | #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 Collard | f5a8c3d | 2019-02-25 12:02:18 +0800 | [diff] [blame] | 17 | #include <openssl/x509.h> |
Louis Collard | 70c29b4 | 2019-02-26 18:38:25 +0800 | [diff] [blame] | 18 | |
Yicheng Li | 1bbf529 | 2021-01-25 17:19:56 -0800 | [diff] [blame] | 19 | #include "u2fd/tpm_vendor_cmd.h" |
| 20 | |
Louis Collard | 70c29b4 | 2019-02-26 18:38:25 +0800 | [diff] [blame] | 21 | namespace u2f { |
| 22 | namespace util { |
| 23 | |
| 24 | template <> |
| 25 | void AppendToVector(const std::vector<uint8_t>& from, |
| 26 | std::vector<uint8_t>* to) { |
| 27 | to->insert(to->end(), from.begin(), from.end()); |
| 28 | } |
| 29 | |
| 30 | template <> |
| 31 | void AppendToVector(const std::string& from, std::vector<uint8_t>* to) { |
| 32 | to->insert(to->end(), from.begin(), from.end()); |
| 33 | } |
| 34 | |
| 35 | void 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 Li | f5949a0 | 2020-03-27 11:34:41 -0700 | [diff] [blame] | 42 | std::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 Collard | 70c29b4 | 2019-02-26 18:38:25 +0800 | [diff] [blame] | 48 | base::Optional<std::vector<uint8_t>> SignatureToDerBytes(const uint8_t* r, |
| 49 | const uint8_t* s) { |
Daniel Kurtz | 540ff0e | 2019-07-15 19:55:52 -0600 | [diff] [blame] | 50 | crypto::ScopedBIGNUM sig_r(BN_new()), sig_s(BN_new()); |
Louis Collard | 70c29b4 | 2019-02-26 18:38:25 +0800 | [diff] [blame] | 51 | crypto::ScopedECDSA_SIG sig(ECDSA_SIG_new()); |
Daniel Kurtz | 540ff0e | 2019-07-15 19:55:52 -0600 | [diff] [blame] | 52 | if (!sig_r || !sig_s || !sig) { |
| 53 | LOG(ERROR) << "Failed to allocate ECDSA_SIG or BIGNUM."; |
| 54 | return base::nullopt; |
| 55 | } |
Tom Hughes | 0f7203b | 2020-08-24 18:29:15 -0700 | [diff] [blame] | 56 | if (!BN_bin2bn(r, 32, sig_r.get()) || !BN_bin2bn(s, 32, sig_s.get())) { |
Daniel Kurtz | 540ff0e | 2019-07-15 19:55:52 -0600 | [diff] [blame] | 57 | LOG(ERROR) << "Failed to convert ECDSA_SIG parameters to BIGNUM"; |
| 58 | return base::nullopt; |
| 59 | } |
Louis Collard | 70c29b4 | 2019-02-26 18:38:25 +0800 | [diff] [blame] | 60 | |
Daniel Kurtz | 540ff0e | 2019-07-15 19:55:52 -0600 | [diff] [blame] | 61 | if (!ECDSA_SIG_set0(sig.get(), sig_r.release(), sig_s.release())) { |
Louis Collard | 70c29b4 | 2019-02-26 18:38:25 +0800 | [diff] [blame] | 62 | 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 Li | 1bbf529 | 2021-01-25 17:19:56 -0800 | [diff] [blame] | 79 | bool 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 Collard | f5a8c3d | 2019-02-25 12:02:18 +0800 | [diff] [blame] | 103 | crypto::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 | |
| 115 | base::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 | |
| 132 | namespace { |
| 133 | |
| 134 | template <typename C> |
| 135 | crypto::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 | |
| 150 | base::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 | |
| 171 | base::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 | |
| 216 | bool 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 Li | 1bbf529 | 2021-01-25 17:19:56 -0800 | [diff] [blame] | 240 | base::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 | |
| 262 | std::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 Collard | 70c29b4 | 2019-02-26 18:38:25 +0800 | [diff] [blame] | 276 | } // namespace util |
| 277 | } // namespace u2f |