Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add C++ linking test #1971

Merged
merged 1 commit into from
Nov 1, 2024
Merged
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
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);
}
Loading