Skip to content

Commit

Permalink
Add random nonces to Oak C++ and Java Crypto (#4615)
Browse files Browse the repository at this point in the history
This PR makes Oak C++ and Java Hybrid Encryption use random nonces.

Ref #4507
  • Loading branch information
ipetr0v authored Jan 9, 2024
1 parent 71b4014 commit 3a28b30
Show file tree
Hide file tree
Showing 23 changed files with 152 additions and 269 deletions.
11 changes: 8 additions & 3 deletions cc/crypto/client_encryptor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "absl/status/statusor.h"
#include "cc/crypto/common.h"
#include "cc/crypto/hpke/sender_context.h"
#include "cc/crypto/hpke/utils.h"
#include "oak_crypto/proto/v1/crypto.pb.h"

namespace oak::crypto {
Expand All @@ -45,16 +46,20 @@ absl::StatusOr<std::unique_ptr<ClientEncryptor>> ClientEncryptor::Create(
absl::StatusOr<EncryptedRequest> ClientEncryptor::Encrypt(absl::string_view plaintext,
absl::string_view associated_data) {
// Encrypt request.
const std::vector<uint8_t> nonce = sender_context_->GenerateNonce();
absl::StatusOr<std::string> ciphertext = sender_context_->Seal(nonce, plaintext, associated_data);
absl::StatusOr<const std::vector<uint8_t>> nonce = GenerateRandomNonce();
if (!nonce.ok()) {
return nonce.status();
}
absl::StatusOr<std::string> ciphertext =
sender_context_->Seal(*nonce, plaintext, associated_data);
if (!ciphertext.ok()) {
return ciphertext.status();
}

// Create request message.
EncryptedRequest encrypted_request;
*encrypted_request.mutable_encrypted_message()->mutable_nonce() =
std::string(nonce.begin(), nonce.end());
std::string(nonce->begin(), nonce->end());
*encrypted_request.mutable_encrypted_message()->mutable_ciphertext() = *ciphertext;
*encrypted_request.mutable_encrypted_message()->mutable_associated_data() = associated_data;

Expand Down
12 changes: 12 additions & 0 deletions cc/crypto/hpke/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,15 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)

cc_test(
name = "utils_test",
size = "small",
srcs = ["utils_test.cc"],
deps = [
":utils",
"@boringssl//:crypto",
"@com_google_absl//absl/status:statusor",
"@com_google_googletest//:gtest_main",
],
)
1 change: 1 addition & 0 deletions cc/crypto/hpke/jni/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ cc_library(
":jni",
"//cc/crypto/hpke:recipient_context",
"//cc/crypto/hpke:sender_context",
"//cc/crypto/hpke:utils",
"@com_google_absl//absl/status:statusor",
],
alwayslink = 1,
Expand Down
8 changes: 8 additions & 0 deletions cc/crypto/hpke/jni/com_google_oak_crypto_hpke_Hpke.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 0 additions & 8 deletions cc/crypto/hpke/jni/com_google_oak_crypto_hpke_SenderContext.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 0 additions & 34 deletions cc/crypto/hpke/jni/context_jni.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,6 @@
#include "com_google_oak_crypto_hpke_SenderContext.h"
#include "jni_helper.h"

JNIEXPORT jbyteArray JNICALL
Java_com_google_oak_crypto_hpke_SenderContext_nativeGenerateNonce(JNIEnv* env, jobject obj) {
jclass sender_context_class = env->GetObjectClass(obj);
jfieldID fid = env->GetFieldID(sender_context_class, "nativePtr", "J");
oak::crypto::SenderContext* sender_context =
(oak::crypto::SenderContext*)(env->GetLongField(obj, fid));
if (sender_context == NULL) {
return {};
}

std::vector<uint8_t> nonce = sender_context->GenerateNonce();

jbyteArray ret = env->NewByteArray(nonce.size());
env->SetByteArrayRegion(ret, 0, nonce.size(), reinterpret_cast<const jbyte*>(&nonce.front()));
return ret;
}

JNIEXPORT jbyteArray JNICALL Java_com_google_oak_crypto_hpke_SenderContext_nativeSeal(
JNIEnv* env, jobject obj, jbyteArray nonce, jbyteArray plaintext, jbyteArray associated_data) {
if (nonce == NULL || plaintext == NULL || associated_data == NULL) {
Expand Down Expand Up @@ -100,23 +83,6 @@ JNIEXPORT jbyteArray JNICALL Java_com_google_oak_crypto_hpke_SenderContext_nativ
return ret;
}

JNIEXPORT jbyteArray JNICALL
Java_com_google_oak_crypto_hpke_RecipientContext_nativeGenerateNonce(JNIEnv* env, jobject obj) {
jclass recipient_context_class = env->GetObjectClass(obj);
jfieldID fid = env->GetFieldID(recipient_context_class, "nativePtr", "J");
oak::crypto::RecipientContext* recipient_context =
(oak::crypto::RecipientContext*)(env->GetLongField(obj, fid));
if (recipient_context == NULL) {
return {};
}

std::vector<uint8_t> nonce = recipient_context->GenerateNonce();

jbyteArray ret = env->NewByteArray(nonce.size());
env->SetByteArrayRegion(ret, 0, nonce.size(), reinterpret_cast<const jbyte*>(&nonce.front()));
return ret;
}

JNIEXPORT jbyteArray JNICALL Java_com_google_oak_crypto_hpke_RecipientContext_nativeOpen(
JNIEnv* env, jobject obj, jbyteArray nonce, jbyteArray ciphertext, jbyteArray associated_data) {
if (ciphertext == NULL || associated_data == NULL) {
Expand Down
13 changes: 13 additions & 0 deletions cc/crypto/hpke/jni/hpke_jni.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "../recipient_context.h"
#include "../sender_context.h"
#include "../utils.h"
#include "absl/status/statusor.h"
#include "com_google_oak_crypto_hpke_Hpke.h"
#include "jni_helper.h"
Expand Down Expand Up @@ -96,3 +97,15 @@ JNIEXPORT jobject JNICALL Java_com_google_oak_crypto_hpke_Hpke_nativeSetupBaseRe

return recipient_context;
}

JNIEXPORT jbyteArray JNICALL
Java_com_google_oak_crypto_hpke_Hpke_nativeGenerateRandomNonce(JNIEnv* env, jclass obj) {
absl::StatusOr<std::vector<uint8_t>> nonce = oak::crypto::GenerateRandomNonce();
if (!nonce.ok()) {
return {};
}

jbyteArray ret = env->NewByteArray(nonce->size());
env->SetByteArrayRegion(ret, 0, nonce->size(), reinterpret_cast<const jbyte*>(&nonce->front()));
return ret;
}
56 changes: 4 additions & 52 deletions cc/crypto/hpke/recipient_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@

namespace oak::crypto {

const uint64_t kStartingSequenceNumber = 0;

namespace {
using ::oak::crypto::v1::CryptoContext;

Expand Down Expand Up @@ -97,62 +95,30 @@ absl::StatusOr<std::unique_ptr<RecipientContext>> RecipientContext::Deserialize(
return absl::AbortedError("Unable to deserialize response AEAD context");
}

std::vector<uint8_t> request_base_nonce(serialized_recipient_context.request_base_nonce().begin(),
serialized_recipient_context.request_base_nonce().end());

std::vector<uint8_t> response_base_nonce(
serialized_recipient_context.response_base_nonce().begin(),
serialized_recipient_context.response_base_nonce().end());

return std::make_unique<RecipientContext>(
/* request_aead_context= */ std::move(request_aead_context),
/* request_base_nonce= */ request_base_nonce,
/* request_sequence_number= */ serialized_recipient_context.request_sequence_number(),
/* response_aead_context= */ std::move(response_aead_context),
/* response_base_nonce= */ response_base_nonce,
/* response_sequence_number= */ serialized_recipient_context.response_sequence_number());
}

std::vector<uint8_t> RecipientContext::GenerateNonce() {
std::vector<uint8_t> nonce = CalculateNonce(response_base_nonce_, response_sequence_number_);
return nonce;
/* response_aead_context= */ std::move(response_aead_context));
}

absl::StatusOr<std::string> RecipientContext::Open(const std::vector<uint8_t>& nonce,
absl::string_view ciphertext,
absl::string_view associated_data) {
/// Maximum sequence number which can fit in kAeadNonceSizeBytes bytes.
/// <https://www.rfc-editor.org/rfc/rfc9180.html#name-encryption-and-decryption>
if (request_sequence_number_ == UINT64_MAX) {
return absl::OutOfRangeError("Maximum sequence number reached");
}

absl::StatusOr<std::string> plaintext =
AeadOpen(request_aead_context_.get(), nonce, ciphertext, associated_data);
if (!plaintext.ok()) {
return plaintext.status();
}
request_sequence_number_ += 1;

return plaintext;
}

absl::StatusOr<std::string> RecipientContext::Seal(const std::vector<uint8_t>& nonce,
absl::string_view plaintext,
absl::string_view associated_data) {
/// Maximum sequence number which can fit in kAeadNonceSizeBytes bytes.
/// <https://www.rfc-editor.org/rfc/rfc9180.html#name-encryption-and-decryption>
if (response_sequence_number_ == UINT64_MAX) {
return absl::OutOfRangeError("Maximum sequence number reached");
}

absl::StatusOr<std::string> ciphertext =
AeadSeal(response_aead_context_.get(), nonce, plaintext, associated_data);
if (!ciphertext.ok()) {
return ciphertext.status();
}
response_sequence_number_ += 1;

return ciphertext;
}

Expand Down Expand Up @@ -198,7 +164,7 @@ absl::StatusOr<std::unique_ptr<RecipientContext>> SetupBaseRecipient(
return absl::AbortedError("Unable to setup recipient context");
}

// Configure recipient request context and nonce.
// Configure recipient request context.
// This is a deviation from the HPKE RFC, because we are deriving both session request and
// response keys from the exporter secret, instead of having a request key be directly derived
// from the shared secret. This is required to be able to share session keys between the Kernel
Expand All @@ -209,30 +175,16 @@ absl::StatusOr<std::unique_ptr<RecipientContext>> SetupBaseRecipient(
return request_aead_context.status();
}

auto request_nonce = GetBaseNonce(hpke_recipient_context.get(), "request_nonce");
if (!request_nonce.ok()) {
return request_nonce.status();
}

// Configure recipient response context and nonce.
// Configure recipient response context.
auto response_aead_context = GetContext(hpke_recipient_context.get(), "response_key");
if (!response_aead_context.ok()) {
return response_aead_context.status();
}

auto response_nonce = GetBaseNonce(hpke_recipient_context.get(), "response_nonce");
if (!response_nonce.ok()) {
return response_nonce.status();
}

// Create recipient context.
std::unique_ptr<RecipientContext> recipient_context = std::make_unique<RecipientContext>(
/* request_aead_context= */ *std::move(request_aead_context),
/* request_base_nonce= */ *request_nonce,
/* request_sequence_number= */ kStartingSequenceNumber,
/* response_aead_context= */ *std::move(response_aead_context),
/* response_base_nonce= */ *response_nonce,
/* response_sequence_number= */ kStartingSequenceNumber);
/* response_aead_context= */ *std::move(response_aead_context));

EVP_HPKE_CTX_free(hpke_recipient_context.release());
return recipient_context;
Expand Down
17 changes: 2 additions & 15 deletions cc/crypto/hpke/recipient_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,13 @@ struct KeyPair {
class RecipientContext {
public:
RecipientContext(std::unique_ptr<EVP_AEAD_CTX> request_aead_context,
std::vector<uint8_t> request_base_nonce, uint64_t request_sequence_number,
std::unique_ptr<EVP_AEAD_CTX> response_aead_context,
std::vector<uint8_t> response_base_nonce, uint64_t response_sequence_number)
std::unique_ptr<EVP_AEAD_CTX> response_aead_context)
: request_aead_context_(std::move(request_aead_context)),
request_base_nonce_(request_base_nonce),
request_sequence_number_(request_sequence_number),
response_aead_context_(std::move(response_aead_context)),
response_base_nonce_(response_base_nonce),
response_sequence_number_(response_sequence_number) {}
response_aead_context_(std::move(response_aead_context)) {}

static absl::StatusOr<std::unique_ptr<RecipientContext>> Deserialize(
::oak::crypto::v1::CryptoContext serialized_recipient_context);

std::vector<uint8_t> GenerateNonce();

// Decrypts message and validates associated data using AEAD.
// <https://www.rfc-editor.org/rfc/rfc9180.html#name-encryption-and-decryption>
absl::StatusOr<std::string> Open(const std::vector<uint8_t>& nonce, absl::string_view ciphertext,
Expand All @@ -69,12 +61,7 @@ class RecipientContext {

private:
std::unique_ptr<EVP_AEAD_CTX> request_aead_context_;
std::vector<uint8_t> request_base_nonce_;
uint64_t request_sequence_number_;

std::unique_ptr<EVP_AEAD_CTX> response_aead_context_;
std::vector<uint8_t> response_base_nonce_;
uint64_t response_sequence_number_;
};

// Sets up an HPKE recipient by creating a recipient context.
Expand Down
Loading

0 comments on commit 3a28b30

Please sign in to comment.