blob: 48bf13addae0bfbab746fc690d9ae637834566da [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.
#include "device/fido/mac/credential_metadata.h"
#include "device/fido/public_key_credential_user_entity.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace device {
namespace fido {
namespace mac {
namespace {
bool UserEqual(const CredentialMetadata::UserEntity& lhs,
const CredentialMetadata::UserEntity& rhs) {
return lhs.id == rhs.id && lhs.name == rhs.name &&
lhs.display_name == rhs.display_name;
}
base::span<const uint8_t> to_bytes(base::StringPiece in) {
return base::make_span(reinterpret_cast<const uint8_t*>(in.data()),
in.size());
}
class CredentialMetadataTest : public ::testing::Test {
protected:
CredentialMetadata::UserEntity DefaultUser() {
return CredentialMetadata::UserEntity(default_id_, "user", "user@acme.com");
}
std::vector<uint8_t> SealCredentialId(CredentialMetadata::UserEntity user) {
return *CredentialMetadata::SealCredentialId(key_, rp_id_, std::move(user));
}
CredentialMetadata::UserEntity UnsealCredentialId(
base::span<const uint8_t> credential_id) {
return *CredentialMetadata::UnsealCredentialId(key_, rp_id_, credential_id);
}
std::string EncodeRpIdAndUserId(base::StringPiece user_id) {
return *CredentialMetadata::EncodeRpIdAndUserId(key_, rp_id_,
to_bytes(user_id));
}
std::string EncodeRpId() {
return *CredentialMetadata::EncodeRpId(key_, rp_id_);
}
std::string DecodeRpId(const std::string& ct) {
return *CredentialMetadata::DecodeRpId(key_, ct);
}
std::vector<uint8_t> default_id_ = {0, 1, 2, 3};
std::string rp_id_ = "acme.com";
std::string key_ = "supersecretsupersecretsupersecre";
std::string wrong_key_ = "SUPERsecretsupersecretsupersecre";
};
TEST_F(CredentialMetadataTest, CredentialId) {
std::vector<uint8_t> id = SealCredentialId(DefaultUser());
EXPECT_EQ(0, (id)[0]);
EXPECT_EQ(54u, id.size());
EXPECT_TRUE(UserEqual(UnsealCredentialId(id), DefaultUser()));
}
TEST_F(CredentialMetadataTest, CredentialId_IsRandom) {
EXPECT_NE(SealCredentialId(DefaultUser()), SealCredentialId(DefaultUser()));
}
TEST_F(CredentialMetadataTest, CredentialId_FailDecode) {
const auto id = SealCredentialId(DefaultUser());
// Flipping a bit in version, nonce, or ciphertext will fail credential
// decryption.
for (size_t i = 0; i < id.size(); i++) {
std::vector<uint8_t> new_id(id);
new_id[i] = new_id[i] ^ 0x01;
EXPECT_FALSE(CredentialMetadata::UnsealCredentialId(key_, rp_id_, new_id));
}
}
TEST_F(CredentialMetadataTest, CredentialId_InvalidRp) {
std::vector<uint8_t> id = SealCredentialId(DefaultUser());
// The credential id is authenticated with the RP, thus decryption under a
// different RP fails.
EXPECT_FALSE(CredentialMetadata::UnsealCredentialId(key_, "notacme.com", id));
}
TEST_F(CredentialMetadataTest, EncodeRpIdAndUserId) {
EXPECT_EQ(64u, EncodeRpIdAndUserId("user@acme.com").size())
<< EncodeRpIdAndUserId("user@acme.com");
EXPECT_EQ(EncodeRpIdAndUserId("user"), EncodeRpIdAndUserId("user"));
EXPECT_NE(EncodeRpIdAndUserId("userA"), EncodeRpIdAndUserId("userB"));
EXPECT_NE(EncodeRpIdAndUserId("user"),
*CredentialMetadata::EncodeRpIdAndUserId(key_, "notacme.com",
to_bytes("user")));
EXPECT_NE(EncodeRpIdAndUserId("user"),
*CredentialMetadata::EncodeRpIdAndUserId(wrong_key_, rp_id_,
to_bytes("user")));
}
TEST_F(CredentialMetadataTest, EncodeRpId) {
EXPECT_EQ(48u, EncodeRpId().size());
EXPECT_EQ(EncodeRpId(), EncodeRpId());
EXPECT_NE(EncodeRpId(), *CredentialMetadata::EncodeRpId(key_, "notacme.com"));
EXPECT_NE(EncodeRpId(), *CredentialMetadata::EncodeRpId(wrong_key_, rp_id_));
}
TEST_F(CredentialMetadataTest, DecodeRpId) {
EXPECT_EQ(rp_id_, DecodeRpId(EncodeRpId()));
EXPECT_NE(rp_id_,
*CredentialMetadata::DecodeRpId(
key_, *CredentialMetadata::EncodeRpId(key_, "notacme.com")));
EXPECT_FALSE(CredentialMetadata::DecodeRpId(wrong_key_, EncodeRpId()));
}
TEST(CredentialMetadata, GenerateRandomSecret) {
std::string s1 = CredentialMetadata::GenerateRandomSecret();
EXPECT_EQ(32u, s1.size());
std::string s2 = CredentialMetadata::GenerateRandomSecret();
EXPECT_EQ(32u, s2.size());
EXPECT_NE(s1, s2);
}
TEST(CredentialMetadata, FromPublicKeyCredentialUserEntity) {
std::vector<uint8_t> user_id = {{1, 2, 3}};
PublicKeyCredentialUserEntity in(user_id);
in.SetUserName("username");
in.SetDisplayName("display name");
in.SetIconUrl(GURL("http://rp.foo/user.png"));
CredentialMetadata::UserEntity out =
CredentialMetadata::UserEntity::FromPublicKeyCredentialUserEntity(
std::move(in));
EXPECT_EQ(user_id, out.id);
EXPECT_EQ("username", out.name);
EXPECT_EQ("display name", out.display_name);
}
TEST(CredentialMetadata, ToPublicKeyCredentialUserEntity) {
std::vector<uint8_t> user_id = {{1, 2, 3}};
CredentialMetadata::UserEntity in(user_id, "username", "display name");
PublicKeyCredentialUserEntity out = in.ToPublicKeyCredentialUserEntity();
EXPECT_EQ(user_id, out.user_id());
EXPECT_EQ("username", out.user_name().value());
EXPECT_EQ("display name", out.user_display_name().value());
EXPECT_FALSE(out.user_icon_url().has_value());
}
} // namespace
} // namespace mac
} // namespace fido
} // namespace device