Add ed25519 signature utilities

PiperOrigin-RevId: 537351543
This commit is contained in:
Anay Wadhera
2023-06-02 10:45:40 -07:00
committed by Copybara-Service
parent 5740dcccbb
commit 71558c0a5c
5 changed files with 356 additions and 0 deletions
+1
View File
@@ -466,6 +466,7 @@ let package = Package(
"internal/crypto/aead_unittest.cc",
"internal/crypto/ec_private_key_unittest.cc",
"internal/crypto/ec_signature_creator_unittest.cc",
"internal/crypto/ed25519_unittest.cc",
"internal/crypto/encryptor_unittest.cc",
"internal/crypto/hmac_unittest.cc",
"internal/crypto/random_unittest.cc",
+6
View File
@@ -27,6 +27,7 @@ cc_library(
"ec_private_key.cc",
"ec_signature_creator.cc",
"ec_signature_creator_impl.cc",
"ed25519.cc",
"encryptor.cc",
"hkdf.cc",
"hmac.cc",
@@ -46,6 +47,7 @@ cc_library(
"ec_private_key.h",
"ec_signature_creator.h",
"ec_signature_creator_impl.h",
"ed25519.h",
"encryptor.h",
"hkdf.h",
"hmac.h",
@@ -68,7 +70,10 @@ cc_library(
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/types:optional",
"@com_google_absl//absl/types:span",
],
@@ -81,6 +86,7 @@ cc_test(
"aead_unittest.cc",
"ec_private_key_unittest.cc",
"ec_signature_creator_unittest.cc",
"ed25519_unittest.cc",
"encryptor_unittest.cc",
"hmac_unittest.cc",
"random_unittest.cc",
+128
View File
@@ -0,0 +1,128 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/crypto/ed25519.h"
#include <optional>
#include <string>
#include <utility>
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include <openssl/evp.h>
namespace crypto {
constexpr size_t kEd25519SignatureSize = 64;
constexpr size_t kEd25519PrivateKeySize = 32;
constexpr size_t kEd25519PublicKeySize = 32;
// Signer
absl::StatusOr<Ed25519Signer> Ed25519Signer::Create(std::string private_key) {
// OpenSSL/BoringSSL consider the ED25519's private key to be: private_key ||
// public_key.
const size_t kKeyLength = kEd25519PrivateKeySize + kEd25519PublicKeySize;
if (private_key.length() != kKeyLength) {
return absl::InvalidArgumentError(
absl::StrFormat("Only acceptable key length is %d", kKeyLength));
}
CryptoKeyUniquePtr priv_key(EVP_PKEY_new_raw_private_key(
EVP_PKEY_ED25519, /*unused=*/nullptr,
reinterpret_cast<const uint8_t *>(private_key.data()),
kEd25519PrivateKeySize));
if (priv_key == nullptr) {
return absl::InternalError("EVP_PKEY_new_raw_private_key failed");
}
return Ed25519Signer(std::move(priv_key));
}
Ed25519Signer::Ed25519Signer(CryptoKeyUniquePtr private_key) {
private_key_ = std::move(private_key);
}
std::optional<std::string> Ed25519Signer::Sign(absl::string_view data) {
if (data.empty() || data.data() == nullptr) {
data = "";
}
uint8_t signature[kEd25519SignatureSize] = {0};
CryptoMdCtxUniquePtr md_ctx(EVP_MD_CTX_create());
if (md_ctx == nullptr) {
return std::nullopt;
}
size_t signature_size = kEd25519SignatureSize;
if (EVP_DigestSignInit(md_ctx.get(), /*pctx=*/nullptr, /*type=*/nullptr,
/*e=*/nullptr, private_key_.get()) != 1 ||
EVP_DigestSign(md_ctx.get(), signature, &signature_size,
/*data=*/reinterpret_cast<const uint8_t *>(data.data()),
data.size()) != 1) {
return std::nullopt;
}
return std::string(reinterpret_cast<char *>(signature),
kEd25519SignatureSize);
}
// Verifier
absl::StatusOr<Ed25519Verifier> Ed25519Verifier::Create(
std::string public_key) {
if (public_key.length() != kEd25519PublicKeySize) {
return absl::InvalidArgumentError(absl::StrFormat(
"Only acceptable key length is %d", kEd25519PublicKeySize));
}
CryptoKeyUniquePtr pub_key(EVP_PKEY_new_raw_public_key(
EVP_PKEY_ED25519, /*unused=*/nullptr,
reinterpret_cast<const uint8_t *>(public_key.data()),
kEd25519PublicKeySize));
if (pub_key == nullptr) {
return absl::InternalError("EVP_PKEY_new_raw_public_key failed");
}
return Ed25519Verifier(std::move(pub_key));
}
Ed25519Verifier::Ed25519Verifier(CryptoKeyUniquePtr public_key) {
public_key_ = std::move(public_key);
}
absl::Status Ed25519Verifier::Verify(absl::string_view data,
absl::string_view signature) {
if (data.empty() || data.data() == nullptr) {
data = "";
}
if (signature.empty() || signature.data() == nullptr) {
signature = "";
}
if (signature.length() != kEd25519SignatureSize) {
return absl::InvalidArgumentError(absl::StrFormat(
"Only acceptable signature length is %d", kEd25519SignatureSize));
}
CryptoMdCtxUniquePtr md_ctx(EVP_MD_CTX_create());
if (md_ctx == nullptr) {
return absl::InternalError("EVP_MD_CTX_create failed");
}
// `type` must be set to nullptr with Ed25519.
if (EVP_DigestVerifyInit(md_ctx.get(), /*pctx=*/nullptr, /*type=*/nullptr,
/*e=*/nullptr, public_key_.get()) != 1) {
return absl::InternalError("EVP_DigestVerifyInit failed");
}
return EVP_DigestVerify(
md_ctx.get(),
/*sig=*/reinterpret_cast<const uint8_t *>(signature.data()),
signature.size(),
/*data=*/reinterpret_cast<const uint8_t *>(data.data()),
data.size()) != 0
? absl::OkStatus()
: absl::InternalError("Signature is invalid.");
}
} // namespace crypto
+68
View File
@@ -0,0 +1,68 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_NEARBY_INTERNAL_CRYPTO_ED25519_H_
#define THIRD_PARTY_NEARBY_INTERNAL_CRYPTO_ED25519_H_
#include <optional>
#include <string>
#include "absl/status/statusor.h"
#include "internal/crypto/crypto_export.h"
#include <openssl/base.h>
#include <openssl/digest.h>
#include <openssl/evp.h>
namespace crypto {
#ifdef OPENSSL_IS_BORINGSSL
using CryptoKeyUniquePtr = ::bssl::UniquePtr<EVP_PKEY>;
using CryptoMdCtxUniquePtr = ::bssl::UniquePtr<EVP_MD_CTX>;
#else
struct Ed25519KeyFree {
void operator()(EVP_PKEY* pkey) const { EVP_PKEY_free(pkey); }
};
struct Ed25519MdCtxFree {
void operator()(EVP_MD_CTX* ctx) const { EVP_MD_CTX_free(ctx); }
};
using CryptoKeyUniquePtr = std::unique_ptr<EVP_PKEY, Ed25519KeyFree>;
using CryptoMdCtxUniquePtr = std::unique_ptr<EVP_MD_CTX, Ed25519MdCtxFree>;
#endif
class CRYPTO_EXPORT Ed25519Signer {
public:
static absl::StatusOr<Ed25519Signer> Create(std::string private_key);
std::optional<std::string> Sign(absl::string_view data);
private:
explicit Ed25519Signer(CryptoKeyUniquePtr private_key);
CryptoKeyUniquePtr private_key_;
};
class CRYPTO_EXPORT Ed25519Verifier {
public:
static absl::StatusOr<Ed25519Verifier> Create(std::string public_key);
absl::Status Verify(absl::string_view data, absl::string_view signature);
private:
explicit Ed25519Verifier(CryptoKeyUniquePtr public_key);
CryptoKeyUniquePtr public_key_;
};
} // namespace crypto
#endif // THIRD_PARTY_NEARBY_INTERNAL_CRYPTO_ED25519_H_
+153
View File
@@ -0,0 +1,153 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/crypto/ed25519.h"
#include <string>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/strings/escaping.h"
namespace crypto {
namespace {
using ::testing::status::StatusIs;
// From
// https://github.com/google/boringssl/blob/master/crypto/curve25519/ed25519_tests.txt
TEST(Ed25519SignerTest, SignatureIsCorrect1) {
std::string priv = absl::HexStringToBytes(
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60d75a9801"
"82b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a");
ASSERT_EQ(priv.size(), 64);
auto signer = Ed25519Signer::Create(priv);
ASSERT_OK(signer);
auto signature = signer->Sign("");
ASSERT_TRUE(signature.has_value());
EXPECT_EQ(
*signature,
absl::HexStringToBytes(
"e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8"
"821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"));
}
TEST(Ed25519SignerTest, SignatureIsCorrect2) {
std::string priv = absl::HexStringToBytes(
"4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb3d4017c3"
"e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c");
ASSERT_EQ(priv.size(), 64);
auto signer = Ed25519Signer::Create(priv);
ASSERT_OK(signer);
auto signature = signer->Sign(absl::HexStringToBytes("72"));
ASSERT_TRUE(signature.has_value());
std::string expected_sig = absl::HexStringToBytes(
"92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085a"
"c1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00");
ASSERT_EQ(expected_sig.size(), 64);
EXPECT_EQ(*signature, expected_sig);
}
TEST(Ed25519SignerTest, SignatureIsCorrect3) {
std::string priv = absl::HexStringToBytes(
"c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7fc51cd8e"
"6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025");
ASSERT_EQ(priv.size(), 64);
auto signer = Ed25519Signer::Create(priv);
ASSERT_OK(signer);
auto signature = signer->Sign(absl::HexStringToBytes("af82"));
ASSERT_TRUE(signature.has_value());
EXPECT_EQ(
*signature,
absl::HexStringToBytes(
"6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff"
"9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a"));
}
TEST(Ed25519SignerTest, RejectsBadKeyLength) {
std::string priv = absl::HexStringToBytes(
"c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7fc51cd8e"
"6218a1a38da47ed00230f0580816ed13ba3303ac5deb91154890802570");
ASSERT_EQ(priv.size(), 65);
auto signer = Ed25519Signer::Create(priv);
EXPECT_THAT(signer,
testing::status::StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST(Ed25519VerifierTest, CanVerifyCorrect1) {
std::string pub = absl::HexStringToBytes(
"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a");
ASSERT_EQ(pub.size(), 32);
auto verifier = Ed25519Verifier::Create(pub);
ASSERT_OK(verifier);
EXPECT_OK(verifier->Verify(
"",
absl::HexStringToBytes(
"e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8"
"821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b")));
}
TEST(Ed25519VerifierTest, CanVerifyCorrect2) {
std::string pub = absl::HexStringToBytes(
"3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c");
ASSERT_EQ(pub.size(), 32);
auto verifier = Ed25519Verifier::Create(pub);
ASSERT_OK(verifier);
EXPECT_OK(verifier->Verify(
absl::HexStringToBytes("72"),
absl::HexStringToBytes(
"92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085a"
"c1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00")));
}
TEST(Ed25519VerifierTest, CanVerifyCorrect3) {
std::string pub = absl::HexStringToBytes(
"fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025");
ASSERT_EQ(pub.size(), 32);
auto verifier = Ed25519Verifier::Create(pub);
ASSERT_OK(verifier);
EXPECT_OK(verifier->Verify(
absl::HexStringToBytes("af82"),
absl::HexStringToBytes(
"6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff"
"9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a")));
}
TEST(Ed25519VerifierTest, DoesNotVerifyBadSignature) {
std::string pub = absl::HexStringToBytes(
"fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025");
ASSERT_EQ(pub.size(), 32);
auto verifier = Ed25519Verifier::Create(pub);
ASSERT_OK(verifier);
EXPECT_THAT(verifier->Verify(
absl::HexStringToBytes("af82"),
// Added 0x80 to signature.
absl::HexStringToBytes("6291d657deec24024827e69c3abe01a30ce54"
"8a284743a445e3680d7db5ac3ac18ff"
"9b538d16f290ae67f760984dc6594a7c15e97"
"16ed28dc027beceea1ec40a80")),
StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST(Ed25519VerifierTest, RejectsBadKeyLength) {
std::string pub = absl::HexStringToBytes(
"fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb91154890802570");
ASSERT_EQ(pub.size(), 33);
auto verifier = Ed25519Verifier::Create(pub);
EXPECT_THAT(verifier, StatusIs(absl::StatusCode::kInvalidArgument));
}
} // namespace
} // namespace crypto