Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Nigori] Add classes representing Public-key & Public-private key-pair.
Public-private cryptography will be leveraged for encrypting passwords outside the account boundary in the context of Password Sharing. The primitives that are used are based on the X25519 curve. The intended encryption protocol will be HPKE. Bug: 1445056 Change-Id: I596640f95ab0b816e8a9a0171cfa5a11d0bee01f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4547322 Commit-Queue: Elias Khsheibun <eliaskh@chromium.org> Auto-Submit: Elias Khsheibun <eliaskh@chromium.org> Reviewed-by: David Benjamin <davidben@chromium.org> Reviewed-by: Maksim Moskvitin <mmoskvitin@google.com> Cr-Commit-Position: refs/heads/main@{#1149740}
- Loading branch information
Elias Khsheibun
authored and
Chromium LUCI CQ
committed
May 26, 2023
1 parent
1c7dbda
commit efddb51
Showing
9 changed files
with
367 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
include_rules = [ | ||
"+third_party/boringssl/src/include/openssl", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Copyright 2023 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "components/sync/engine/nigori/public_key.h" | ||
|
||
#include <algorithm> | ||
#include <array> | ||
#include <utility> | ||
|
||
#include "base/check.h" | ||
#include "base/check_op.h" | ||
#include "base/memory/ptr_util.h" | ||
|
||
namespace syncer { | ||
|
||
PublicKey::PublicKey(PublicKey&& other) = default; | ||
PublicKey& PublicKey::operator=(PublicKey&& other) = default; | ||
PublicKey::~PublicKey() = default; | ||
|
||
PublicKey::PublicKey(base::span<const uint8_t> public_key) { | ||
CHECK_EQ(static_cast<size_t>(X25519_PUBLIC_VALUE_LEN), public_key.size()); | ||
|
||
std::copy(public_key.begin(), public_key.end(), public_key_); | ||
} | ||
|
||
// static | ||
absl::optional<PublicKey> PublicKey::CreateByImport( | ||
base::span<const uint8_t> public_key) { | ||
if (public_key.size() != X25519_PUBLIC_VALUE_LEN) { | ||
return {}; | ||
} | ||
return PublicKey(public_key); | ||
} | ||
|
||
std::array<uint8_t, X25519_PUBLIC_VALUE_LEN> PublicKey::GetRawPublicKey() | ||
const { | ||
std::array<uint8_t, X25519_PUBLIC_VALUE_LEN> raw_public_key; | ||
std::copy(public_key_, public_key_ + X25519_PUBLIC_VALUE_LEN, | ||
raw_public_key.begin()); | ||
return raw_public_key; | ||
} | ||
|
||
PublicKey PublicKey::Clone() const { | ||
return PublicKey(GetRawPublicKey()); | ||
} | ||
|
||
} // namespace syncer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// Copyright 2023 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#ifndef COMPONENTS_SYNC_ENGINE_NIGORI_PUBLIC_KEY_H_ | ||
#define COMPONENTS_SYNC_ENGINE_NIGORI_PUBLIC_KEY_H_ | ||
|
||
#include <array> | ||
|
||
#include "base/containers/span.h" | ||
#include "third_party/abseil-cpp/absl/types/optional.h" | ||
#include "third_party/boringssl/src/include/openssl/curve25519.h" | ||
|
||
namespace syncer { | ||
|
||
// A wrapper around a 32-byte X25519 public key. | ||
class PublicKey { | ||
public: | ||
PublicKey(const PublicKey& other) = delete; | ||
PublicKey(PublicKey&& other); | ||
PublicKey& operator=(PublicKey&& other); | ||
PublicKey& operator=(const PublicKey& other) = delete; | ||
~PublicKey(); | ||
|
||
// Initialize the key using |public_key|. | ||
static absl::optional<PublicKey> CreateByImport( | ||
base::span<const uint8_t> public_key); | ||
|
||
// Returns the raw public key. | ||
std::array<uint8_t, X25519_PUBLIC_VALUE_LEN> GetRawPublicKey() const; | ||
|
||
// Creates an exact clone. | ||
PublicKey Clone() const; | ||
|
||
private: | ||
explicit PublicKey(base::span<const uint8_t> public_key); | ||
|
||
uint8_t public_key_[X25519_PUBLIC_VALUE_LEN]; | ||
}; | ||
|
||
} // namespace syncer | ||
|
||
#endif // COMPONENTS_SYNC_ENGINE_NIGORI_PUBLIC_KEY_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// Copyright 2023 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "components/sync/engine/nigori/public_key.h" | ||
|
||
#include <vector> | ||
|
||
#include "testing/gmock/include/gmock/gmock.h" | ||
#include "testing/gtest/include/gtest/gtest.h" | ||
|
||
namespace syncer { | ||
namespace { | ||
|
||
TEST(PublicKeyTest, GetRawPublicKeyShouldAlwaysSucceed) { | ||
const std::vector<uint8_t> key(X25519_PUBLIC_VALUE_LEN, 0xDE); | ||
const absl::optional<PublicKey> public_key = PublicKey::CreateByImport(key); | ||
|
||
ASSERT_TRUE(public_key.has_value()); | ||
|
||
const std::array<uint8_t, X25519_PUBLIC_VALUE_LEN> raw_key = | ||
public_key->GetRawPublicKey(); | ||
EXPECT_THAT(key, testing::ElementsAreArray(raw_key)); | ||
} | ||
|
||
TEST(PublicKeyTest, CreateByImportShouldFailOnLongerKeys) { | ||
const std::vector<uint8_t> key(X25519_PUBLIC_VALUE_LEN * 2); | ||
const absl::optional<PublicKey> public_key = PublicKey::CreateByImport(key); | ||
|
||
EXPECT_FALSE(public_key.has_value()); | ||
} | ||
|
||
TEST(PublicKeyTest, CreateByImportShouldFailOnShorterKeys) { | ||
const std::vector<uint8_t> key(X25519_PUBLIC_VALUE_LEN - 1); | ||
const absl::optional<PublicKey> public_key = PublicKey::CreateByImport(key); | ||
|
||
EXPECT_FALSE(public_key.has_value()); | ||
} | ||
|
||
TEST(PublicKeyTest, CloneShouldAlwaysSucceed) { | ||
const std::vector<uint8_t> key(X25519_PUBLIC_VALUE_LEN, 0xDE); | ||
const absl::optional<PublicKey> public_key = PublicKey::CreateByImport(key); | ||
|
||
absl::optional<PublicKey> clone = public_key->Clone(); | ||
|
||
ASSERT_TRUE(clone.has_value()); | ||
|
||
const std::array<uint8_t, X25519_PUBLIC_VALUE_LEN> raw_key = | ||
public_key->GetRawPublicKey(); | ||
const std::array<uint8_t, X25519_PUBLIC_VALUE_LEN> clone_raw_key = | ||
clone->GetRawPublicKey(); | ||
|
||
EXPECT_THAT(key, testing::ElementsAreArray(raw_key)); | ||
EXPECT_THAT(key, testing::ElementsAreArray(clone_raw_key)); | ||
} | ||
|
||
} // namespace | ||
} // namespace syncer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// Copyright 2023 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "components/sync/engine/nigori/public_private_key_pair.h" | ||
|
||
#include <algorithm> | ||
#include <array> | ||
#include <cstdint> | ||
#include <iterator> | ||
#include <string> | ||
#include <utility> | ||
|
||
#include "base/base64.h" | ||
#include "base/check_op.h" | ||
#include "base/logging.h" | ||
#include "base/memory/ptr_util.h" | ||
#include "third_party/abseil-cpp/absl/types/optional.h" | ||
|
||
namespace syncer { | ||
|
||
PublicPrivateKeyPair::PublicPrivateKeyPair(PublicPrivateKeyPair&& other) = | ||
default; | ||
|
||
PublicPrivateKeyPair& PublicPrivateKeyPair::operator=( | ||
PublicPrivateKeyPair&& other) = default; | ||
|
||
PublicPrivateKeyPair::~PublicPrivateKeyPair() = default; | ||
|
||
// static | ||
PublicPrivateKeyPair PublicPrivateKeyPair::GenerateNewKeyPair() { | ||
return PublicPrivateKeyPair(); | ||
} | ||
|
||
// static | ||
absl::optional<PublicPrivateKeyPair> PublicPrivateKeyPair::CreateByImport( | ||
base::span<uint8_t> private_key) { | ||
if (private_key.size() != X25519_PRIVATE_KEY_LEN) { | ||
return {}; | ||
} | ||
return PublicPrivateKeyPair(std::move(private_key)); | ||
} | ||
|
||
PublicPrivateKeyPair::PublicPrivateKeyPair(base::span<uint8_t> private_key) { | ||
CHECK_EQ(static_cast<size_t>(X25519_PRIVATE_KEY_LEN), private_key.size()); | ||
|
||
std::copy(private_key.begin(), private_key.end(), private_key_); | ||
X25519_public_from_private(public_key_, private_key_); | ||
} | ||
|
||
PublicPrivateKeyPair::PublicPrivateKeyPair() { | ||
X25519_keypair(public_key_, private_key_); | ||
} | ||
|
||
std::array<uint8_t, X25519_PRIVATE_KEY_LEN> | ||
PublicPrivateKeyPair::GetRawPrivateKey() const { | ||
std::array<uint8_t, X25519_PRIVATE_KEY_LEN> raw_private_key; | ||
std::copy(private_key_, private_key_ + X25519_PRIVATE_KEY_LEN, | ||
raw_private_key.begin()); | ||
return raw_private_key; | ||
} | ||
|
||
std::array<uint8_t, X25519_PUBLIC_VALUE_LEN> | ||
PublicPrivateKeyPair::GetRawPublicKey() const { | ||
std::array<uint8_t, X25519_PUBLIC_VALUE_LEN> raw_public_key; | ||
std::copy(public_key_, public_key_ + X25519_PUBLIC_VALUE_LEN, | ||
raw_public_key.begin()); | ||
return raw_public_key; | ||
} | ||
|
||
} // namespace syncer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// Copyright 2023 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#ifndef COMPONENTS_SYNC_ENGINE_NIGORI_PUBLIC_PRIVATE_KEY_PAIR_H_ | ||
#define COMPONENTS_SYNC_ENGINE_NIGORI_PUBLIC_PRIVATE_KEY_PAIR_H_ | ||
|
||
#include <array> | ||
#include <memory> | ||
#include <string> | ||
|
||
#include "base/containers/span.h" | ||
#include "third_party/abseil-cpp/absl/types/optional.h" | ||
#include "third_party/boringssl/src/include/openssl/curve25519.h" | ||
|
||
namespace syncer { | ||
|
||
// A wrapper around a 32-byte X25519 public-private key-pair. | ||
class PublicPrivateKeyPair { | ||
public: | ||
// Generate a X25519 key pair. | ||
static PublicPrivateKeyPair GenerateNewKeyPair(); | ||
// Initialize the Public-private key-pair using |private_key|. | ||
static absl::optional<PublicPrivateKeyPair> CreateByImport( | ||
base::span<uint8_t> private_key); | ||
|
||
PublicPrivateKeyPair(const PublicPrivateKeyPair& other) = delete; | ||
PublicPrivateKeyPair(PublicPrivateKeyPair&& other); | ||
PublicPrivateKeyPair& operator=(PublicPrivateKeyPair&& other); | ||
PublicPrivateKeyPair& operator=(const PublicPrivateKeyPair&) = delete; | ||
~PublicPrivateKeyPair(); | ||
|
||
// Returns the raw private key. | ||
std::array<uint8_t, X25519_PRIVATE_KEY_LEN> GetRawPrivateKey() const; | ||
|
||
// Returns the raw public key. | ||
std::array<uint8_t, X25519_PUBLIC_VALUE_LEN> GetRawPublicKey() const; | ||
|
||
private: | ||
PublicPrivateKeyPair(); | ||
explicit PublicPrivateKeyPair(base::span<uint8_t> private_key); | ||
|
||
uint8_t private_key_[X25519_PRIVATE_KEY_LEN]; | ||
uint8_t public_key_[X25519_PUBLIC_VALUE_LEN]; | ||
}; | ||
|
||
} // namespace syncer | ||
|
||
#endif // COMPONENTS_SYNC_ENGINE_NIGORI_PUBLIC_PRIVATE_KEY_PAIR_H_ |
89 changes: 89 additions & 0 deletions
89
components/sync/engine/nigori/public_private_key_pair_unittest.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// Copyright 2023 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "components/sync/engine/nigori/public_private_key_pair.h" | ||
|
||
#include <algorithm> | ||
#include <vector> | ||
|
||
#include "testing/gmock/include/gmock/gmock.h" | ||
#include "testing/gtest/include/gtest/gtest.h" | ||
#include "third_party/boringssl/src/include/openssl/curve25519.h" | ||
|
||
namespace syncer { | ||
namespace { | ||
|
||
TEST(PublicPrivateKeyPairTest, GenerateNewKeyPairShouldAlwaysSucceed) { | ||
PublicPrivateKeyPair key = PublicPrivateKeyPair::GenerateNewKeyPair(); | ||
|
||
EXPECT_THAT(key.GetRawPrivateKey(), testing::SizeIs(X25519_PRIVATE_KEY_LEN)); | ||
EXPECT_THAT(key.GetRawPublicKey(), testing::SizeIs(X25519_PUBLIC_VALUE_LEN)); | ||
} | ||
|
||
TEST(PublicPrivateKeyPairTest, GenerateNewKeyPairShouldGenerateDifferentKeys) { | ||
PublicPrivateKeyPair key_1 = PublicPrivateKeyPair::GenerateNewKeyPair(); | ||
PublicPrivateKeyPair key_2 = PublicPrivateKeyPair::GenerateNewKeyPair(); | ||
|
||
EXPECT_NE(key_1.GetRawPrivateKey(), key_2.GetRawPrivateKey()); | ||
EXPECT_NE(key_1.GetRawPublicKey(), key_2.GetRawPublicKey()); | ||
} | ||
|
||
TEST(PublicPrivateKeyPairTest, | ||
GenerateNewKeyPairShouldGenerateDifferentPublicPrivateParts) { | ||
PublicPrivateKeyPair key = PublicPrivateKeyPair::GenerateNewKeyPair(); | ||
|
||
EXPECT_NE(key.GetRawPrivateKey(), key.GetRawPublicKey()); | ||
} | ||
|
||
TEST(PublicPrivateKeyPairTest, GeneratedPublicKeyShouldMatchX25519Derivation) { | ||
PublicPrivateKeyPair key = PublicPrivateKeyPair::GenerateNewKeyPair(); | ||
|
||
const std::array<uint8_t, X25519_PRIVATE_KEY_LEN> raw_private_key = | ||
key.GetRawPrivateKey(); | ||
const std::array<uint8_t, X25519_PUBLIC_VALUE_LEN> raw_public_key = | ||
key.GetRawPublicKey(); | ||
|
||
uint8_t expected_public_key[X25519_PUBLIC_VALUE_LEN]; | ||
uint8_t expected_private_key_arr[X25519_PRIVATE_KEY_LEN]; | ||
std::copy(raw_private_key.begin(), raw_private_key.end(), | ||
expected_private_key_arr); | ||
X25519_public_from_private(expected_public_key, expected_private_key_arr); | ||
|
||
EXPECT_THAT(raw_public_key, testing::ElementsAreArray(expected_public_key)); | ||
} | ||
|
||
TEST(PublicPrivateKeyPairTest, CreateByImportShouldSucceed) { | ||
std::vector<uint8_t> private_key(X25519_PRIVATE_KEY_LEN, 0xDE); | ||
|
||
absl::optional<PublicPrivateKeyPair> key = | ||
PublicPrivateKeyPair::CreateByImport(private_key); | ||
|
||
ASSERT_TRUE(key.has_value()); | ||
|
||
std::array<uint8_t, X25519_PRIVATE_KEY_LEN> raw_private_key = | ||
key->GetRawPrivateKey(); | ||
|
||
EXPECT_THAT(private_key, testing::ElementsAreArray(raw_private_key)); | ||
} | ||
|
||
TEST(PublicPrivateKeyPairTest, CreateByImportShouldFailOnShorterKey) { | ||
std::vector<uint8_t> private_key(X25519_PRIVATE_KEY_LEN - 1, 0xDE); | ||
|
||
absl::optional<PublicPrivateKeyPair> key = | ||
PublicPrivateKeyPair::CreateByImport(private_key); | ||
|
||
EXPECT_FALSE(key.has_value()); | ||
} | ||
|
||
TEST(PublicPrivateKeyPairTest, CreateByImportShouldFailOnLongerKey) { | ||
std::vector<uint8_t> private_key(X25519_PRIVATE_KEY_LEN + 1, 0xDE); | ||
|
||
absl::optional<PublicPrivateKeyPair> key = | ||
PublicPrivateKeyPair::CreateByImport(private_key); | ||
|
||
EXPECT_FALSE(key.has_value()); | ||
} | ||
|
||
} // namespace | ||
} // namespace syncer |