diff options
Diffstat (limited to 'chromium/device/fido/ed25519_public_key.cc')
-rw-r--r-- | chromium/device/fido/ed25519_public_key.cc | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/chromium/device/fido/ed25519_public_key.cc b/chromium/device/fido/ed25519_public_key.cc new file mode 100644 index 00000000000..ff86b9e7c8f --- /dev/null +++ b/chromium/device/fido/ed25519_public_key.cc @@ -0,0 +1,89 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/fido/ed25519_public_key.h" + +#include <utility> + +#include "components/cbor/writer.h" +#include "device/fido/cbor_extract.h" +#include "device/fido/fido_constants.h" +#include "third_party/boringssl/src/include/openssl/bn.h" +#include "third_party/boringssl/src/include/openssl/bytestring.h" +#include "third_party/boringssl/src/include/openssl/evp.h" +#include "third_party/boringssl/src/include/openssl/mem.h" +#include "third_party/boringssl/src/include/openssl/obj.h" +#include "third_party/boringssl/src/include/openssl/rsa.h" + +using device::cbor_extract::IntKey; +using device::cbor_extract::Is; +using device::cbor_extract::StepOrByte; +using device::cbor_extract::Stop; + +namespace device { + +// static +std::unique_ptr<PublicKey> Ed25519PublicKey::ExtractFromCOSEKey( + int32_t algorithm, + base::span<const uint8_t> cbor_bytes, + const cbor::Value::MapValue& map) { + // See https://tools.ietf.org/html/rfc8152#section-13.2 + struct COSEKey { + const int64_t* kty; + const int64_t* crv; + const std::vector<uint8_t>* key; + } cose_key; + + static constexpr cbor_extract::StepOrByte<COSEKey> kSteps[] = { + // clang-format off + ELEMENT(Is::kRequired, COSEKey, kty), + IntKey<COSEKey>(static_cast<int>(CoseKeyKey::kKty)), + + ELEMENT(Is::kRequired, COSEKey, crv), + IntKey<COSEKey>(static_cast<int>(CoseKeyKey::kEllipticCurve)), + + ELEMENT(Is::kRequired, COSEKey, key), + IntKey<COSEKey>(static_cast<int>(CoseKeyKey::kEllipticX)), + + Stop<COSEKey>(), + // clang-format on + }; + + if (!cbor_extract::Extract<COSEKey>(&cose_key, kSteps, map) || + *cose_key.kty != static_cast<int64_t>(CoseKeyTypes::kOKP) || + *cose_key.crv != static_cast<int64_t>(CoseCurves::kEd25519) || + cose_key.key->size() != 32) { + return nullptr; + } + + // The COSE RFC says that "This contains the x-coordinate for the EC point". + // The RFC authors do not appear to understand what's going on because it + // actually just contains the Ed25519 public key, which you would expect, and + // which also encodes the y-coordinate as a sign bit. + // + // We could attempt to check whether |key| contains a quadratic residue, as it + // should. But that would involve diving into the guts of Ed25519 too much. + + bssl::UniquePtr<EVP_PKEY> pkey( + EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, /*engine=*/nullptr, + cose_key.key->data(), cose_key.key->size())); + if (!pkey) { + return nullptr; + } + + bssl::ScopedCBB cbb; + uint8_t* der_bytes = nullptr; + size_t der_bytes_len = 0; + CHECK(CBB_init(cbb.get(), /* initial size */ 128) && + EVP_marshal_public_key(cbb.get(), pkey.get()) && + CBB_finish(cbb.get(), &der_bytes, &der_bytes_len)); + + std::vector<uint8_t> der_bytes_vec(der_bytes, der_bytes + der_bytes_len); + OPENSSL_free(der_bytes); + + return std::make_unique<PublicKey>(algorithm, cbor_bytes, + std::move(der_bytes_vec)); +} + +} // namespace device |