Skip to content
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
47 changes: 16 additions & 31 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,22 @@ name: CI
on: [push, pull_request]

jobs:
# C_test:
# strategy:
# matrix:
# os: [ubuntu-latest]
#
# runs-on: ${{ matrix.os }}
# timeout-minutes: 30
#
# steps:
# - name: Checkout code
# uses: actions/checkout@v6.0.2
#
# - name: Install C dependencies (Ubuntu)
# if: matrix.os == 'ubuntu-latest'
# run: |
# sudo apt-get remove needrestart
# sudo apt-get update
# sudo apt-get install -y cmake
#
# - name: Build C code
# run: |
# mkdir -p build
# cd build
# cmake ..
# make
#
# - name: Run C tests
# run: |
# cd build
# ctest

C_test:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.2
- name: Install C dependencies
run: |
sudo apt-get update
sudo apt-get install -y cmake libssl-dev build-essential
- name: Build C code
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
- name: Compile C code
run: cmake --build build --parallel
- name: Run C tests
run: ctest --test-dir build --output-on-failure
Lint:
runs-on: ubuntu-latest
timeout-minutes: 5
Expand Down
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,23 @@ src/setup.json
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#cmake-build-*

# ss_crypto build and test artifacts
/build/
crypto/build/
crypto/tests/*.log
crypto/tests/*.out
crypto/tests/*.err
crypto/tests/*.tmp
crypto/tests/*.key
crypto/tests/*.pem
*.o
*.d
*.so
*.a
*.dll
*.dylib
*.exe
*.core
*.pdb
*.map
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Top-level build for ServSpy. C sources live in the self-contained
# crypto/ module; the Python package is managed by uv/pytest instead.
cmake_minimum_required(VERSION 3.16)
project(servspy VERSION 0.1.0 LANGUAGES C)

include(CTest)
enable_testing()
add_subdirectory(crypto)
90 changes: 90 additions & 0 deletions crypto/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# ss_crypto - ServSpy C/OpenSSL cryptography module.
#
# Usable both standalone (cmake -S crypto -B build) and as a subdirectory
# of a larger project (add_subdirectory(crypto)).

if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
cmake_minimum_required(VERSION 3.16)
project(ss_crypto VERSION 1.0.0 LANGUAGES C)
endif()

include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
include(CTest) # defines BUILD_TESTING

enable_testing()

option(SS_CRYPTO_BUILD_TESTS "Build the ss_crypto test suite" ON)

find_package(OpenSSL REQUIRED)

set(SS_CRYPTO_SOURCES
src/ss_crypto.c
src/ss_rsa.c
src/ss_ecdh.c
)

add_library(ss_crypto ${SS_CRYPTO_SOURCES})
add_library(ss_crypto::ss_crypto ALIAS ss_crypto)

target_include_directories(ss_crypto PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
target_link_libraries(ss_crypto PUBLIC OpenSSL::Crypto)

set_target_properties(ss_crypto PROPERTIES
C_STANDARD 11
C_STANDARD_REQUIRED ON
C_EXTENSIONS OFF
C_VISIBILITY_PRESET hidden
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
POSITION_INDEPENDENT_CODE ON
)

if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(ss_crypto PRIVATE -Wall -Wextra)
endif()

# ---- Install & export -----------------------------------------------------
install(TARGETS ss_crypto
EXPORT ss_cryptoTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

install(EXPORT ss_cryptoTargets
FILE ss_cryptoTargets.cmake
NAMESPACE ss_crypto::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ss_crypto
)

configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/ss_cryptoConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/ss_cryptoConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ss_crypto
)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/ss_cryptoConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/ss_cryptoConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/ss_cryptoConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ss_crypto
)

# pkg-config file
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ss_crypto.pc.in
${CMAKE_CURRENT_BINARY_DIR}/ss_crypto.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ss_crypto.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)

# ---- Tests -----------------------------------------------------------------
if(SS_CRYPTO_BUILD_TESTS AND BUILD_TESTING)
add_subdirectory(tests)
endif()
5 changes: 5 additions & 0 deletions crypto/cmake/ss_cryptoConfig.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@PACKAGE_INIT@

include("${CMAKE_CURRENT_LIST_DIR}/ss_cryptoTargets.cmake")

check_required_components(ss_crypto)
95 changes: 95 additions & 0 deletions crypto/include/ss_crypto.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* ss_crypto.h - ServSpy crypto module, common public interface.
*
* A self-contained C/OpenSSL cryptography library providing:
* - RSA-OAEP key generation, PEM persistence, encrypt/decrypt
* - ECDH key agreement with HKDF-SHA256 session key derivation
* - AES-256-GCM authenticated encryption (seal/open) bound to an
* ECDH key pair, with implicit key confirmation of the peer
*
* Design rules:
* - Every function returns an ss_err_t; SS_OK (0) means success.
* - No global mutable state; the library is thread-safe as long as
* each handle is used by one thread at a time.
* - Output buffers supplied by the caller are never overrun; the
* "query length" pattern (out == NULL) returns the required size
* in *out_len.
* - Buffers returned via **out (PEM strings, seal/open results) are
* allocated with malloc(3) and MUST be released with free(3).
*
* Requires OpenSSL >= 1.1.1. Uses only the EVP high-level API.
*/
#ifndef SS_CRYPTO_H
#define SS_CRYPTO_H

