Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added .clangd
Empty file.
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.16)
project(Pinata VERSION 3.2 LANGUAGES C ASM)
project(Pinata VERSION 4.0 LANGUAGES C ASM)

if(NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "")
message(STATUS "Setting CMAKE_BUILD_TYPE to MinSizeRel")
Expand Down
30 changes: 25 additions & 5 deletions PinataTests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
cmake_minimum_required(VERSION 3.16)
project(PinataTests C CXX)

if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.27)
cmake_policy(SET CMP0144 NEW)
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.30)
cmake_policy(SET CMP0167 NEW)
endif()
endif()

project(PinataTests VERSION 4.0 LANGUAGES C CXX)

find_package(OpenSSL REQUIRED)
find_package(GTest REQUIRED)
find_package(Boost REQUIRED)

set(DILITHIUM PQClean/crypto_sign/dilithium3/clean)
set(KYBER PQClean/crypto_kem/kyber512/clean)
set(COMMON PQClean/common)
include(FetchContent)
FetchContent_Declare(
pqm4
GIT_REPOSITORY https://github.com/mupq/pqm4.git
# Keep this git hash the same as the one in src/CMakeLists.txt !
GIT_TAG a24bb4b662016968c19f5e6a0719c9ad530f0286
)
FetchContent_MakeAvailable(pqm4)

# We can reuse the PQClean sources checked out by PQM4.
set(DILITHIUM "${pqm4_SOURCE_DIR}/mupq/pqclean/crypto_sign/ml-dsa-65/clean")
set(KYBER "${pqm4_SOURCE_DIR}/mupq/pqclean/crypto_kem/ml-kem-512/clean")
set(COMMON "${pqm4_SOURCE_DIR}/mupq/pqclean/common")

