mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
Add ed25519 signature utilities
PiperOrigin-RevId: 537351543
This commit is contained in:
committed by
Copybara-Service
parent
5740dcccbb
commit
71558c0a5c
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user