Commit 652394e7 authored by Martin Kreichgauer's avatar Martin Kreichgauer Committed by Commit Bot

fido/mac: simplify CredentialMetadata interface

Change the SealCredentialId(), EncodeRpId(), EncodeRpIdAndUserId() and
SealLegacyV0CredentialIdForTestingOnly() methods to not return base::Optional
in order to make them less clunky. No functional changes intended.

Change-Id: Ic7088e3e016c09b1df5e4bcdc665d72c5ea3286b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1963861Reviewed-by: default avatarAdam Langley <agl@chromium.org>
Commit-Queue: Martin Kreichgauer <martinkr@google.com>
Cr-Commit-Position: refs/heads/master@{#727456}
parent 9a8d8f4d
...@@ -19,10 +19,6 @@ namespace device { ...@@ -19,10 +19,6 @@ namespace device {
namespace fido { namespace fido {
namespace mac { namespace mac {
using cbor::Reader;
using cbor::Value;
using cbor::Writer;
// The version tag encoded into encrypted credential metadata. // The version tag encoded into encrypted credential metadata.
static constexpr uint8_t kVersionLegacy0 = 0x00; static constexpr uint8_t kVersionLegacy0 = 0x00;
...@@ -35,8 +31,10 @@ namespace { ...@@ -35,8 +31,10 @@ namespace {
// MakeAad returns the concatenation of |version| and |rp_id|, // MakeAad returns the concatenation of |version| and |rp_id|,
// which is used as the additional authenticated data (AAD) input to the AEAD. // which is used as the additional authenticated data (AAD) input to the AEAD.
std::string MakeAad(const uint8_t version, const std::string& rp_id) { std::vector<uint8_t> MakeAad(const uint8_t version, const std::string& rp_id) {
return std::string(1, version) + rp_id; std::vector<uint8_t> result = {version};
result.insert(result.end(), rp_id.data(), rp_id.data() + rp_id.size());
return result;
} }
// Cryptor provides methods for encrypting and authenticating credential // Cryptor provides methods for encrypting and authenticating credential
...@@ -54,18 +52,18 @@ class Cryptor { ...@@ -54,18 +52,18 @@ class Cryptor {
kAes256GcmSiv = 2, kAes256GcmSiv = 2,
}; };
base::Optional<std::string> Seal(Algorithm alg, std::vector<uint8_t> Seal(Algorithm alg,
base::span<const uint8_t> nonce, base::span<const uint8_t> nonce,
base::span<const uint8_t> plaintext, base::span<const uint8_t> plaintext,
base::StringPiece authenticated_data) const; base::span<const uint8_t> authenticated_data) const;
base::Optional<std::string> Unseal( base::Optional<std::vector<uint8_t>> Unseal(
Algorithm alg, Algorithm alg,
base::span<const uint8_t> nonce, base::span<const uint8_t> nonce,
base::span<const uint8_t> ciphertext, base::span<const uint8_t> ciphertext,
base::StringPiece authenticated_data) const; base::span<const uint8_t> authenticated_data) const;
base::Optional<std::string> HmacForStorage(base::StringPiece data) const; std::string HmacForStorage(base::StringPiece data) const;
private: private:
static base::Optional<crypto::Aead::AeadAlgorithm> ToAeadAlgorithm( static base::Optional<crypto::Aead::AeadAlgorithm> ToAeadAlgorithm(
...@@ -84,62 +82,35 @@ class Cryptor { ...@@ -84,62 +82,35 @@ class Cryptor {
std::string secret_; std::string secret_;
}; };
base::Optional<std::string> Cryptor::Seal( std::vector<uint8_t> Cryptor::Seal(
Algorithm algorithm, Algorithm algorithm,
base::span<const uint8_t> nonce, base::span<const uint8_t> nonce,
base::span<const uint8_t> plaintext, base::span<const uint8_t> plaintext,
base::StringPiece authenticated_data) const { base::span<const uint8_t> authenticated_data) const {
auto opt_aead_algorithm = ToAeadAlgorithm(algorithm);
if (!opt_aead_algorithm)
return base::nullopt;
const std::string key = DeriveKey(algorithm); const std::string key = DeriveKey(algorithm);
crypto::Aead aead(*opt_aead_algorithm); crypto::Aead aead(*ToAeadAlgorithm(algorithm));
aead.Init(&key); aead.Init(&key);
std::string ciphertext; return aead.Seal(plaintext, nonce, authenticated_data);
if (!aead.Seal(
base::StringPiece(reinterpret_cast<const char*>(plaintext.data()),
plaintext.size()),
base::StringPiece(reinterpret_cast<const char*>(nonce.data()),
nonce.size()),
authenticated_data, &ciphertext)) {
return base::nullopt;
}
return ciphertext;
} }
base::Optional<std::string> Cryptor::Unseal( base::Optional<std::vector<uint8_t>> Cryptor::Unseal(
Algorithm algorithm, Algorithm algorithm,
base::span<const uint8_t> nonce, base::span<const uint8_t> nonce,
base::span<const uint8_t> ciphertext, base::span<const uint8_t> ciphertext,
base::StringPiece authenticated_data) const { base::span<const uint8_t> authenticated_data) const {
auto opt_aead_algorithm = ToAeadAlgorithm(algorithm);
if (!opt_aead_algorithm)
return base::nullopt;
const std::string key = DeriveKey(algorithm); const std::string key = DeriveKey(algorithm);
crypto::Aead aead(*opt_aead_algorithm); crypto::Aead aead(*ToAeadAlgorithm(algorithm));
aead.Init(&key); aead.Init(&key);
std::string plaintext; return aead.Open(ciphertext, nonce, authenticated_data);
if (!aead.Open(
base::StringPiece(reinterpret_cast<const char*>(ciphertext.data()),
ciphertext.size()),
base::StringPiece(reinterpret_cast<const char*>(nonce.data()),
nonce.size()),
authenticated_data, &plaintext)) {
return base::nullopt;
}
return plaintext;
} }
base::Optional<std::string> Cryptor::HmacForStorage( std::string Cryptor::HmacForStorage(base::StringPiece data) const {
base::StringPiece data) const {
crypto::HMAC hmac(crypto::HMAC::SHA256); crypto::HMAC hmac(crypto::HMAC::SHA256);
const std::string key = DeriveKey(Algorithm::kHmacSha256); const std::string key = DeriveKey(Algorithm::kHmacSha256);
std::vector<uint8_t> digest(hmac.DigestLength()); std::vector<uint8_t> digest(hmac.DigestLength());
if (!hmac.Init(key) || !hmac.Sign(data, digest.data(), hmac.DigestLength())) { CHECK(hmac.Init(key));
return base::nullopt; CHECK(hmac.Sign(data, digest.data(), hmac.DigestLength()));
}
// The keychain fields that store RP ID and User ID seem to only accept // The keychain fields that store RP ID and User ID seem to only accept
// NSString (not NSData), so we HexEncode to ensure the result to be // NSString (not NSData), so we HexEncode to ensure the result to be
// UTF-8-decodable. // UTF-8-decodable.
...@@ -232,10 +203,9 @@ static std::string MaybeTruncateWithTrailingEllipsis(const std::string& in) { ...@@ -232,10 +203,9 @@ static std::string MaybeTruncateWithTrailingEllipsis(const std::string& in) {
return out; return out;
} }
base::Optional<std::vector<uint8_t>> SealCredentialId( std::vector<uint8_t> SealCredentialId(const std::string& secret,
const std::string& secret, const std::string& rp_id,
const std::string& rp_id, const CredentialMetadata& metadata) {
const CredentialMetadata& metadata) {
// The first 13 bytes are the version and nonce. // The first 13 bytes are the version and nonce.
std::vector<uint8_t> result(1 + kNonceLength); std::vector<uint8_t> result(1 + kNonceLength);
result[0] = kVersion; result[0] = kVersion;
...@@ -248,28 +218,22 @@ base::Optional<std::vector<uint8_t>> SealCredentialId( ...@@ -248,28 +218,22 @@ base::Optional<std::vector<uint8_t>> SealCredentialId(
// The remaining bytes are the CBOR-encoded CredentialMetadata, encrypted with // The remaining bytes are the CBOR-encoded CredentialMetadata, encrypted with
// AES-256-GCM and authenticated with the version and RP ID. // AES-256-GCM and authenticated with the version and RP ID.
Value::ArrayValue cbor_user; cbor::Value::ArrayValue cbor_user;
cbor_user.emplace_back(Value(metadata.user_id)); cbor_user.emplace_back(cbor::Value(metadata.user_id));
cbor_user.emplace_back( cbor_user.emplace_back(
Value(MaybeTruncateWithTrailingEllipsis(metadata.user_name), cbor::Value(MaybeTruncateWithTrailingEllipsis(metadata.user_name),
Value::Type::BYTE_STRING)); cbor::Value::Type::BYTE_STRING));
cbor_user.emplace_back( cbor_user.emplace_back(
Value(MaybeTruncateWithTrailingEllipsis(metadata.user_display_name), cbor::Value(MaybeTruncateWithTrailingEllipsis(metadata.user_display_name),
Value::Type::BYTE_STRING)); cbor::Value::Type::BYTE_STRING));
cbor_user.emplace_back(Value(metadata.is_resident)); cbor_user.emplace_back(cbor::Value(metadata.is_resident));
base::Optional<std::vector<uint8_t>> pt = base::Optional<std::vector<uint8_t>> pt =
Writer::Write(Value(std::move(cbor_user))); cbor::Writer::Write(cbor::Value(std::move(cbor_user)));
if (!pt) { DCHECK(pt);
return base::nullopt;
} const std::vector<uint8_t> ct = Cryptor(secret).Seal(
base::Optional<std::string> ciphertext = Cryptor(secret).Seal(
Cryptor::Algorithm::kAes256Gcm, nonce, *pt, MakeAad(kVersion, rp_id)); Cryptor::Algorithm::kAes256Gcm, nonce, *pt, MakeAad(kVersion, rp_id));
if (!ciphertext) { result.insert(result.end(), ct.begin(), ct.end());
return base::nullopt;
}
base::span<const char> cts(reinterpret_cast<const char*>(ciphertext->data()),
ciphertext->size());
result.insert(result.end(), cts.begin(), cts.end());
return result; return result;
} }
...@@ -290,7 +254,7 @@ static base::Optional<CredentialMetadata> UnsealLegacyCredentialId( ...@@ -290,7 +254,7 @@ static base::Optional<CredentialMetadata> UnsealLegacyCredentialId(
return base::nullopt; return base::nullopt;
} }
base::Optional<std::string> plaintext = Cryptor(secret).Unseal( base::Optional<std::vector<uint8_t>> plaintext = Cryptor(secret).Unseal(
Cryptor::Algorithm::kAes256Gcm, credential_id.subspan(1, kNonceLength), Cryptor::Algorithm::kAes256Gcm, credential_id.subspan(1, kNonceLength),
credential_id.subspan(1 + kNonceLength), MakeAad(kVersionLegacy0, rp_id)); credential_id.subspan(1 + kNonceLength), MakeAad(kVersionLegacy0, rp_id));
if (!plaintext) { if (!plaintext) {
...@@ -298,12 +262,11 @@ static base::Optional<CredentialMetadata> UnsealLegacyCredentialId( ...@@ -298,12 +262,11 @@ static base::Optional<CredentialMetadata> UnsealLegacyCredentialId(
} }
// The recovered plaintext should decode into the CredentialMetadata struct. // The recovered plaintext should decode into the CredentialMetadata struct.
base::Optional<Value> maybe_array = Reader::Read(base::make_span( base::Optional<cbor::Value> maybe_array = cbor::Reader::Read(*plaintext);
reinterpret_cast<const uint8_t*>(plaintext->data()), plaintext->size()));
if (!maybe_array || !maybe_array->is_array()) { if (!maybe_array || !maybe_array->is_array()) {
return base::nullopt; return base::nullopt;
} }
const Value::ArrayValue& array = maybe_array->GetArray(); const cbor::Value::ArrayValue& array = maybe_array->GetArray();
if (array.size() != 3 || !array[0].is_bytestring() || if (array.size() != 3 || !array[0].is_bytestring() ||
!array[1].is_bytestring() || !array[2].is_bytestring()) { !array[1].is_bytestring() || !array[2].is_bytestring()) {
return base::nullopt; return base::nullopt;
...@@ -327,7 +290,7 @@ base::Optional<CredentialMetadata> UnsealCredentialId( ...@@ -327,7 +290,7 @@ base::Optional<CredentialMetadata> UnsealCredentialId(
return base::nullopt; return base::nullopt;
} }
base::Optional<std::string> plaintext = Cryptor(secret).Unseal( base::Optional<std::vector<uint8_t>> plaintext = Cryptor(secret).Unseal(
Cryptor::Algorithm::kAes256Gcm, credential_id.subspan(1, kNonceLength), Cryptor::Algorithm::kAes256Gcm, credential_id.subspan(1, kNonceLength),
credential_id.subspan(1 + kNonceLength), MakeAad(kVersion, rp_id)); credential_id.subspan(1 + kNonceLength), MakeAad(kVersion, rp_id));
if (!plaintext) { if (!plaintext) {
...@@ -335,12 +298,12 @@ base::Optional<CredentialMetadata> UnsealCredentialId( ...@@ -335,12 +298,12 @@ base::Optional<CredentialMetadata> UnsealCredentialId(
} }
// The recovered plaintext should decode into the CredentialMetadata struct. // The recovered plaintext should decode into the CredentialMetadata struct.
base::Optional<Value> maybe_array = Reader::Read(base::make_span( base::Optional<cbor::Value> maybe_array = cbor::Reader::Read(base::make_span(
reinterpret_cast<const uint8_t*>(plaintext->data()), plaintext->size())); reinterpret_cast<const uint8_t*>(plaintext->data()), plaintext->size()));
if (!maybe_array || !maybe_array->is_array()) { if (!maybe_array || !maybe_array->is_array()) {
return base::nullopt; return base::nullopt;
} }
const Value::ArrayValue& array = maybe_array->GetArray(); const cbor::Value::ArrayValue& array = maybe_array->GetArray();
if (array.size() != 4 || !array[0].is_bytestring() || if (array.size() != 4 || !array[0].is_bytestring() ||
!array[1].is_bytestring() || !array[2].is_bytestring() || !array[1].is_bytestring() || !array[2].is_bytestring() ||
!array[3].is_bool()) { !array[3].is_bool()) {
...@@ -351,10 +314,9 @@ base::Optional<CredentialMetadata> UnsealCredentialId( ...@@ -351,10 +314,9 @@ base::Optional<CredentialMetadata> UnsealCredentialId(
array[2].GetBytestringAsString().as_string(), array[3].GetBool()); array[2].GetBytestringAsString().as_string(), array[3].GetBool());
} }
base::Optional<std::string> EncodeRpIdAndUserId( std::string EncodeRpIdAndUserId(const std::string& secret,
const std::string& secret, const std::string& rp_id,
const std::string& rp_id, base::span<const uint8_t> user_id) {
base::span<const uint8_t> user_id) {
// Encoding RP ID along with the user ID hides whether the same user ID was // Encoding RP ID along with the user ID hides whether the same user ID was
// reused on different RPs. // reused on different RPs.
const auto* user_id_data = reinterpret_cast<const char*>(user_id.data()); const auto* user_id_data = reinterpret_cast<const char*>(user_id.data());
...@@ -362,24 +324,21 @@ base::Optional<std::string> EncodeRpIdAndUserId( ...@@ -362,24 +324,21 @@ base::Optional<std::string> EncodeRpIdAndUserId(
rp_id + "/" + std::string(user_id_data, user_id_data + user_id.size())); rp_id + "/" + std::string(user_id_data, user_id_data + user_id.size()));
} }
base::Optional<std::string> EncodeRpId(const std::string& secret, std::string EncodeRpId(const std::string& secret, const std::string& rp_id) {
const std::string& rp_id) {
// Encrypt with a fixed nonce to make the result deterministic while still // Encrypt with a fixed nonce to make the result deterministic while still
// allowing the RP ID to be recovered from the ciphertext later. // allowing the RP ID to be recovered from the ciphertext later.
static constexpr std::array<uint8_t, kNonceLength> fixed_zero_nonce = {}; static constexpr std::array<uint8_t, kNonceLength> fixed_zero_nonce = {};
base::span<const uint8_t> pt(reinterpret_cast<const uint8_t*>(rp_id.data()), base::span<const uint8_t> pt(reinterpret_cast<const uint8_t*>(rp_id.data()),
rp_id.size()); rp_id.size());
std::string empty_ad;
// Using AES-GCM with a fixed nonce would break confidentiality, so this uses // Using AES-GCM with a fixed nonce would break confidentiality, so this uses
// AES-GCM-SIV instead. // AES-GCM-SIV instead.
base::Optional<std::string> ct = Cryptor(secret).Seal( const std::vector<uint8_t> ct =
Cryptor::Algorithm::kAes256GcmSiv, fixed_zero_nonce, pt, empty_ad); Cryptor(secret).Seal(Cryptor::Algorithm::kAes256GcmSiv, fixed_zero_nonce,
if (!ct) { pt, /*authenticated_data=*/{});
return base::nullopt;
}
// The keychain field that stores the encrypted RP ID only accepts NSString // The keychain field that stores the encrypted RP ID only accepts NSString
// (not NSData), so we HexEncode to ensure the result is UTF-8-decodable. // (not NSData), so we HexEncode to ensure the result is UTF-8-decodable.
return base::HexEncode(ct->data(), ct->size()); return base::HexEncode(ct.data(), ct.size());
} }
base::Optional<std::string> DecodeRpId(const std::string& secret, base::Optional<std::string> DecodeRpId(const std::string& secret,
...@@ -389,12 +348,16 @@ base::Optional<std::string> DecodeRpId(const std::string& secret, ...@@ -389,12 +348,16 @@ base::Optional<std::string> DecodeRpId(const std::string& secret,
return base::nullopt; return base::nullopt;
} }
static constexpr std::array<uint8_t, kNonceLength> fixed_zero_nonce = {}; static constexpr std::array<uint8_t, kNonceLength> fixed_zero_nonce = {};
std::string empty_ad; base::Optional<std::vector<uint8_t>> pt = Cryptor(secret).Unseal(
return Cryptor(secret).Unseal(Cryptor::Algorithm::kAes256GcmSiv, Cryptor::Algorithm::kAes256GcmSiv, fixed_zero_nonce, ct,
fixed_zero_nonce, ct, empty_ad); /*authenticated_data=*/{});
if (!pt) {
return base::nullopt;
}
return std::string(pt->begin(), pt->end());
} }
base::Optional<std::vector<uint8_t>> SealLegacyV0CredentialIdForTestingOnly( std::vector<uint8_t> SealLegacyV0CredentialIdForTestingOnly(
const std::string& secret, const std::string& secret,
const std::string& rp_id, const std::string& rp_id,
const std::vector<uint8_t>& user_id, const std::vector<uint8_t>& user_id,
...@@ -409,24 +372,22 @@ base::Optional<std::vector<uint8_t>> SealLegacyV0CredentialIdForTestingOnly( ...@@ -409,24 +372,22 @@ base::Optional<std::vector<uint8_t>> SealLegacyV0CredentialIdForTestingOnly(
base::span<uint8_t> nonce(result.data() + 1, 12); base::span<uint8_t> nonce(result.data() + 1, 12);
RAND_bytes(nonce.data(), nonce.size()); // RAND_bytes always returns 1. RAND_bytes(nonce.data(), nonce.size()); // RAND_bytes always returns 1.
Value::ArrayValue cbor_user; cbor::Value::ArrayValue cbor_user;
cbor_user.emplace_back(Value(user_id)); cbor_user.emplace_back(cbor::Value(user_id));
cbor_user.emplace_back(Value(user_name, Value::Type::BYTE_STRING)); cbor_user.emplace_back(
cbor_user.emplace_back(Value(user_display_name, Value::Type::BYTE_STRING)); cbor::Value(user_name, cbor::Value::Type::BYTE_STRING));
cbor_user.emplace_back(
cbor::Value(user_display_name, cbor::Value::Type::BYTE_STRING));
base::Optional<std::vector<uint8_t>> pt = base::Optional<std::vector<uint8_t>> pt =
Writer::Write(Value(std::move(cbor_user))); cbor::Writer::Write(cbor::Value(std::move(cbor_user)));
if (!pt) { DCHECK(pt);
return base::nullopt;
} std::vector<uint8_t> aad;
std::string aad = std::string(1, version) + rp_id; aad.push_back(version);
base::Optional<std::string> ciphertext = aad.insert(aad.end(), rp_id.data(), rp_id.data() + rp_id.size());
const std::vector<uint8_t> ct =
Cryptor(secret).Seal(Cryptor::Algorithm::kAes256Gcm, nonce, *pt, aad); Cryptor(secret).Seal(Cryptor::Algorithm::kAes256Gcm, nonce, *pt, aad);
if (!ciphertext) { result.insert(result.end(), ct.begin(), ct.end());
return base::nullopt;
}
base::span<const char> cts(reinterpret_cast<const char*>(ciphertext->data()),
ciphertext->size());
result.insert(result.end(), cts.begin(), cts.end());
return result; return result;
} }
......
...@@ -76,7 +76,7 @@ std::string GenerateCredentialMetadataSecret(); ...@@ -76,7 +76,7 @@ std::string GenerateCredentialMetadataSecret();
// //
// Credential IDs have following format: // Credential IDs have following format:
// //
// | version | nonce | AEAD(pt=CBOR(metadata), | // | version | nonce | AEAD(pt=CBOR(metadata), |
// | (1 byte) | (12 bytes) | nonce=nonce, | // | (1 byte) | (12 bytes) | nonce=nonce, |
// | | | ad=(version, rpID)) | // | | | ad=(version, rpID)) |
// //
...@@ -86,10 +86,9 @@ std::string GenerateCredentialMetadataSecret(); ...@@ -86,10 +86,9 @@ std::string GenerateCredentialMetadataSecret();
// The |user_name| and |user_display_name| fields may be truncated before // The |user_name| and |user_display_name| fields may be truncated before
// encryption. The truncated values are guaranteed to be valid UTF-8. // encryption. The truncated values are guaranteed to be valid UTF-8.
COMPONENT_EXPORT(DEVICE_FIDO) COMPONENT_EXPORT(DEVICE_FIDO)
base::Optional<std::vector<uint8_t>> SealCredentialId( std::vector<uint8_t> SealCredentialId(const std::string& secret,
const std::string& secret, const std::string& rp_id,
const std::string& rp_id, const CredentialMetadata& metadata);
const CredentialMetadata& metadata);
// UnsealCredentialId attempts to decrypt a CredentialMetadata from a credential // UnsealCredentialId attempts to decrypt a CredentialMetadata from a credential
// id. // id.
...@@ -105,15 +104,13 @@ base::Optional<CredentialMetadata> UnsealCredentialId( ...@@ -105,15 +104,13 @@ base::Optional<CredentialMetadata> UnsealCredentialId(
// This encoding allows lookup of credentials for a given RP and user but // This encoding allows lookup of credentials for a given RP and user but
// without the credential ID. // without the credential ID.
COMPONENT_EXPORT(DEVICE_FIDO) COMPONENT_EXPORT(DEVICE_FIDO)
base::Optional<std::string> EncodeRpIdAndUserId( std::string EncodeRpIdAndUserId(const std::string& secret,
const std::string& secret, const std::string& rp_id,
const std::string& rp_id, base::span<const uint8_t> user_id);
base::span<const uint8_t> user_id);
// EncodeRpId encodes the given RP ID for storage in the macOS keychain. // EncodeRpId encodes the given RP ID for storage in the macOS keychain.
COMPONENT_EXPORT(DEVICE_FIDO) COMPONENT_EXPORT(DEVICE_FIDO)
base::Optional<std::string> EncodeRpId(const std::string& secret, std::string EncodeRpId(const std::string& secret, const std::string& rp_id);
const std::string& rp_id);
// DecodeRpId attempts to decode a given RP ID from the keychain. // DecodeRpId attempts to decode a given RP ID from the keychain.
// //
...@@ -126,7 +123,7 @@ base::Optional<std::string> DecodeRpId(const std::string& secret, ...@@ -126,7 +123,7 @@ base::Optional<std::string> DecodeRpId(const std::string& secret,
// Seals a legacy V0 credential ID. // Seals a legacy V0 credential ID.
COMPONENT_EXPORT(DEVICE_FIDO) COMPONENT_EXPORT(DEVICE_FIDO)
base::Optional<std::vector<uint8_t>> SealLegacyV0CredentialIdForTestingOnly( std::vector<uint8_t> SealLegacyV0CredentialIdForTestingOnly(
const std::string& secret, const std::string& secret,
const std::string& rp_id, const std::string& rp_id,
const std::vector<uint8_t>& user_id, const std::vector<uint8_t>& user_id,
......
...@@ -35,7 +35,7 @@ class CredentialMetadataTest : public ::testing::Test { ...@@ -35,7 +35,7 @@ class CredentialMetadataTest : public ::testing::Test {
} }
std::vector<uint8_t> SealCredentialId(CredentialMetadata user) { std::vector<uint8_t> SealCredentialId(CredentialMetadata user) {
return *device::fido::mac::SealCredentialId(key_, rp_id_, std::move(user)); return device::fido::mac::SealCredentialId(key_, rp_id_, std::move(user));
} }
CredentialMetadata UnsealCredentialId( CredentialMetadata UnsealCredentialId(
...@@ -44,11 +44,11 @@ class CredentialMetadataTest : public ::testing::Test { ...@@ -44,11 +44,11 @@ class CredentialMetadataTest : public ::testing::Test {
} }
std::string EncodeRpIdAndUserId(base::StringPiece user_id) { std::string EncodeRpIdAndUserId(base::StringPiece user_id) {
return *device::fido::mac::EncodeRpIdAndUserId(key_, rp_id_, return device::fido::mac::EncodeRpIdAndUserId(key_, rp_id_,
to_bytes(user_id)); to_bytes(user_id));
} }
std::string EncodeRpId() { std::string EncodeRpId() {
return *device::fido::mac::EncodeRpId(key_, rp_id_); return device::fido::mac::EncodeRpId(key_, rp_id_);
} }
std::string DecodeRpId(const std::string& ct) { std::string DecodeRpId(const std::string& ct) {
...@@ -70,11 +70,11 @@ TEST_F(CredentialMetadataTest, CredentialId) { ...@@ -70,11 +70,11 @@ TEST_F(CredentialMetadataTest, CredentialId) {
TEST_F(CredentialMetadataTest, LegacyCredentialId) { TEST_F(CredentialMetadataTest, LegacyCredentialId) {
auto user = DefaultUser(); auto user = DefaultUser();
auto id = SealLegacyV0CredentialIdForTestingOnly( std::vector<uint8_t> id = SealLegacyV0CredentialIdForTestingOnly(
key_, rp_id_, user.user_id, user.user_name, user.user_display_name); key_, rp_id_, user.user_id, user.user_name, user.user_display_name);
EXPECT_EQ(0, (*id)[0]); EXPECT_EQ(0, id[0]);
EXPECT_EQ(54u, id->size()); EXPECT_EQ(54u, id.size());
EXPECT_TRUE(MetadataEq(UnsealCredentialId(*id), DefaultUser())); EXPECT_TRUE(MetadataEq(UnsealCredentialId(id), DefaultUser()));
} }
TEST_F(CredentialMetadataTest, CredentialId_IsRandom) { TEST_F(CredentialMetadataTest, CredentialId_IsRandom) {
...@@ -106,26 +106,26 @@ TEST_F(CredentialMetadataTest, EncodeRpIdAndUserId) { ...@@ -106,26 +106,26 @@ TEST_F(CredentialMetadataTest, EncodeRpIdAndUserId) {
EXPECT_EQ(EncodeRpIdAndUserId("user"), EncodeRpIdAndUserId("user")); EXPECT_EQ(EncodeRpIdAndUserId("user"), EncodeRpIdAndUserId("user"));
EXPECT_NE(EncodeRpIdAndUserId("userA"), EncodeRpIdAndUserId("userB")); EXPECT_NE(EncodeRpIdAndUserId("userA"), EncodeRpIdAndUserId("userB"));
EXPECT_NE(EncodeRpIdAndUserId("user"), EXPECT_NE(EncodeRpIdAndUserId("user"),
*device::fido::mac::EncodeRpIdAndUserId(key_, "notacme.com", device::fido::mac::EncodeRpIdAndUserId(key_, "notacme.com",
to_bytes("user"))); to_bytes("user")));
EXPECT_NE(EncodeRpIdAndUserId("user"), EXPECT_NE(EncodeRpIdAndUserId("user"),
*device::fido::mac::EncodeRpIdAndUserId(wrong_key_, rp_id_, device::fido::mac::EncodeRpIdAndUserId(wrong_key_, rp_id_,
to_bytes("user"))); to_bytes("user")));
} }
TEST_F(CredentialMetadataTest, EncodeRpId) { TEST_F(CredentialMetadataTest, EncodeRpId) {
EXPECT_EQ(48u, EncodeRpId().size()); EXPECT_EQ(48u, EncodeRpId().size());
EXPECT_EQ(EncodeRpId(), EncodeRpId()); EXPECT_EQ(EncodeRpId(), EncodeRpId());
EXPECT_NE(EncodeRpId(), *device::fido::mac::EncodeRpId(key_, "notacme.com")); EXPECT_NE(EncodeRpId(), device::fido::mac::EncodeRpId(key_, "notacme.com"));
EXPECT_NE(EncodeRpId(), *device::fido::mac::EncodeRpId(wrong_key_, rp_id_)); EXPECT_NE(EncodeRpId(), device::fido::mac::EncodeRpId(wrong_key_, rp_id_));
} }
TEST_F(CredentialMetadataTest, DecodeRpId) { TEST_F(CredentialMetadataTest, DecodeRpId) {
EXPECT_EQ(rp_id_, DecodeRpId(EncodeRpId())); EXPECT_EQ(rp_id_, DecodeRpId(EncodeRpId()));
EXPECT_NE(rp_id_, EXPECT_NE(rp_id_,
*device::fido::mac::DecodeRpId( *device::fido::mac::DecodeRpId(
key_, *device::fido::mac::EncodeRpId(key_, "notacme.com"))); key_, device::fido::mac::EncodeRpId(key_, "notacme.com")));
EXPECT_FALSE(device::fido::mac::DecodeRpId(wrong_key_, EncodeRpId())); EXPECT_FALSE(device::fido::mac::DecodeRpId(wrong_key_, EncodeRpId()));
} }
......
...@@ -46,12 +46,7 @@ const std::string& GetAssertionOperation::RpId() const { ...@@ -46,12 +46,7 @@ const std::string& GetAssertionOperation::RpId() const {
} }
void GetAssertionOperation::Run() { void GetAssertionOperation::Run() {
if (!Init()) { Init();
std::move(callback())
.Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
return;
}
// Display the macOS Touch ID prompt. // Display the macOS Touch ID prompt.
PromptTouchId(l10n_util::GetStringFUTF16(IDS_WEBAUTHN_TOUCH_ID_PROMPT_REASON, PromptTouchId(l10n_util::GetStringFUTF16(IDS_WEBAUTHN_TOUCH_ID_PROMPT_REASON,
base::UTF8ToUTF16(RpId()))); base::UTF8ToUTF16(RpId())));
......
...@@ -87,19 +87,14 @@ static std::list<Credential> FindCredentialsImpl( ...@@ -87,19 +87,14 @@ static std::list<Credential> FindCredentialsImpl(
const std::string& rp_id, const std::string& rp_id,
const std::set<std::vector<uint8_t>>& allowed_credential_ids, const std::set<std::vector<uint8_t>>& allowed_credential_ids,
LAContext* authentication_context) API_AVAILABLE(macosx(10.12.2)) { LAContext* authentication_context) API_AVAILABLE(macosx(10.12.2)) {
base::Optional<std::string> encoded_rp_id =
EncodeRpId(metadata_secret, rp_id);
if (!encoded_rp_id) {
return {};
}
base::ScopedCFTypeRef<CFMutableDictionaryRef> query( base::ScopedCFTypeRef<CFMutableDictionaryRef> query(
CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr)); CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr));
CFDictionarySetValue(query, kSecClass, kSecClassKey); CFDictionarySetValue(query, kSecClass, kSecClassKey);
CFDictionarySetValue(query, kSecAttrAccessGroup, CFDictionarySetValue(query, kSecAttrAccessGroup,
base::SysUTF8ToNSString(keychain_access_group)); base::SysUTF8ToNSString(keychain_access_group));
CFDictionarySetValue(query, kSecAttrLabel, CFDictionarySetValue(
base::SysUTF8ToNSString(*encoded_rp_id)); query, kSecAttrLabel,
base::SysUTF8ToNSString(EncodeRpId(metadata_secret, rp_id)));
if (authentication_context) { if (authentication_context) {
CFDictionarySetValue(query, kSecUseAuthenticationContext, CFDictionarySetValue(query, kSecUseAuthenticationContext,
authentication_context); authentication_context);
......
...@@ -59,10 +59,6 @@ class API_AVAILABLE(macosx(10.12.2)) ...@@ -59,10 +59,6 @@ class API_AVAILABLE(macosx(10.12.2))
// OperationBase: // OperationBase:
const std::string& RpId() const override; const std::string& RpId() const override;
void PromptTouchIdDone(bool success) override; void PromptTouchIdDone(bool success) override;
// Generates a credential ID by invoking SealCredentialId() with parameters
// for the request. Note that results are non-deterministic.
base::Optional<std::vector<uint8_t>> GenerateCredentialIdForRequest() const;
}; };
} // namespace mac } // namespace mac
......
...@@ -49,12 +49,7 @@ const std::string& MakeCredentialOperation::RpId() const { ...@@ -49,12 +49,7 @@ const std::string& MakeCredentialOperation::RpId() const {
} }
void MakeCredentialOperation::Run() { void MakeCredentialOperation::Run() {
if (!Init()) { Init();
std::move(callback())
.Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
return;
}
// Verify pubKeyCredParams contains ES-256, which is the only algorithm we // Verify pubKeyCredParams contains ES-256, which is the only algorithm we
// support. // support.
auto is_es256 = auto is_es256 =
...@@ -113,17 +108,12 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) { ...@@ -113,17 +108,12 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) {
// //
// Note that because the rk bit is not encoded here, a resident credential // Note that because the rk bit is not encoded here, a resident credential
// may overwrite a non-resident credential and vice versa. // may overwrite a non-resident credential and vice versa.
base::Optional<std::string> encoded_rp_id_user_id = const std::string encoded_rp_id_user_id =
EncodeRpIdAndUserId(metadata_secret(), RpId(), request().user.id); EncodeRpIdAndUserId(metadata_secret(), RpId(), request().user.id);
if (!encoded_rp_id_user_id) {
std::move(callback())
.Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
return;
}
{ {
ScopedCFTypeRef<CFMutableDictionaryRef> query = DefaultKeychainQuery(); ScopedCFTypeRef<CFMutableDictionaryRef> query = DefaultKeychainQuery();
CFDictionarySetValue(query, kSecAttrApplicationTag, CFDictionarySetValue(query, kSecAttrApplicationTag,
base::SysUTF8ToNSString(*encoded_rp_id_user_id)); base::SysUTF8ToNSString(encoded_rp_id_user_id));
OSStatus status = Keychain::GetInstance().ItemDelete(query); OSStatus status = Keychain::GetInstance().ItemDelete(query);
if (status != errSecSuccess && status != errSecItemNotFound) { if (status != errSecSuccess && status != errSecItemNotFound) {
OSSTATUS_DLOG(ERROR, status) << "SecItemDelete failed"; OSSTATUS_DLOG(ERROR, status) << "SecItemDelete failed";
...@@ -134,14 +124,10 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) { ...@@ -134,14 +124,10 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) {
} }
// Generate the new key pair. // Generate the new key pair.
base::Optional<std::vector<uint8_t>> credential_id = const std::vector<uint8_t> credential_id =
GenerateCredentialIdForRequest(); SealCredentialId(metadata_secret(), RpId(),
if (!credential_id) { CredentialMetadata::FromPublicKeyCredentialUserEntity(
FIDO_LOG(ERROR) << "GenerateCredentialIdForRequest failed"; request().user, request().resident_key_required));
std::move(callback())
.Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt);
return;
}
ScopedCFTypeRef<CFMutableDictionaryRef> params( ScopedCFTypeRef<CFMutableDictionaryRef> params(
CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr)); CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr));
...@@ -160,10 +146,10 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) { ...@@ -160,10 +146,10 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) {
CFDictionarySetValue(private_key_params, kSecUseAuthenticationContext, CFDictionarySetValue(private_key_params, kSecUseAuthenticationContext,
authentication_context()); authentication_context());
CFDictionarySetValue(private_key_params, kSecAttrApplicationTag, CFDictionarySetValue(private_key_params, kSecAttrApplicationTag,
base::SysUTF8ToNSString(*encoded_rp_id_user_id)); base::SysUTF8ToNSString(encoded_rp_id_user_id));
CFDictionarySetValue(private_key_params, kSecAttrApplicationLabel, CFDictionarySetValue(private_key_params, kSecAttrApplicationLabel,
[NSData dataWithBytes:credential_id->data() [NSData dataWithBytes:credential_id.data()
length:credential_id->size()]); length:credential_id.size()]);
ScopedCFTypeRef<CFErrorRef> cferr; ScopedCFTypeRef<CFErrorRef> cferr;
ScopedCFTypeRef<SecKeyRef> private_key( ScopedCFTypeRef<SecKeyRef> private_key(
...@@ -187,7 +173,7 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) { ...@@ -187,7 +173,7 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) {
// Create attestation object. There is no separate attestation key pair, so // Create attestation object. There is no separate attestation key pair, so
// we perform self-attestation. // we perform self-attestation.
base::Optional<AttestedCredentialData> attested_credential_data = base::Optional<AttestedCredentialData> attested_credential_data =
MakeAttestedCredentialData(*credential_id, MakeAttestedCredentialData(credential_id,
SecKeyRefToECPublicKey(public_key)); SecKeyRefToECPublicKey(public_key));
if (!attested_credential_data) { if (!attested_credential_data) {
FIDO_LOG(ERROR) << "MakeAttestedCredentialData failed"; FIDO_LOG(ERROR) << "MakeAttestedCredentialData failed";
...@@ -216,13 +202,6 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) { ...@@ -216,13 +202,6 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) {
.Run(CtapDeviceResponseCode::kSuccess, std::move(response)); .Run(CtapDeviceResponseCode::kSuccess, std::move(response));
} }
base::Optional<std::vector<uint8_t>>
MakeCredentialOperation::GenerateCredentialIdForRequest() const {
return SealCredentialId(metadata_secret(), RpId(),
CredentialMetadata::FromPublicKeyCredentialUserEntity(
request().user, request().resident_key_required));
}
} // namespace mac } // namespace mac
} // namespace fido } // namespace fido
} // namespace device } // namespace device
...@@ -43,15 +43,7 @@ class API_AVAILABLE(macosx(10.12.2)) OperationBase : public Operation { ...@@ -43,15 +43,7 @@ class API_AVAILABLE(macosx(10.12.2)) OperationBase : public Operation {
protected: protected:
// Subclasses must call Init() at the beginning of Run(). // Subclasses must call Init() at the beginning of Run().
bool Init() { void Init() { encoded_rp_id_ = EncodeRpId(metadata_secret(), RpId()); }
base::Optional<std::string> encoded_rp_id =
EncodeRpId(metadata_secret(), RpId());
if (!encoded_rp_id)
return false;
encoded_rp_id_ = std::move(*encoded_rp_id);
return true;
}
// PromptTouchId triggers a Touch ID consent dialog with the given reason // PromptTouchId triggers a Touch ID consent dialog with the given reason
// string. Subclasses implement the PromptTouchIdDone callback to receive the // string. Subclasses implement the PromptTouchIdDone callback to receive the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment