Skip to content

Commit

Permalink
Adding C++ support for legacy signature/mac format.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 209385880
GitOrigin-RevId: 9b282acb31ced6c3f5e15371417457411266d64c
  • Loading branch information
przydatek authored and Tink Team committed Aug 30, 2018
1 parent 6d78a4d commit a6b016c
Show file tree
Hide file tree
Showing 15 changed files with 210 additions and 39 deletions.
7 changes: 7 additions & 0 deletions cc/core/primitive_set_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,11 @@ TEST_F(PrimitiveSetTest, testBasic) {
EXPECT_EQ(data + mac_name_4,
primitives[0]->get_primitive().ComputeMac(data).ValueOrDie());
EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status());
EXPECT_EQ(OutputPrefixType::RAW, primitives[0]->get_output_prefix_type());
EXPECT_EQ(data + mac_name_5,
primitives[1]->get_primitive().ComputeMac(data).ValueOrDie());
EXPECT_EQ(KeyStatusType::DISABLED, primitives[1]->get_status());
EXPECT_EQ(OutputPrefixType::RAW, primitives[1]->get_output_prefix_type());
}

{ // Check Tink primitives.
Expand All @@ -216,9 +218,11 @@ TEST_F(PrimitiveSetTest, testBasic) {
EXPECT_EQ(data + mac_name_1,
primitives[0]->get_primitive().ComputeMac(data).ValueOrDie());
EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status());
EXPECT_EQ(OutputPrefixType::TINK, primitives[0]->get_output_prefix_type());
EXPECT_EQ(data + mac_name_6,
primitives[1]->get_primitive().ComputeMac(data).ValueOrDie());
EXPECT_EQ(KeyStatusType::DISABLED, primitives[1]->get_status());
EXPECT_EQ(OutputPrefixType::TINK, primitives[1]->get_output_prefix_type());
}

{ // Check another Tink primitive.
Expand All @@ -228,6 +232,7 @@ TEST_F(PrimitiveSetTest, testBasic) {
EXPECT_EQ(data + mac_name_3,
primitives[0]->get_primitive().ComputeMac(data).ValueOrDie());
EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status());
EXPECT_EQ(OutputPrefixType::TINK, primitives[0]->get_output_prefix_type());
}

{ // Check legacy primitive.
Expand All @@ -237,6 +242,8 @@ TEST_F(PrimitiveSetTest, testBasic) {
EXPECT_EQ(data + mac_name_2,
primitives[0]->get_primitive().ComputeMac(data).ValueOrDie());
EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status());
EXPECT_EQ(OutputPrefixType::LEGACY,
primitives[0]->get_output_prefix_type());
}
}

Expand Down
1 change: 1 addition & 0 deletions cc/mac/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ cc_test(
copts = ["-Iexternal/gtest/include"],
deps = [
":mac_set_wrapper",
"//cc:crypto_format",
"//cc:mac",
"//cc:primitive_set",
"//cc/util:status",
Expand Down
25 changes: 22 additions & 3 deletions cc/mac/mac_set_wrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@
#include "tink/subtle/subtle_util_boringssl.h"
#include "tink/util/status.h"
#include "tink/util/statusor.h"
#include "proto/tink.pb.h"

namespace crypto {
namespace tink {

using google::crypto::tink::OutputPrefixType;

namespace {

util::Status Validate(PrimitiveSet<Mac>* mac_set) {
Expand Down Expand Up @@ -56,24 +59,40 @@ util::StatusOr<std::string> MacSetWrapper::ComputeMac(
// regardless of whether the size is 0.
data = subtle::SubtleUtilBoringSSL::EnsureNonNull(data);

auto compute_mac_result =
mac_set_->get_primary()->get_primitive().ComputeMac(data);
auto primary = mac_set_->get_primary();
std::string local_data;
if (primary->get_output_prefix_type() == OutputPrefixType::LEGACY) {
local_data = std::string(data);
local_data.append(
reinterpret_cast<const char*>(&CryptoFormat::kLegacyStartByte), 1);
data = local_data;
}
auto compute_mac_result = primary->get_primitive().ComputeMac(data);
if (!compute_mac_result.ok()) return compute_mac_result.status();
const std::string& key_id = mac_set_->get_primary()->get_identifier();
const std::string& key_id = primary->get_identifier();
return key_id + compute_mac_result.ValueOrDie();
}

util::Status MacSetWrapper::VerifyMac(
absl::string_view mac_value,
absl::string_view data) const {
data = subtle::SubtleUtilBoringSSL::EnsureNonNull(data);
mac_value = subtle::SubtleUtilBoringSSL::EnsureNonNull(mac_value);

if (mac_value.length() > CryptoFormat::kNonRawPrefixSize) {
const std::string& key_id = std::string(mac_value.substr(0,
CryptoFormat::kNonRawPrefixSize));
auto primitives_result = mac_set_->get_primitives(key_id);
if (primitives_result.ok()) {
absl::string_view raw_mac_value =
mac_value.substr(CryptoFormat::kNonRawPrefixSize);
std::string local_data;
for (auto& mac_entry : *(primitives_result.ValueOrDie())) {
if (mac_entry->get_output_prefix_type() == OutputPrefixType::LEGACY) {
local_data = std::string(data);
local_data.append(1, CryptoFormat::kLegacyStartByte);
data = local_data;
}
Mac& mac = mac_entry->get_primitive();
util::Status status = mac.VerifyMac(raw_mac_value, data);
if (status.ok()) {
Expand Down
43 changes: 43 additions & 0 deletions cc/mac/mac_set_wrapper_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
////////////////////////////////////////////////////////////////////////////////

#include "tink/mac/mac_set_wrapper.h"
#include "tink/crypto_format.h"
#include "tink/mac.h"
#include "tink/primitive_set.h"
#include "tink/util/status.h"
Expand Down Expand Up @@ -113,6 +114,48 @@ TEST_F(MacSetWrapperTest, testBasic) {
}
}

TEST_F(MacSetWrapperTest, testLegacyAuthentication) {
// Prepare a set for the wrapper.
Keyset::Key key;
uint32_t key_id = 1234543;
key.set_output_prefix_type(OutputPrefixType::LEGACY);
key.set_key_id(key_id);
std::string mac_name = "SomeLegacyMac";

std::unique_ptr<PrimitiveSet<Mac>> mac_set(new PrimitiveSet<Mac>());
std::unique_ptr<Mac> mac(new DummyMac(mac_name));
auto entry_result = mac_set->AddPrimitive(std::move(mac), key);
ASSERT_TRUE(entry_result.ok());
mac_set->set_primary(entry_result.ValueOrDie());

// Wrap mac_set and test the resulting Mac.
auto mac_result = MacSetWrapper::NewMac(std::move(mac_set));
EXPECT_TRUE(mac_result.ok()) << mac_result.status();
mac = std::move(mac_result.ValueOrDie());
std::string data = "Some data to authenticate";

// Compute and verify MAC via wrapper.
auto compute_mac_result = mac->ComputeMac(data);
EXPECT_TRUE(compute_mac_result.ok()) << compute_mac_result.status();
std::string mac_value = compute_mac_result.ValueOrDie();
EXPECT_PRED_FORMAT2(testing::IsSubstring, mac_name, mac_value);
auto status = mac->VerifyMac(mac_value, data);
EXPECT_TRUE(status.ok()) << status;

// Try verifying on raw Mac-primitive using original data.
std::unique_ptr<Mac> raw_mac(new DummyMac(mac_name)); // same as in wrapper
std::string raw_mac_value = mac_value.substr(CryptoFormat::kNonRawPrefixSize);
status = raw_mac->VerifyMac(raw_mac_value, data);
EXPECT_FALSE(status.ok());
EXPECT_EQ(util::error::INVALID_ARGUMENT, status.error_code());

// Verify on raw Mac-primitive using legacy-formatted data.
std::string legacy_data = data;
legacy_data.append(1, CryptoFormat::kLegacyStartByte);
status = raw_mac->VerifyMac(raw_mac_value, legacy_data);
EXPECT_TRUE(status.ok()) << status;
}

} // namespace
} // namespace tink
} // namespace crypto
Expand Down
15 changes: 12 additions & 3 deletions cc/primitive_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,12 @@ class PrimitiveSet {
class Entry {
public:
Entry(std::unique_ptr<P2> primitive, const std::string& identifier,
google::crypto::tink::KeyStatusType status)
google::crypto::tink::KeyStatusType status,
google::crypto::tink::OutputPrefixType output_prefix_type)
: primitive_(std::move(primitive)),
identifier_(identifier),
status_(status) {}
status_(status),
output_prefix_type_(output_prefix_type) {}

P2& get_primitive() const { return *primitive_; }

Expand All @@ -69,10 +71,16 @@ class PrimitiveSet {
return status_;
}

const google::crypto::tink::OutputPrefixType get_output_prefix_type()
const {
return output_prefix_type_;
}

private:
std::unique_ptr<P> primitive_;
std::string identifier_;
google::crypto::tink::KeyStatusType status_;
google::crypto::tink::OutputPrefixType output_prefix_type_;
};

typedef std::vector<std::unique_ptr<Entry<P>>> Primitives;
Expand All @@ -93,7 +101,8 @@ class PrimitiveSet {
std::lock_guard<std::mutex> lock(primitives_mutex_);
primitives_[identifier].push_back(
absl::make_unique<Entry<P>>(std::move(primitive),
identifier, key.status()));
identifier, key.status(),
key.output_prefix_type()));
return primitives_[identifier].back().get();
}

Expand Down
1 change: 1 addition & 0 deletions cc/signature/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ cc_test(
copts = ["-Iexternal/gtest/include"],
deps = [
":public_key_sign_set_wrapper",
"//cc:crypto_format",
"//cc:primitive_set",
"//cc:public_key_sign",
"//cc:public_key_verify",
Expand Down
10 changes: 10 additions & 0 deletions cc/signature/public_key_sign_set_wrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@
#include "tink/public_key_sign.h"
#include "tink/subtle/subtle_util_boringssl.h"
#include "tink/util/statusor.h"
#include "proto/tink.pb.h"

namespace crypto {
namespace tink {

using google::crypto::tink::OutputPrefixType;

namespace {

util::Status Validate(PrimitiveSet<PublicKeySign>* public_key_sign_set) {
Expand Down Expand Up @@ -58,6 +62,12 @@ util::StatusOr<std::string> PublicKeySignSetWrapper::Sign(
data = subtle::SubtleUtilBoringSSL::EnsureNonNull(data);

auto primary = public_key_sign_set_->get_primary();
std::string local_data;
if (primary->get_output_prefix_type() == OutputPrefixType::LEGACY) {
local_data = std::string(data);
local_data.append(1, CryptoFormat::kLegacyStartByte);
data = local_data;
}
auto sign_result = primary->get_primitive().Sign(data);
if (!sign_result.ok()) return sign_result.status();
const std::string& key_id = primary->get_identifier();
Expand Down
49 changes: 47 additions & 2 deletions cc/signature/public_key_sign_set_wrapper_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
////////////////////////////////////////////////////////////////////////////////

#include "tink/signature/public_key_sign_set_wrapper.h"
#include "tink/crypto_format.h"
#include "tink/public_key_sign.h"
#include "tink/primitive_set.h"
#include "tink/util/status.h"
Expand Down Expand Up @@ -66,7 +67,7 @@ TEST_F(PublicKeySignSetWrapperTest, testBasic) {

uint32_t key_id_0 = 1234543;
key = keyset.add_key();
key->set_output_prefix_type(OutputPrefixType::RAW);
key->set_output_prefix_type(OutputPrefixType::TINK);
key->set_key_id(key_id_0);

uint32_t key_id_1 = 726329;
Expand All @@ -76,7 +77,7 @@ TEST_F(PublicKeySignSetWrapperTest, testBasic) {

uint32_t key_id_2 = 7213743;
key = keyset.add_key();
key->set_output_prefix_type(OutputPrefixType::TINK);
key->set_output_prefix_type(OutputPrefixType::RAW);
key->set_key_id(key_id_2);

std::string signature_name_0 = "signature_0";
Expand Down Expand Up @@ -120,6 +121,50 @@ TEST_F(PublicKeySignSetWrapperTest, testBasic) {
}
}

TEST_F(PublicKeySignSetWrapperTest, testLegacySignatures) {
// Prepare a set for the wrapper.
Keyset::Key key;
uint32_t key_id = 1234543;
key.set_output_prefix_type(OutputPrefixType::LEGACY);
key.set_key_id(key_id);
std::string signature_name = "SomeLegacySignatures";

std::unique_ptr<PrimitiveSet<PublicKeySign>> pk_sign_set(
new PrimitiveSet<PublicKeySign>());
std::string data = "Some data to sign";
std::unique_ptr<PublicKeySign> pk_sign(
new DummyPublicKeySign(signature_name));
auto entry_result = pk_sign_set->AddPrimitive(std::move(pk_sign), key);
ASSERT_TRUE(entry_result.ok());
pk_sign_set->set_primary(entry_result.ValueOrDie());

// Wrap pk_sign_set and test the resulting PublicKeySign.
auto pk_sign_result = PublicKeySignSetWrapper::NewPublicKeySign(
std::move(pk_sign_set));
EXPECT_TRUE(pk_sign_result.ok()) << pk_sign_result.status();
pk_sign = std::move(pk_sign_result.ValueOrDie());

// Compute the signature via wrapper.
auto sign_result = pk_sign->Sign(data);
EXPECT_TRUE(sign_result.ok()) << sign_result.status();
std::string signature = sign_result.ValueOrDie();
EXPECT_PRED_FORMAT2(testing::IsSubstring, signature_name, signature);

// Try verifying on raw PublicKeyVerify-primitive using original data.
std::unique_ptr<PublicKeyVerify> raw_pk_verify(
new DummyPublicKeyVerify(signature_name));
std::string raw_signature = signature.substr(CryptoFormat::kNonRawPrefixSize);
auto status = raw_pk_verify->Verify(raw_signature, data);
EXPECT_FALSE(status.ok());
EXPECT_EQ(util::error::INVALID_ARGUMENT, status.error_code());

// Verify on raw PublicKeyVerify-primitive using legacy-formatted data.
std::string legacy_data = data;
legacy_data.append(1, CryptoFormat::kLegacyStartByte);
status = raw_pk_verify->Verify(raw_signature, legacy_data);
EXPECT_TRUE(status.ok()) << status;
}

} // namespace
} // namespace tink
} // namespace crypto
Expand Down
15 changes: 13 additions & 2 deletions cc/signature/public_key_verify_set_wrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@
#include "tink/subtle/subtle_util_boringssl.h"
#include "tink/util/status.h"
#include "tink/util/statusor.h"
#include "proto/tink.pb.h"

namespace crypto {
namespace tink {

using google::crypto::tink::OutputPrefixType;

namespace {

util::Status Validate(PrimitiveSet<PublicKeyVerify>* public_key_verify_set) {
Expand Down Expand Up @@ -58,6 +62,7 @@ util::Status PublicKeyVerifySetWrapper::Verify(
// BoringSSL expects a non-null pointer for data,
// regardless of whether the size is 0.
data = subtle::SubtleUtilBoringSSL::EnsureNonNull(data);
signature = subtle::SubtleUtilBoringSSL::EnsureNonNull(signature);

if (signature.length() <= CryptoFormat::kNonRawPrefixSize) {
// This also rejects raw signatures with size of 4 bytes or fewer.
Expand All @@ -70,8 +75,14 @@ util::Status PublicKeyVerifySetWrapper::Verify(
if (primitives_result.ok()) {
absl::string_view raw_signature =
signature.substr(CryptoFormat::kNonRawPrefixSize);
for (auto& public_key_verify_entry : *(primitives_result.ValueOrDie())) {
auto& public_key_verify = public_key_verify_entry->get_primitive();
std::string local_data;
for (auto& entry : *(primitives_result.ValueOrDie())) {
if (entry->get_output_prefix_type() == OutputPrefixType::LEGACY) {
local_data = std::string(data);
local_data.append(1, CryptoFormat::kLegacyStartByte);
data = local_data;
}
auto& public_key_verify = entry->get_primitive();
auto verify_result =
public_key_verify.Verify(raw_signature, data);
if (verify_result.ok()) {
Expand Down
7 changes: 4 additions & 3 deletions cc/util/test_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include <string>

#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
#include "tink/aead.h"
#include "tink/hybrid_decrypt.h"
Expand Down Expand Up @@ -226,9 +227,9 @@ class DummyPublicKeyVerify : public PublicKeyVerify {
// of this DummyPublicKeyVerify with the provided 'data'.
crypto::tink::util::Status Verify(
absl::string_view signature, absl::string_view data) const override {
size_t pos = signature.rfind(signature_name_);
if (pos != std::string::npos &&
signature.length() == (unsigned)(signature_name_.length() + pos)) {
if (signature.length() == data.length() + signature_name_.length() &&
absl::StartsWith(signature, data) &&
absl::EndsWith(signature, signature_name_)) {
return crypto::tink::util::Status::OK;
}
return crypto::tink::util::Status(
Expand Down
2 changes: 1 addition & 1 deletion tools/testing/cross_language/aead_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ aead_basic_test() {
for key_template in ${key_templates[*]}
do
local test_instance="${test_name}_${key_template}"
generate_symmetric_key "aead" $test_instance $key_template
generate_symmetric_key $test_instance $key_template
generate_plaintext $test_instance

local encrypted_file="$TEST_TMPDIR/${test_instance}_encrypted.bin"
Expand Down
2 changes: 1 addition & 1 deletion tools/testing/cross_language/hybrid_encryption_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ hybrid_basic_test() {
for key_template in ${key_templates[*]}
do
local test_instance="${test_name}_${key_template}"
generate_asymmetric_keys "hybrid" $test_instance $key_template
generate_asymmetric_keys $test_instance $key_template
generate_plaintext $test_instance

local encrypted_file="$TEST_TMPDIR/${test_instance}_encrypted.bin"
Expand Down
Loading

0 comments on commit a6b016c

Please sign in to comment.