p256_public_key.cc 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // Copyright 2017 The Chromium 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. #include "device/fido/p256_public_key.h"
  5. #include <utility>
  6. #include "base/memory/raw_ptr_exclusion.h"
  7. #include "components/cbor/writer.h"
  8. #include "components/device_event_log/device_event_log.h"
  9. #include "device/fido/cbor_extract.h"
  10. #include "device/fido/fido_constants.h"
  11. #include "device/fido/public_key.h"
  12. #include "third_party/boringssl/src/include/openssl/bn.h"
  13. #include "third_party/boringssl/src/include/openssl/bytestring.h"
  14. #include "third_party/boringssl/src/include/openssl/ec.h"
  15. #include "third_party/boringssl/src/include/openssl/evp.h"
  16. #include "third_party/boringssl/src/include/openssl/mem.h"
  17. #include "third_party/boringssl/src/include/openssl/obj.h"
  18. using device::cbor_extract::IntKey;
  19. using device::cbor_extract::Is;
  20. using device::cbor_extract::StepOrByte;
  21. using device::cbor_extract::Stop;
  22. namespace device {
  23. // kFieldElementLength is the size of a P-256 field element. The field is
  24. // GF(2^256 - 2^224 + 2^192 + 2^96 - 1) and thus an element is 256 bits, or 32
  25. // bytes.
  26. constexpr size_t kFieldElementLength = 32;
  27. // kUncompressedPointLength is the size of an X9.62 uncompressed point over
  28. // P-256. It's one byte of type information followed by two field elements (x
  29. // and y).
  30. constexpr size_t kUncompressedPointLength = 1 + 2 * kFieldElementLength;
  31. namespace {
  32. // DERFromEC_POINT returns the ASN.1, DER, SubjectPublicKeyInfo for a given
  33. // elliptic-curve point.
  34. static std::vector<uint8_t> DERFromEC_POINT(const EC_POINT* point) {
  35. bssl::UniquePtr<EC_KEY> ec_key(
  36. EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
  37. CHECK(EC_KEY_set_public_key(ec_key.get(), point));
  38. bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
  39. CHECK(EVP_PKEY_assign_EC_KEY(pkey.get(), ec_key.release()));
  40. bssl::ScopedCBB cbb;
  41. uint8_t* der_bytes = nullptr;
  42. size_t der_bytes_len = 0;
  43. CHECK(CBB_init(cbb.get(), /* initial size */ 128) &&
  44. EVP_marshal_public_key(cbb.get(), pkey.get()) &&
  45. CBB_finish(cbb.get(), &der_bytes, &der_bytes_len));
  46. std::vector<uint8_t> ret(der_bytes, der_bytes + der_bytes_len);
  47. OPENSSL_free(der_bytes);
  48. return ret;
  49. }
  50. } // namespace
  51. // static
  52. std::unique_ptr<PublicKey> P256PublicKey::ExtractFromU2fRegistrationResponse(
  53. int32_t algorithm,
  54. base::span<const uint8_t> u2f_data) {
  55. // In a U2F registration response, there is first a reserved byte that must be
  56. // ignored. Following that is the rest of the response.
  57. if (u2f_data.size() < 1 + kUncompressedPointLength) {
  58. return nullptr;
  59. }
  60. return ParseX962Uncompressed(algorithm,
  61. u2f_data.subspan(1, kUncompressedPointLength));
  62. }
  63. // static
  64. std::unique_ptr<PublicKey> P256PublicKey::ExtractFromCOSEKey(
  65. int32_t algorithm,
  66. base::span<const uint8_t> cbor_bytes,
  67. const cbor::Value::MapValue& map) {
  68. struct COSEKey {
  69. // All the fields below are not a raw_ptr<,,,>, because ELEMENT() treats the
  70. // raw_ptr<T> as a void*, skipping AddRef() call and causing a ref-counting
  71. // mismatch.
  72. RAW_PTR_EXCLUSION const int64_t* kty;
  73. RAW_PTR_EXCLUSION const int64_t* crv;
  74. RAW_PTR_EXCLUSION const std::vector<uint8_t>* x;
  75. RAW_PTR_EXCLUSION const std::vector<uint8_t>* y;
  76. } cose_key;
  77. static constexpr cbor_extract::StepOrByte<COSEKey> kSteps[] = {
  78. // clang-format off
  79. ELEMENT(Is::kRequired, COSEKey, kty),
  80. IntKey<COSEKey>(static_cast<int>(CoseKeyKey::kKty)),
  81. ELEMENT(Is::kRequired, COSEKey, crv),
  82. IntKey<COSEKey>(static_cast<int>(CoseKeyKey::kEllipticCurve)),
  83. ELEMENT(Is::kRequired, COSEKey, x),
  84. IntKey<COSEKey>(static_cast<int>(CoseKeyKey::kEllipticX)),
  85. ELEMENT(Is::kRequired, COSEKey, y),
  86. IntKey<COSEKey>(static_cast<int>(CoseKeyKey::kEllipticY)),
  87. Stop<COSEKey>(),
  88. // clang-format on
  89. };
  90. if (!cbor_extract::Extract<COSEKey>(&cose_key, kSteps, map) ||
  91. *cose_key.kty != static_cast<int64_t>(CoseKeyTypes::kEC2) ||
  92. *cose_key.crv != static_cast<int64_t>(CoseCurves::kP256) ||
  93. cose_key.x->size() != kFieldElementLength ||
  94. cose_key.y->size() != kFieldElementLength) {
  95. return nullptr;
  96. }
  97. bssl::UniquePtr<BIGNUM> x_bn(BN_new());
  98. bssl::UniquePtr<BIGNUM> y_bn(BN_new());
  99. if (!BN_bin2bn(cose_key.x->data(), cose_key.x->size(), x_bn.get()) ||
  100. !BN_bin2bn(cose_key.y->data(), cose_key.y->size(), y_bn.get())) {
  101. return nullptr;
  102. }
  103. // Parse into an |EC_POINT| to perform the validity checks that BoringSSL
  104. // does.
  105. bssl::UniquePtr<EC_GROUP> p256(
  106. EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
  107. bssl::UniquePtr<EC_POINT> point(EC_POINT_new(p256.get()));
  108. if (!EC_POINT_set_affine_coordinates_GFp(p256.get(), point.get(), x_bn.get(),
  109. y_bn.get(), /*ctx=*/nullptr)) {
  110. FIDO_LOG(ERROR) << "P-256 public key is not on curve";
  111. return nullptr;
  112. }
  113. return std::make_unique<PublicKey>(algorithm, cbor_bytes,
  114. DERFromEC_POINT(point.get()));
  115. }
  116. // static
  117. std::unique_ptr<PublicKey> P256PublicKey::ParseX962Uncompressed(
  118. int32_t algorithm,
  119. base::span<const uint8_t> x962) {
  120. // Parse into an |EC_POINT| to perform the validity checks that BoringSSL
  121. // does.
  122. bssl::UniquePtr<EC_GROUP> p256(
  123. EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
  124. bssl::UniquePtr<EC_POINT> point(EC_POINT_new(p256.get()));
  125. if (x962.size() != kUncompressedPointLength ||
  126. x962[0] != POINT_CONVERSION_UNCOMPRESSED ||
  127. !EC_POINT_oct2point(p256.get(), point.get(), x962.data(), x962.size(),
  128. /*ctx=*/nullptr)) {
  129. FIDO_LOG(ERROR) << "P-256 public key is not on curve";
  130. return nullptr;
  131. }
  132. base::span<const uint8_t, kFieldElementLength> x(&x962[1],
  133. kFieldElementLength);
  134. base::span<const uint8_t, kFieldElementLength> y(
  135. &x962[1 + kFieldElementLength], kFieldElementLength);
  136. cbor::Value::MapValue map;
  137. map.emplace(static_cast<int>(CoseKeyKey::kKty),
  138. static_cast<int64_t>(CoseKeyTypes::kEC2));
  139. map.emplace(static_cast<int>(CoseKeyKey::kAlg), algorithm);
  140. map.emplace(static_cast<int>(CoseKeyKey::kEllipticCurve),
  141. static_cast<int64_t>(CoseCurves::kP256));
  142. map.emplace(static_cast<int>(CoseKeyKey::kEllipticX), x);
  143. map.emplace(static_cast<int>(CoseKeyKey::kEllipticY), y);
  144. const std::vector<uint8_t> cbor_bytes(
  145. std::move(cbor::Writer::Write(cbor::Value(std::move(map))).value()));
  146. return std::make_unique<PublicKey>(algorithm, cbor_bytes,
  147. DERFromEC_POINT(point.get()));
  148. }
  149. } // namespace device