| // Copyright 2020 The Chromium OS 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 "cryptohome/challenge_credentials/fido_utils.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/logging.h> |
| #include <brillo/secure_blob.h> |
| |
| #include "cryptohome/cryptolib.h" |
| #include "cryptohome/fido.pb.h" |
| |
| namespace cryptohome { |
| |
| constexpr char kCrosLoginPrefix[] = "chromeos:login:"; |
| constexpr char kIconUrl[] = "http://www.noicon.google.com"; |
| // Default FIDO request timeout 30s. |
| constexpr base::TimeDelta kRequestTimeOut = base::TimeDelta::FromSeconds(30); |
| |
| // MakeCredential only uses ES256 algorithms. |
| constexpr int kCOSEAlgorithmIdentifierES256 = -7; |
| |
| std::vector<uint8_t> GetFidoUserId(const std::string& account_id) { |
| auto digest = |
| CryptoLib::Sha256ToSecureBlob(brillo::BlobFromString(account_id)); |
| return std::vector<uint8_t>(digest.begin(), digest.end()); |
| } |
| |
| std::unique_ptr<cryptohome::fido::PublicKeyCredentialRpEntity> |
| GetPublicKeyCredentialRpEntity(const std::string& id, const std::string& name) { |
| auto rp = std::make_unique<cryptohome::fido::PublicKeyCredentialRpEntity>(); |
| rp->set_id(id); |
| rp->set_name(name); |
| return rp; |
| } |
| |
| // Create User entity. Note the id is a 32-byte unique identifier derived from |
| // AccountIdentifier.account_id. |
| std::unique_ptr<cryptohome::fido::PublicKeyCredentialUserEntity> |
| GetPublicKeyCredentialUserEntity(const std::string& name, |
| const std::vector<uint8_t>& id, |
| const std::string& url_str, |
| const std::string& display_name) { |
| auto user = |
| std::make_unique<cryptohome::fido::PublicKeyCredentialUserEntity>(); |
| user->set_name(name); |
| user->set_id(std::string(id.begin(), id.end())); |
| auto url = new cryptohome::fido::Url(); |
| url->set_url(url_str); |
| user->set_allocated_icon(url); |
| user->set_display_name(display_name); |
| return user; |
| } |
| |
| FidoPKCredCreationOptionsPtr BuildFidoMakeCredentialOptions( |
| std::unique_ptr<cryptohome::fido::PublicKeyCredentialUserEntity> user, |
| std::unique_ptr<cryptohome::fido::PublicKeyCredentialRpEntity> rp, |
| std::vector<uint8_t> challenge, |
| std::vector<cryptohome::fido::PublicKeyCredentialDescriptor> |
| exclude_credentials, |
| base::TimeDelta adjusted_timeout, |
| std::unique_ptr<cryptohome::fido::CableRegistration> |
| cable_registration_data, |
| cryptohome::fido::ProtectionPolicy protection_policy, |
| bool use_hmac_secret, |
| bool enforce_protection_policy, |
| std::string appid_exclude) { |
| auto options = |
| std::make_unique<cryptohome::fido::PublicKeyCredentialCreationOptions>(); |
| |
| // Owned by |options| and lifecycle managed by |options|. |
| options->set_allocated_relying_party(rp.release()); |
| options->set_allocated_user(user.release()); |
| auto param = options->add_public_key_parameters(); |
| param->set_type(cryptohome::fido::PUBLIC_KEY); |
| param->set_algorithm_identifier(kCOSEAlgorithmIdentifierES256); |
| |
| options->set_adjusted_timeout(adjusted_timeout.InMilliseconds()); |
| options->set_challenge({challenge.begin(), challenge.end()}); |
| |
| // TODO(xzhou): need to implement attestation signature checking. |
| options->set_attestation(cryptohome::fido::NONE_ATTESTATION_PREFERENCE); |
| |
| // Specify the RP's authenticator attributes requirements. |
| auto authenticator_selection = |
| std::make_unique<cryptohome::fido::AuthenticatorSelectionCriteria>(); |
| |
| authenticator_selection->set_authenticator_attachment( |
| cryptohome::fido::NO_PREFERENCE /* equivalent to any or unset */); |
| authenticator_selection->set_require_resident_key(false); |
| // TODO(xzhou): If security key supports user verification, we may allow |
| // security key as single factor authentication. |
| authenticator_selection->set_user_verification(cryptohome::fido::DISCOURAGED); |
| options->set_allocated_authenticator_selection( |
| authenticator_selection.release()); |
| |
| // A list of credentials RP knows about. If an authenticator has one of these |
| // credentials, it should not create a new one. |
| for (auto credential : exclude_credentials) { |
| auto next = options->add_exclude_credentials(); |
| // Deep copy. |
| next->CopyFrom(credential); |
| } |
| |
| if (cable_registration_data) { |
| options->set_allocated_cable_registration_data( |
| cable_registration_data.release()); |
| } |
| |
| options->set_hmac_create_secret(use_hmac_secret); |
| if (protection_policy != cryptohome::fido::ProtectionPolicy::UNSPECIFIED) |
| options->set_protection_policy(protection_policy); |
| options->set_enforce_protection_policy(enforce_protection_policy); |
| |
| if (!appid_exclude.empty()) { |
| options->set_appid_exclude(appid_exclude); |
| } |
| return options; |
| } |
| |
| FidoPKCredCreationOptionsPtr BuildFidoMakeCredentialOptions( |
| const cryptohome::AccountIdentifier& account, |
| const std::vector<uint8_t>& challenge, |
| bool create_hmac_secret) { |
| if (account.account_id() == "") |
| return nullptr; |
| |
| std::vector<uint8_t> fido_account_id = GetFidoUserId(account.account_id()); |
| auto user = GetPublicKeyCredentialUserEntity( |
| account.email(), /* name */ |
| fido_account_id, /* id */ |
| kIconUrl, account.email() /* display name */); |
| |
| auto rp = GetPublicKeyCredentialRpEntity( |
| kCrosLoginPrefix + account.account_id(), /* the relying party id */ |
| "Chrome OS Login" /* relying party name */); |
| |
| return BuildFidoMakeCredentialOptions( |
| std::move(user), std::move(rp), challenge, {}, /* exclude credentials */ |
| kRequestTimeOut, nullptr, /* cable registration data */ |
| cryptohome::fido::ProtectionPolicy::UNSPECIFIED, create_hmac_secret, |
| false, /* enforce protection policy */ |
| "" /* appid exclude */); |
| } |
| |
| FidoPKCredRequestOptionsPtr BuildFidoGetAssertionOptions( |
| std::vector<uint8_t> challenge, |
| int64_t adjusted_timeout, |
| std::string relying_party_id, |
| std::vector<cryptohome::fido::PublicKeyCredentialDescriptor> |
| allow_credentials, |
| std::string appid, |
| std::vector<cryptohome::fido::CableAuthentication> |
| cable_authentication_data) { |
| auto options = |
| std::make_unique<cryptohome::fido::PublicKeyCredentialRequestOptions>(); |
| std::string str_challenge(challenge.begin(), challenge.end()); |
| options->set_challenge(str_challenge); |
| options->set_adjusted_timeout(adjusted_timeout); |
| options->set_relying_party_id(relying_party_id); |
| |
| for (auto& credential : allow_credentials) { |
| auto next = options->add_allow_credentials(); |
| next->CopyFrom(credential); |
| } |
| |
| options->set_appid(appid); |
| for (const auto& cable_authentication : cable_authentication_data) { |
| auto next = options->add_cable_authentication_data(); |
| next->CopyFrom(cable_authentication); |
| } |
| return options; |
| } |
| |
| FidoPKCredRequestOptionsPtr BuildFidoGetAssertionOptions( |
| std::vector<uint8_t> challenge, |
| std::string relying_party_id, |
| std::string appid) { |
| return BuildFidoGetAssertionOptions( |
| challenge, kRequestTimeOut.InMilliseconds(), relying_party_id, |
| {}, /* allow credentials */ |
| appid, {} /* Cloud Assisted BLE authentication data */); |
| } |
| |
| } // namespace cryptohome |