add_executable(PinataTests
main.cpp
Expand Down Expand Up @@ -39,5 +57,7 @@ add_executable(PinataTests
)

target_compile_features(PinataTests PRIVATE cxx_std_20)
target_include_directories(PinataTests PRIVATE PQClean/common)
target_include_directories(PinataTests PRIVATE "${pqm4_SOURCE_DIR}/mupq/pqclean/common")
set_source_files_properties(PqcFirmware.cpp PROPERTIES INCLUDE_DIRECTORIES "${pqm4_SOURCE_DIR}/mupq/pqclean")
target_link_libraries(PinataTests PRIVATE Boost::boost OpenSSL::Crypto GTest::GTest)

1 change: 0 additions & 1 deletion PinataTests/PQClean
Submodule PQClean deleted from 4f86c3
121 changes: 59 additions & 62 deletions PinataTests/PqcFirmware.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,39 @@
#include <openssl/rand.h>

extern "C" {
#include "PQClean/crypto_kem/kyber512/clean/api.h"
#include "PQClean/crypto_sign/dilithium3/clean/api.h"
#include "crypto_kem/ml-kem-512/clean/api.h"
#include "crypto_sign/ml-dsa-65/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 MLDSA_PUBLIC_KEY_SIZE 1952
#define MLDSA_PRIVATE_KEY_SIZE 4032
#define MLDSA_SIGNATURE_SIZE 3309
#define MLDSA_MESSAGE_SIZE 16
#define MLDSA_N 256
#define MLDSA_SIGNED_MESSAGE_SIZE (MLDSA_SIGNATURE_SIZE + MLDSA_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
#define MLKEM_PUBLIC_KEY_SIZE 800
#define MLKEM_PRIVATE_KEY_SIZE 1632
#define MLKEM_SHARED_SECRET_SIZE 32
#define MLKEM_CIPHERTEXT_SIZE 768

#if DILITHIUM_PUBLIC_KEY_SIZE != PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_PUBLICKEYBYTES
#if MLDSA_PUBLIC_KEY_SIZE != PQCLEAN_MLDSA65_CLEAN_CRYPTO_PUBLICKEYBYTES
#error invalid public key size, update me!
#endif
#if DILITHIUM_PRIVATE_KEY_SIZE != PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_SECRETKEYBYTES
#if MLDSA_PRIVATE_KEY_SIZE != PQCLEAN_MLDSA65_CLEAN_CRYPTO_SECRETKEYBYTES
#error invalid private key size, update me!
#endif
#if DILITHIUM_SIGNATURE_SIZE != PQCLEAN_DILITHIUM3_CLEAN_CRYPTO_BYTES
#if MLDSA_SIGNATURE_SIZE != PQCLEAN_MLDSA65_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
#if MLKEM_PUBLIC_KEY_SIZE != PQCLEAN_MLKEM512_CLEAN_CRYPTO_PUBLICKEYBYTES
#error invalid public key size, update me!
#endif
#if KYBER512_PRIVATE_KEY_SIZE != PQCLEAN_KYBER512_CLEAN_CRYPTO_SECRETKEYBYTES
#if MLKEM_PRIVATE_KEY_SIZE != PQCLEAN_MLKEM512_CLEAN_CRYPTO_SECRETKEYBYTES
#error invalid secret key size, update me!
#endif
#if KYBER512_SHARED_SECRET_SIZE != PQCLEAN_KYBER512_CLEAN_CRYPTO_BYTES
#if MLKEM_SHARED_SECRET_SIZE != PQCLEAN_MLKEM512_CLEAN_CRYPTO_BYTES
#error invalid secret size, update me!
#endif

Expand All @@ -52,87 +49,87 @@ class PqcFirmware : public TestBase {
};

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;
std::array<unsigned char, MLDSA_PUBLIC_KEY_SIZE> publicKey;
std::array<unsigned char, MLDSA_PRIVATE_KEY_SIZE> privateKey;
std::array<unsigned char, MLDSA_MESSAGE_SIZE> message;
std::array<unsigned char, PQCLEAN_MLDSA65_CLEAN_CRYPTO_BYTES + MLDSA_MESSAGE_SIZE> pinataSignedMessage;
std::array<unsigned char, PQCLEAN_MLDSA65_CLEAN_CRYPTO_BYTES + MLDSA_MESSAGE_SIZE> referenceSignedMessage;

// Ensure the mode is the same
std::cerr << "asserting security level\n";
ASSERT_EQ(mClient.dilithiumGetSecurityLevel(), 3);
ASSERT_EQ(mClient.mldsaGetSecurityLevel(), 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);
const auto [pinataPublicKeySize, pinataPrivateKeySize] = mClient.mldsaGetKeySizes();
ASSERT_EQ(pinataPublicKeySize, PQCLEAN_MLDSA65_CLEAN_CRYPTO_PUBLICKEYBYTES);
ASSERT_EQ(pinataPrivateKeySize, PQCLEAN_MLDSA65_CLEAN_CRYPTO_SECRETKEYBYTES);

// Generate a public/private key pair with the reference X86 implementation
PQCLEAN_DILITHIUM3_CLEAN_crypto_sign_keypair(publicKey.data(), privateKey.data());
PQCLEAN_MLDSA65_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());
mClient.mldsaSetPublicPrivateKeyPair(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);
mClient.mldsaSign(message.data(), message.size(), pinataSignedMessage.data(),
PQCLEAN_MLDSA65_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);
ASSERT_EQ(pinataSignedMessage.size(), PQCLEAN_MLDSA65_CLEAN_CRYPTO_BYTES + message.size());
std::copy(message.begin(), message.end(), pinataSignedMessage.data() + PQCLEAN_MLDSA65_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);
ASSERT_EQ(std::memcmp(pinataSignedMessage.data() + PQCLEAN_MLDSA65_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(),
PQCLEAN_MLDSA65_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,
ASSERT_EQ(PQCLEAN_MLDSA65_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()));
ASSERT_TRUE(mClient.mldsaVerify(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;
std::array<unsigned char, MLKEM_PUBLIC_KEY_SIZE> publicKey;
std::array<unsigned char, MLKEM_PRIVATE_KEY_SIZE> privateKey;
std::array<unsigned char, PQCLEAN_MLKEM512_CLEAN_CRYPTO_BYTES> ssPinata;
std::array<unsigned char, PQCLEAN_MLKEM512_CLEAN_CRYPTO_BYTES> ssRef;
std::array<unsigned char, PQCLEAN_MLKEM512_CLEAN_CRYPTO_BYTES> ssPinataGenerateRefDecode;
std::array<unsigned char, PQCLEAN_MLKEM512_CLEAN_CRYPTO_BYTES> ssRefGeneratePinataDecode;
std::array<unsigned char, PQCLEAN_MLKEM512_CLEAN_CRYPTO_BYTES> ssRefGenerateRefDecode;
std::array<unsigned char, PQCLEAN_MLKEM512_CLEAN_CRYPTO_BYTES> ssPinataGeneratePinataDecode;
std::array<unsigned char, PQCLEAN_MLKEM512_CLEAN_CRYPTO_CIPHERTEXTBYTES> ctPinata;
std::array<unsigned char, PQCLEAN_MLKEM512_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);
const auto [pinataPublicKeySize, pinataPrivateKeySize] = mClient.mlkemGetKeySizes();
ASSERT_EQ(pinataPublicKeySize, PQCLEAN_MLKEM512_CLEAN_CRYPTO_PUBLICKEYBYTES);
ASSERT_EQ(pinataPrivateKeySize, PQCLEAN_MLKEM512_CLEAN_CRYPTO_SECRETKEYBYTES);

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

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

// Zero out arrays
std::fill(ssPinataGenerateRefDecode.begin(), ssPinataGenerateRefDecode.end(), (unsigned char)0);
Expand All @@ -142,25 +139,25 @@ TEST_F(PqcFirmware, Kyber512) {

// Generate a shared secret on Pinata
std::cerr << "generating shared secret\n";
mClient.kyber512Generate(ssPinata.data(), ssPinata.size(), ctPinata.data(), ctPinata.size());
mClient.mlkemGenerate(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());
PQCLEAN_MLKEM512_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());
PQCLEAN_MLKEM512_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());
PQCLEAN_MLKEM512_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(),
mClient.mlkemDecode(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(),
mClient.mlkemDecode(ctRef.data(), ctRef.size(), ssRefGeneratePinataDecode.data(),
ssRefGeneratePinataDecode.size());

ASSERT_EQ(ssPinata, ssPinataGeneratePinataDecode);
Expand Down
Loading
Loading