ed25519_public_key.cc 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. // Copyright 2020 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/ed25519_public_key.h"
  5. #include <utility>
  6. #include "base/memory/raw_ptr_exclusion.h"
  7. #include "components/cbor/writer.h"
  8. #include "device/fido/cbor_extract.h"
  9. #include "device/fido/fido_constants.h"
  10. #include "third_party/boringssl/src/include/openssl/bn.h"
  11. #include "third_party/boringssl/src/include/openssl/bytestring.h"
  12. #include "third_party/boringssl/src/include/openssl/evp.h"
  13. #include "third_party/boringssl/src/include/openssl/mem.h"
  14. #include "third_party/boringssl/src/include/openssl/obj.h"
  15. #include "third_party/boringssl/src/include/openssl/rsa.h"
  16. using device::cbor_extract::IntKey;
  17. using device::cbor_extract::Is;
  18. using device::cbor_extract::StepOrByte;
  19. using device::cbor_extract::Stop;
  20. namespace device {
  21. // static
  22. std::unique_ptr<PublicKey> Ed25519PublicKey::ExtractFromCOSEKey(
  23. int32_t algorithm,
  24. base::span<const uint8_t> cbor_bytes,
  25. const cbor::Value::MapValue& map) {
  26. // See https://tools.ietf.org/html/rfc8152#section-13.2
  27. struct COSEKey {
  28. // All the fields below are not a raw_ptr<,,,>, because ELEMENT() treats the
  29. // raw_ptr<T> as a void*, skipping AddRef() call and causing a ref-counting
  30. // mismatch.
  31. RAW_PTR_EXCLUSION const int64_t* kty;
  32. RAW_PTR_EXCLUSION const int64_t* crv;
  33. RAW_PTR_EXCLUSION const std::vector<uint8_t>* key;
  34. } cose_key;
  35. static constexpr cbor_extract::StepOrByte<COSEKey> kSteps[] = {
  36. // clang-format off
  37. ELEMENT(Is::kRequired, COSEKey, kty),
  38. IntKey<COSEKey>(static_cast<int>(CoseKeyKey::kKty)),
  39. ELEMENT(Is::kRequired, COSEKey, crv),
  40. IntKey<COSEKey>(static_cast<int>(CoseKeyKey::kEllipticCurve)),
  41. ELEMENT(Is::kRequired, COSEKey, key),
  42. IntKey<COSEKey>(static_cast<int>(CoseKeyKey::kEllipticX)),
  43. Stop<COSEKey>(),
  44. // clang-format on
  45. };
  46. if (!cbor_extract::Extract<COSEKey>(&cose_key, kSteps, map) ||
  47. *cose_key.kty != static_cast<int64_t>(CoseKeyTypes::kOKP) ||
  48. *cose_key.crv != static_cast<int64_t>(CoseCurves::kEd25519) ||
  49. cose_key.key->size() != 32) {
  50. return nullptr;
  51. }
  52. // The COSE RFC says that "This contains the x-coordinate for the EC point".
  53. // The RFC authors do not appear to understand what's going on because it
  54. // actually just contains the Ed25519 public key, which you would expect, and
  55. // which also encodes the y-coordinate as a sign bit.
  56. //
  57. // We could attempt to check whether |key| contains a quadratic residue, as it
  58. // should. But that would involve diving into the guts of Ed25519 too much.
  59. bssl::UniquePtr<EVP_PKEY> pkey(
  60. EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, /*engine=*/nullptr,
  61. cose_key.key->data(), cose_key.key->size()));
  62. if (!pkey) {
  63. return nullptr;
  64. }
  65. bssl::ScopedCBB cbb;
  66. uint8_t* der_bytes = nullptr;
  67. size_t der_bytes_len = 0;
  68. CHECK(CBB_init(cbb.get(), /* initial size */ 128) &&
  69. EVP_marshal_public_key(cbb.get(), pkey.get()) &&
  70. CBB_finish(cbb.get(), &der_bytes, &der_bytes_len));
  71. std::vector<uint8_t> der_bytes_vec(der_bytes, der_bytes + der_bytes_len);
  72. OPENSSL_free(der_bytes);
  73. return std::make_unique<PublicKey>(algorithm, cbor_bytes,
  74. std::move(der_bytes_vec));
  75. }
  76. } // namespace device