blob: ec79d81a46f7c742c046cca84683a2cdc253f892 [file] [log] [blame]
// Copyright 2018 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.
#ifndef DEVICE_FIDO_MAC_CREDENTIAL_METADATA_H_
#define DEVICE_FIDO_MAC_CREDENTIAL_METADATA_H_
#include <memory>
#include <string>
#include <vector>
#include "base/component_export.h"
#include "base/containers/span.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/strings/string_piece_forward.h"
#include "crypto/aead.h"
#include "crypto/hmac.h"
#include "crypto/symmetric_key.h"
namespace device {
class PublicKeyCredentialUserEntity;
namespace fido {
namespace mac {
// CredentialMetadata generates credential IDs from the associated user entity
// (user ID, name and display name) by encrypting them under a key tied to the
// current Chrome profile. This gives us separation of credentials per Chrome
// profile. It also guarantees that account metadata in the OS keychain is
// rendered unusable after the Chrome profile and the associated encryption key
// have been deleted, in order to limit leakage of account metadata, such as
// the list of RPs with registered credentials, into the OS keychain.
//
// Credential IDs have following format
// | version | nonce | AEAD(pt=CBOR(user_entity), |
// | (1 byte) | (12 bytes) | nonce=nonce, |
// | | | ad=(version, rpID)) |
// with version as 0x00, a random 12-byte nonce, and using AES-256-GCM as the
// AEAD.
//
// CredentialMetadata also encodes the user ID and RP ID for storage in the OS
// keychain by computing their HMAC.
//
// TODO(martinkr): We currently do not store profile icon URLs.
class COMPONENT_EXPORT(DEVICE_FIDO) CredentialMetadata {
public:
// Generate a new random secret to use with the public interface of
// CredentialMetadata. Chrome stores this secret in the Profile Prefs.
static std::string GenerateRandomSecret();
// UserEntity loosely corresponds to a PublicKeyCredentialUserEntity
// (https://www.w3.org/TR/webauthn/#sctn-user-credential-params). Values of
// this type should be moved whenever possible.
struct UserEntity {
public:
static UserEntity FromPublicKeyCredentialUserEntity(
const PublicKeyCredentialUserEntity&);
UserEntity(std::vector<uint8_t> id_,
std::string name_,
std::string display_);
UserEntity(const UserEntity&);
UserEntity(UserEntity&&);
UserEntity& operator=(UserEntity&&);
~UserEntity();
PublicKeyCredentialUserEntity ToPublicKeyCredentialUserEntity();
std::vector<uint8_t> id;
std::string name;
std::string display_name;
};
// SealCredentialId encrypts the given UserEntity into a credential id.
static base::Optional<std::vector<uint8_t>> SealCredentialId(
const std::string& secret,
const std::string& rp_id,
const UserEntity& user);
// UnsealCredentialId attempts to decrypt a UserEntity from a given credential
// id.
static base::Optional<UserEntity> UnsealCredentialId(
const std::string& secret,
const std::string& rp_id,
base::span<const uint8_t> credential_id);
// EncodeRpIdAndUserId encodes the concatenation of RP ID and user ID for
// storage in the macOS keychain.
static base::Optional<std::string> EncodeRpIdAndUserId(
const std::string& secret,
const std::string& rp_id,
base::span<const uint8_t> user_id);
// EncodeRpId encodes the given RP ID for storage in the macOS keychain.
static base::Optional<std::string> EncodeRpId(const std::string& secret,
const std::string& rp_id);
// DecodeRpId attempts to decode a given RP ID from the keychain. This can be
// used to test whether a set of credential metadata was created under the
// given secret without knowing the RP ID (which would be required to unseal
// a credential ID).
static base::Optional<std::string> DecodeRpId(const std::string& secret,
const std::string& ciphertext);
private:
enum Algorithm : uint8_t {
kAes256Gcm = 0,
kHmacSha256 = 1,
kAes256GcmSiv = 2,
};
static constexpr uint8_t kVersion = 0x00;
// MakeAad returns the concatenation of |kVersion| and |rp_id|,
// which is used as the additional authenticated data (AAD) input to the AEAD.
static std::string MakeAad(const std::string& rp_id);
// Derives keys from the caller-provided secret to avoid using the same key
// for different algorithms.
static std::string DeriveKey(base::StringPiece secret, Algorithm alg);
static base::Optional<crypto::Aead::AeadAlgorithm> ToAeadAlgorithm(
Algorithm alg);
CredentialMetadata(const std::string& secret);
~CredentialMetadata();
base::Optional<std::string> Seal(Algorithm alg,
base::span<const uint8_t> nonce,
base::span<const uint8_t> plaintext,
base::StringPiece authenticated_data) const;
base::Optional<std::string> Unseal(
Algorithm alg,
base::span<const uint8_t> nonce,
base::span<const uint8_t> ciphertext,
base::StringPiece authenticated_data) const;
base::Optional<std::string> HmacForStorage(base::StringPiece data) const;
// Used to derive keys for the HMAC and AEAD operations. Chrome picks
// different secrets for each user profile. This ensures that credentials are
// logically tied to the Chrome user profile under which they were created.
const std::string& secret_;
DISALLOW_COPY_AND_ASSIGN(CredentialMetadata);
};
} // namespace mac
} // namespace fido
} // namespace device
#endif // DEVICE_FIDO_MAC_CREDENTIAL_METADATA_H_