Skip to content

Commit

Permalink
add C++ linking test (#1971)
Browse files Browse the repository at this point in the history
Signed-off-by: Aiden Fox Ivey <[email protected]>
  • Loading branch information
aidenfoxivey authored Nov 1, 2024
1 parent 60af4a9 commit 05257da
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 8 deletions.
44 changes: 36 additions & 8 deletions .github/workflows/basic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ permissions:
on: [workflow_call, workflow_dispatch]

jobs:

workflowcheck:
name: Check validity of GitHub workflows
runs-on: ubuntu-latest
Expand All @@ -19,7 +18,7 @@ jobs:

stylecheck:
name: Check code formatting
needs: [ workflowcheck ]
needs: [workflowcheck]
runs-on: ubuntu-latest
container: openquantumsafe/ci-ubuntu-latest:latest
steps:
Expand All @@ -34,7 +33,7 @@ jobs:

upstreamcheck:
name: Check upstream code is properly integrated
needs: [ workflowcheck ]
needs: [workflowcheck]
runs-on: ubuntu-latest
container: openquantumsafe/ci-ubuntu-latest:latest
steps:
Expand All @@ -47,19 +46,19 @@ jobs:
git config --global --add safe.directory "$PWD" && \
echo "LIBOQS_DIR=$PWD" >> "$GITHUB_ENV"
- name: Verify copy_from_upstream state after copy
working-directory: 'scripts/copy_from_upstream'
working-directory: "scripts/copy_from_upstream"
run: |
python3 copy_from_upstream.py copy && \
! git status | grep -i modified
- name: Verify copy_from_upstream state after libjade
working-directory: 'scripts/copy_from_upstream'
working-directory: "scripts/copy_from_upstream"
run: |
python3 copy_from_upstream.py libjade && \
! git status | grep -i modified
buildcheck:
name: Check that code passes a basic build
needs: [ workflowcheck, stylecheck, upstreamcheck ]
needs: [workflowcheck, stylecheck, upstreamcheck]
runs-on: ubuntu-latest
container: openquantumsafe/ci-ubuntu-latest:latest
env:
Expand All @@ -86,10 +85,39 @@ jobs:
run: ninja gen_docs
working-directory: build


cppcheck:
name: Check C++ linking with example program
runs-on: ubuntu-latest
container: openquantumsafe/ci-ubuntu-latest:latest
env:
SIG_NAME: dilithium_2
steps:
- name: Checkout code
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4
- name: Configure
run: |
mkdir build && \
cd build && \
cmake -GNinja -DOQS_STRICT_WARNINGS=ON \
-GNinja \
-DOQS_MINIMAL_BUILD="SIG_$SIG_NAME" \
--warn-uninitialized .. > config.log 2>&1 && \
cat config.log && \
cmake -LA -N .. && \
! (grep -i "uninitialized variable" config.log)
- name: Build liboqs
run: ninja
working-directory: build
- name: Link with C++ program
run: |
g++ ../cpp/sig_linking_test.cpp -g \
-I./include -L./lib -loqs -lcrypto -std=c++11 -o example_sig && \
./example_sig
working-directory: build

fuzzbuildcheck:
name: Check that code passes a basic fuzzing build
needs: [ workflowcheck, stylecheck, upstreamcheck ]
needs: [workflowcheck, stylecheck, upstreamcheck]
runs-on: ubuntu-latest
container: openquantumsafe/ci-ubuntu-latest:latest
env:
Expand Down
182 changes: 182 additions & 0 deletions cpp/sig_linking_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/*
* example_sig.cpp
*
* Minimal C++ example of using a post-quantum signature implemented in liboqs.
*
* SPDX-License-Identifier: MIT
*/

#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <memory>

#include <oqs/oqs.h>

constexpr size_t MESSAGE_LEN = 50;

/* Cleaning up memory etc */
void cleanup_stack(uint8_t *secret_key, size_t secret_key_len);

struct OQSSecureDeleter {
size_t length;

explicit OQSSecureDeleter(size_t len) : length(len) {}

void operator()(uint8_t* ptr) const {
if (ptr) {
OQS_MEM_secure_free(ptr, length);
}
}
};

struct OQSInsecureDeleter {
void operator()(uint8_t* ptr) {
if (ptr) {
OQS_MEM_insecure_free(ptr);
}
}
};

struct OQSSigDeleter {
void operator()(OQS_SIG* sig) {
if (sig) {
OQS_SIG_free(sig);
}
}
};


/* This function gives an example of the signing operations
* using only compile-time macros and allocating variables
* statically on the stack, calling a specific algorithm's functions
* directly.
*
* The macros OQS_SIG_dilithium_2_length_* and the functions OQS_SIG_dilithium_2_*
* are only defined if the algorithm dilithium_2 was enabled at compile-time
* which must be checked using the OQS_ENABLE_SIG_dilithium_2 macro.
*
* <oqs/oqsconfig.h>, which is included in <oqs/oqs.h>, contains macros
* indicating which algorithms were enabled when this instance of liboqs
* was compiled.
*/
static OQS_STATUS example_stack(void) {

#ifdef OQS_ENABLE_SIG_dilithium_2

OQS_STATUS rc;

uint8_t public_key[OQS_SIG_dilithium_2_length_public_key];
uint8_t secret_key[OQS_SIG_dilithium_2_length_secret_key];
uint8_t message[MESSAGE_LEN];
uint8_t signature[OQS_SIG_dilithium_2_length_signature];
size_t message_len = MESSAGE_LEN;
size_t signature_len;

// let's create a random test message to sign
OQS_randombytes(message, message_len);

rc = OQS_SIG_dilithium_2_keypair(public_key, secret_key);
if (rc != OQS_SUCCESS) {
std::cerr << "ERROR: OQS_SIG_dilithium_2_keypair failed!" << std::endl;
cleanup_stack(secret_key, OQS_SIG_dilithium_2_length_secret_key);
return OQS_ERROR;
}
rc = OQS_SIG_dilithium_2_sign(signature, &signature_len, message, message_len, secret_key);
if (rc != OQS_SUCCESS) {
std::cerr << "ERROR: OQS_SIG_dilithium_2_sign failed!" << std::endl;
cleanup_stack(secret_key, OQS_SIG_dilithium_2_length_secret_key);
return OQS_ERROR;
}
rc = OQS_SIG_dilithium_2_verify(message, message_len, signature, signature_len, public_key);
if (rc != OQS_SUCCESS) {
std::cerr << "ERROR: OQS_SIG_dilithium_2_verify failed!" << std::endl;
cleanup_stack(secret_key, OQS_SIG_dilithium_2_length_secret_key);
return OQS_ERROR;
}

std::cout << "[example_stack] OQS_SIG_dilithium_2 operations completed" << std::endl;
cleanup_stack(secret_key, OQS_SIG_dilithium_2_length_secret_key);
return OQS_SUCCESS; // success!

#else

std::cout << "[example_stack] OQS_SIG_dilithium_2 was not enabled at compile-time" << std::endl;
return OQS_SUCCESS;

#endif
}

/* This function gives an example of the signing operations,
* allocating variables dynamically on the heap and calling the generic
* OQS_SIG object.
*
* This does not require the use of compile-time macros to check if the
* algorithm in question was enabled at compile-time; instead, the caller
* must check that the OQS_SIG object returned is not nullptr.
*/
static OQS_STATUS example_heap(void) {

#ifdef OQS_ENABLE_SIG_dilithium_2

size_t message_len = MESSAGE_LEN;
size_t signature_len;
OQS_STATUS rc;

std::unique_ptr<OQS_SIG, OQSSigDeleter> sig(OQS_SIG_new((OQS_SIG_alg_dilithium_2)));
if (sig == nullptr) {
throw std::runtime_error("[example_heap] OQS_SIG_alg_dilithium_2 was not enabled at compile-time.");
}
std::unique_ptr<uint8_t[], OQSInsecureDeleter> public_key(static_cast<uint8_t*>(malloc(sig->length_public_key)));
std::unique_ptr<uint8_t[], OQSSecureDeleter> secret_key(static_cast<uint8_t*>(malloc(sig->length_secret_key)), OQSSecureDeleter(sig->length_secret_key));
std::unique_ptr<uint8_t[], OQSInsecureDeleter> message(static_cast<uint8_t*>(malloc(message_len)));
std::unique_ptr<uint8_t[], OQSInsecureDeleter> signature(static_cast<uint8_t*>(malloc(sig->length_signature)));
if ((public_key == nullptr) || (secret_key == nullptr) || (message == nullptr) || (signature == nullptr)) {
throw std::runtime_error("ERROR: malloc failed!");
}

// let's create a random test message to sign
OQS_randombytes(message.get(), message_len);

rc = OQS_SIG_keypair(sig.get(), public_key.get(), secret_key.get());
if (rc != OQS_SUCCESS) {
throw std::runtime_error("ERROR: OQS_SIG_keypair failed!");
}
rc = OQS_SIG_sign(sig.get(), signature.get(), &signature_len, message.get(), message_len, secret_key.get());
if (rc != OQS_SUCCESS) {
throw std::runtime_error("ERROR: OQS_SIG_sign failed!");
}
rc = OQS_SIG_verify(sig.get(), message.get(), message_len, signature.get(), signature_len, public_key.get());
if (rc != OQS_SUCCESS) {
throw std::runtime_error("ERROR: OQS_SIG_verify failed!");
}

std::cout << "[example_heap] OQS_SIG_dilithium_2 operations completed." << std::endl;
return OQS_SUCCESS; // success
#else

std::cout << "[example_heap] OQS_SIG_dilithium_2 was not enabled at compile-time." << std::endl;
return OQS_SUCCESS;

#endif
}

int main() {
OQS_init();
try {
example_stack();
example_heap();
}
catch (std::exception e) {
std::cerr << e.what() << std::endl;
OQS_destroy();
return EXIT_FAILURE;
}
OQS_destroy();
return EXIT_SUCCESS;
}

void cleanup_stack(uint8_t *secret_key, size_t secret_key_len) {
OQS_MEM_cleanse(secret_key, secret_key_len);
}

0 comments on commit 05257da

Please sign in to comment.