Skip to content

Commit

Permalink
Add PQC integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rwols committed Feb 7, 2024
1 parent e324350 commit 0fdaa4a
Show file tree
Hide file tree
Showing 14 changed files with 314 additions and 361 deletions.
20 changes: 9 additions & 11 deletions PinataTests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,18 @@ find_package(Boost REQUIRED)

set(DILITHIUM PQClean/crypto_sign/dilithium3/clean)
set(KYBER PQClean/crypto_kem/kyber512/clean)
set(COMMON PQClean/common)

add_executable(PinataTests
main.cpp
dilithium/test-dilithium.cpp
kyber/test-kyber.cpp
tests/Environment.cpp
tests/PinataTests.cpp
utilities/common.cpp
#COMMON
PQClean/common/fips202.c
PQClean/common/randombytes.c
PQClean/common/aes.c
#DILITHIUM
Environment.cpp
ClassicFirmware.cpp
HardwareFirmware.cpp
PqcFirmware.cpp
common.cpp
${COMMON}/fips202.c
${COMMON}/randombytes.c
${COMMON}/aes.c
${DILITHIUM}/packing.c
${DILITHIUM}/ntt.c
${DILITHIUM}/poly.c
Expand All @@ -28,7 +27,6 @@ add_executable(PinataTests
${DILITHIUM}/rounding.c
${DILITHIUM}/sign.c
${DILITHIUM}/symmetric-shake.c
#KYBER
${KYBER}/indcpa.c
${KYBER}/polyvec.c
${KYBER}/reduce.c
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "TestBase.hpp"
#include <array>
#include <cstdint>
#include <cstdlib>
Expand All @@ -7,30 +8,30 @@
#include <openssl/rand.h>
#include <stdexcept>

#include "../kyber/test-kyber.hpp"
#include "Environment.hpp"

using EVP_CIPHER_CTX_ptr = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>;
using AesBlock = std::array<uint8_t, 16>;
using DesBlock = std::array<uint8_t, 8>;

class ClassicFirmware : public ::testing::Test {
class ClassicFirmware : public TestBase {

protected:
PinataClient &mClient;

uint8_t pt_16bytes[16];
uint8_t pt_8bytes[8];
uint8_t ct_16bytes[16];
uint8_t ct_8bytes[8];
uint8_t kyber_data[KYBER512_SHARED_SECRET_SIZE];

ClassicFirmware() : mClient(Environment::getInstance().getClient()) {
ClassicFirmware() {
RAND_bytes(pt_16bytes, 16);
RAND_bytes(ct_16bytes, 16);
RAND_bytes(pt_8bytes, 8);
RAND_bytes(ct_8bytes, 8);
RAND_bytes(kyber_data, KYBER512_SHARED_SECRET_SIZE);
}

void SetUp() override {
const FirmwareVariant variant = Environment::getInstance().getFirmwareVariant();
if (variant != FirmwareVariant::Hardware && variant != FirmwareVariant::Classic) {
GTEST_SKIP();
}
}

AesBlock AES128_ecb_encrypt(uint8_t pt[16], const uint8_t key[16]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ void Environment::SetUp() {
"will start failing. So we stop here.\n";
throw;
}
mFirmwareVariant = mClient->determineFirmwareVariant();
}

void Environment::TearDown() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#include "../utilities/common.hpp"
#include "common.hpp"
#include <gtest/gtest.h>
#include <optional>

Expand All @@ -10,6 +10,7 @@ class Environment : public ::testing::Environment {
std::optional<PinataClient> mClient;
int mClientVersionMajor = 0;
int mClientVersionMinor = 0;
FirmwareVariant mFirmwareVariant = FirmwareVariant::Classic;
static Environment *gInstance;

public:
Expand Down Expand Up @@ -37,4 +38,6 @@ class Environment : public ::testing::Environment {

/// Get the client's minor version number.
int getClientVersionMinor() const noexcept { return mClientVersionMinor; }

FirmwareVariant getFirmwareVariant() const noexcept { return mFirmwareVariant; }
};
15 changes: 15 additions & 0 deletions PinataTests/HardwareFirmware.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "TestBase.hpp"
#include <gtest/gtest.h>

class HardwareFirmware : public TestBase {
void SetUp() override {
if (Environment::getInstance().getFirmwareVariant() != FirmwareVariant::Hardware) {
GTEST_SKIP();
}
}
};

TEST_F(HardwareFirmware, AES) {
// TODO
ASSERT_TRUE(true);
}
170 changes: 170 additions & 0 deletions PinataTests/PqcFirmware.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#include "TestBase.hpp"
#include <array>
#include <gtest/gtest.h>
#include <openssl/rand.h>

extern "C" {
#include "PQClean/crypto_kem/kyber512/clean/api.h"
#include "PQClean/crypto_sign/dilithium3/clean/api.h"
}

#define DILITHIUM_PUBLIC_KEY_SIZE 1952
#define DILITHIUM_PRIVATE_KEY_SIZE 4016
#define DILITHIUM_SIGNATURE_SIZE 3293
#define DILITHIUM_MESSAGE_SIZE 16
#define DILITHIUM_SIGNED_MESSAGE_SIZE (DILITHIUM_SIGNATURE_SIZE + DILITHIUM_MESSAGE_SIZE)

#define KYBER512_PUBLIC_KEY_SIZE 800
#define KYBER512_PRIVATE_KEY_SIZE 1632
#define KYBER512_SHARED_SECRET_SIZE 32
#define KYBER512_CIPHERTEXT_SIZE 768

#if DILITHIUM_PUBLIC_KEY_SIZE != PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_PUBLICKEYBYTES
#error invalid public key size, update me!
#endif
#if DILITHIUM_PRIVATE_KEY_SIZE != PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_SECRETKEYBYTES
#error invalid private key size, update me!
#endif
#if DILITHIUM_SIGNATURE_SIZE != PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_BYTES
#error invalid signature size, update me!
#endif

#if defined(MODE) && !defined(DILITHIUM_MODE)
#define DILITHIUM_MODE MODE
#endif

#if KYBER512_PUBLIC_KEY_SIZE != PQCLEAN_KYBER512_CLEAN_CRYPTO_PUBLICKEYBYTES
#error invalid public key size, update me!
#endif
#if KYBER512_PRIVATE_KEY_SIZE != PQCLEAN_KYBER512_CLEAN_CRYPTO_SECRETKEYBYTES
#error invalid secret key size, update me!
#endif
#if KYBER512_SHARED_SECRET_SIZE != PQCLEAN_KYBER512_CLEAN_CRYPTO_BYTES
#error invalid secret size, update me!
#endif

class PqcFirmware : public TestBase {
void SetUp() override {
if (Environment::getInstance().getFirmwareVariant() != FirmwareVariant::PostQuantum) {
GTEST_SKIP();
}
}
};

TEST_F(PqcFirmware, DilithiumLevel3) {
std::array<unsigned char, DILITHIUM_PUBLIC_KEY_SIZE> publicKey;
std::array<unsigned char, DILITHIUM_PRIVATE_KEY_SIZE> privateKey;
std::array<unsigned char, DILITHIUM_MESSAGE_SIZE> message;
std::array<unsigned char, PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_BYTES + DILITHIUM_MESSAGE_SIZE> pinataSignedMessage;
std::array<unsigned char, PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_BYTES + DILITHIUM_MESSAGE_SIZE> referenceSignedMessage;

// Ensure the mode is the same
std::cerr << "asserting security level\n";
ASSERT_EQ(mClient.dilithiumGetSecurityLevel(), 3);

// Ensure public and private key sizes match
std::cerr << "checking key sizes\n";
const auto [pinataPublicKeySize, pinataPrivateKeySize] = mClient.dilithiumGetKeySizes();
ASSERT_EQ(pinataPublicKeySize, PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_PUBLICKEYBYTES);
ASSERT_EQ(pinataPrivateKeySize, PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_SECRETKEYBYTES);

// Generate a public/private key pair with the reference X86 implementation
PQCLEAN_DILITHIUM3_CLEAN_crypto_sign_keypair(publicKey.data(), privateKey.data());

// Tell the pinata to use this public/private key pair for signing with Dilithium3
std::cerr << "setup public/private key pair\n";
mClient.dilithiumSetPublicPrivateKeyPair(publicKey.data(), publicKey.size(), privateKey.data(), privateKey.size());

// Prepare the random message
std::fill(pinataSignedMessage.begin(), pinataSignedMessage.end(), (unsigned char)0);
RAND_bytes(message.data(), message.size());

// Sign the fuzzed message on pinata
std::cerr << "sign message\n";
mClient.dilithiumSign(message.data(), message.size(), pinataSignedMessage.data(),
PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_BYTES);

// Concatenate the signature and the fuzzed message together to obtain a "signed message"
ASSERT_EQ(pinataSignedMessage.size(), PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_BYTES + message.size());
std::copy(message.begin(), message.end(), pinataSignedMessage.data() + PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_BYTES);

// The message should be at the end of the signed message buffer
ASSERT_EQ(std::memcmp(pinataSignedMessage.data() + PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_BYTES, message.begin(), 16), 0);

// Sign the fuzzed message with the X86 reference implementation.
// The reference implementation doesn't use randomized signatures.
unsigned long messageLength = static_cast<unsigned long>(pinataSignedMessage.size());
PQCLEAN_DILITHIUM3_CLEAN_crypto_sign(referenceSignedMessage.data(), &messageLength, message.data(), message.size(),
privateKey.data());
ASSERT_EQ(messageLength, referenceSignedMessage.size());

// Pinata sign --> Reference verify
ASSERT_EQ(PQCLEAN_DILITHIUM3_CLEAN_crypto_sign_open(pinataSignedMessage.data(), &messageLength,
pinataSignedMessage.data(), pinataSignedMessage.size(),
publicKey.data()),
0);

// Reference sign --> Pinata verify
std::cerr << "verify message\n";
ASSERT_TRUE(mClient.dilithiumVerify(referenceSignedMessage.data(), referenceSignedMessage.size()));
}

TEST_F(PqcFirmware, Kyber512) {
std::array<unsigned char, KYBER512_PUBLIC_KEY_SIZE> publicKey;
std::array<unsigned char, KYBER512_PRIVATE_KEY_SIZE> privateKey;
std::array<unsigned char, PQCLEAN_KYBER512_CLEAN_CRYPTO_BYTES> ssPinata;
std::array<unsigned char, PQCLEAN_KYBER512_CLEAN_CRYPTO_BYTES> ssRef;
std::array<unsigned char, PQCLEAN_KYBER512_CLEAN_CRYPTO_BYTES> ssPinataGenerateRefDecode;
std::array<unsigned char, PQCLEAN_KYBER512_CLEAN_CRYPTO_BYTES> ssRefGeneratePinataDecode;
std::array<unsigned char, PQCLEAN_KYBER512_CLEAN_CRYPTO_BYTES> ssRefGenerateRefDecode;
std::array<unsigned char, PQCLEAN_KYBER512_CLEAN_CRYPTO_BYTES> ssPinataGeneratePinataDecode;
std::array<unsigned char, PQCLEAN_KYBER512_CLEAN_CRYPTO_CIPHERTEXTBYTES> ctPinata;
std::array<unsigned char, PQCLEAN_KYBER512_CLEAN_CRYPTO_CIPHERTEXTBYTES> ctRef;

// Ensure public and private key sizes match
std::cerr << "checking wether key sizes agree\n";
const auto [pinataPublicKeySize, pinataPrivateKeySize] = mClient.kyber512GetKeySizes();
ASSERT_EQ(pinataPublicKeySize, PQCLEAN_KYBER512_CLEAN_CRYPTO_PUBLICKEYBYTES);
ASSERT_EQ(pinataPrivateKeySize, PQCLEAN_KYBER512_CLEAN_CRYPTO_SECRETKEYBYTES);

// Generate a public/private key pair with the reference X86 implementation
PQCLEAN_KYBER512_CLEAN_crypto_kem_keypair(publicKey.data(), privateKey.data());

// Tell the pinata to use this public/private key pair for encrypting shared secrets with kyber512.
std::cerr << "setting public private key pair\n";
mClient.kyber512SetPublicPrivateKeyPair(publicKey.data(), publicKey.size(), privateKey.data(), privateKey.size());

// Zero out arrays
std::fill(ssPinataGenerateRefDecode.begin(), ssPinataGenerateRefDecode.end(), (unsigned char)0);
std::fill(ssRefGeneratePinataDecode.begin(), ssRefGeneratePinataDecode.end(), (unsigned char)0);
std::fill(ssRefGenerateRefDecode.begin(), ssRefGenerateRefDecode.end(), (unsigned char)0);
std::fill(ssPinataGeneratePinataDecode.begin(), ssPinataGeneratePinataDecode.end(), (unsigned char)0);

// Generate a shared secret on Pinata
std::cerr << "generating shared secret\n";
mClient.kyber512Generate(ssPinata.data(), ssPinata.size(), ctPinata.data(), ctPinata.size());

// Generate a shared secret with the reference implementation.
PQCLEAN_KYBER512_CLEAN_crypto_kem_enc(ctRef.data(), ssRef.data(), publicKey.data());

// Decode the Pinata ciphertext with ref impl
PQCLEAN_KYBER512_CLEAN_crypto_kem_dec(ssPinataGenerateRefDecode.data(), ctPinata.data(), privateKey.data());

// Decode the ref ciphertext with ref impl
PQCLEAN_KYBER512_CLEAN_crypto_kem_dec(ssRefGenerateRefDecode.data(), ctRef.data(), privateKey.data());

// Decode the Pinata ciphertext with Pinata impl
std::cerr << "decoding shared secret\n";
mClient.kyber512Decode(ctPinata.data(), ctPinata.size(), ssPinataGeneratePinataDecode.data(),
ssPinataGeneratePinataDecode.size());

// Decode the ref ciphertext with Pinata impl
std::cerr << "decoding shared secret (ref)\n";
mClient.kyber512Decode(ctRef.data(), ctRef.size(), ssRefGeneratePinataDecode.data(),
ssRefGeneratePinataDecode.size());

ASSERT_EQ(ssPinata, ssPinataGeneratePinataDecode);
ASSERT_EQ(ssPinata, ssPinataGenerateRefDecode);
ASSERT_EQ(ssRef, ssRefGenerateRefDecode);
ASSERT_EQ(ssRef, ssRefGeneratePinataDecode);
}
12 changes: 12 additions & 0 deletions PinataTests/TestBase.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include "Environment.hpp"
#include <gtest/gtest.h>

class TestBase : public ::testing::Test {

protected:
PinataClient &mClient;

TestBase() : mClient(Environment::getInstance().getClient()) {}
};
Loading

0 comments on commit 0fdaa4a

Please sign in to comment.