#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

#define SS_CRYPTO_VERSION_MAJOR 1
#define SS_CRYPTO_VERSION_MINOR 0
#define SS_CRYPTO_VERSION_PATCH 0
#define SS_CRYPTO_VERSION_STRING "1.0.0"

/* DLL export/import for Windows shared builds; no-op elsewhere. */
#if defined(_WIN32)
# if defined(SS_CRYPTO_SHARED)
# define SS_CRYPTO_API __declspec(dllexport)
# elif defined(SS_CRYPTO_USE_SHARED)
# define SS_CRYPTO_API __declspec(dllimport)
# else
# define SS_CRYPTO_API
# endif
#else
# define SS_CRYPTO_API __attribute__((visibility("default")))
#endif

/* Maximum HKDF-SHA256 output: 255 * 32 bytes (RFC 5869). */
#define SS_CRYPTO_HKDF_SHA256_MAX_OUT 8160u

typedef enum {
SS_OK = 0, /* success */
SS_ERR_INVALID_ARG, /* NULL argument, illegal length, bad combination */
SS_ERR_NOMEM, /* memory allocation failed */
SS_ERR_OPENSSL, /* underlying OpenSSL operation failed; see
ss_crypto_openssl_errors() for detail */
SS_ERR_IO, /* file could not be opened/read/written */
SS_ERR_PARSE, /* PEM/DER input could not be parsed */
SS_ERR_BUFFER_TOO_SMALL, /* caller-supplied output buffer too small */
SS_ERR_AUTH_FAILED, /* AEAD tag verification failed */
SS_ERR_UNSUPPORTED, /* unsupported algorithm or parameter */
SS_ERR_DECRYPT, /* decryption failed for a non-authentication
reason (e.g. malformed RSA padding) */
} ss_err_t;

/* Human-readable description of an error code. Never returns NULL. */
SS_CRYPTO_API const char *ss_err_string(ss_err_t err);

/*
* Formats the pending OpenSSL error queue into buf (always NUL-terminated,
* truncated to buf_len). Each call consumes the queue; "no error" is written
* when the queue is empty. buf may be NULL with buf_len 0 to no-op.
*/
SS_CRYPTO_API void ss_crypto_openssl_errors(char *buf, size_t buf_len);

/*
* HKDF (RFC 5869) with SHA-256: Extract-and-Expand.
* ikm - input key material (for ECDH: the raw shared secret)
* salt - optional salt; may be NULL/0 (HKDF zero-pads per RFC)
* info - optional context binding; may be NULL/0
* out - output buffer of out_len bytes; out_len <= SS_CRYPTO_HKDF_SHA256_MAX_OUT
*/
SS_CRYPTO_API ss_err_t ss_crypto_hkdf_sha256(
const uint8_t *ikm, size_t ikm_len,
const uint8_t *salt, size_t salt_len,
const uint8_t *info, size_t info_len,
uint8_t *out, size_t out_len);

#ifdef __cplusplus
}
#endif

#endif /* SS_CRYPTO_H */
Loading
Loading