This commit is contained in:
eschorn1 2024-03-14 13:35:26 -05:00
parent 20a8263d42
commit 8e4ebfec1a
8 changed files with 255 additions and 2 deletions

7
.gitignore vendored
View file

@ -1,8 +1,11 @@
/target
/Cargo.lock
**/target
/.idea
**/artifacts
**/corpus
**/target
/fuzz/.gitignore
**/Cargo.lock
**/package-lock.json
**/node_modules
**/pkg
**/dist

View file

@ -1,3 +1,5 @@
workspace = { members = ['ffi'], exclude = ["dudect", "ct_cm4"] }
[package]
name = "fips205"
version = "0.1.2"

21
ffi/Cargo.toml Normal file
View file

@ -0,0 +1,21 @@
[package]
name = "fips205-ffi"
version = "0.1.2"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "C shared library exposing FIPS 205 (draft): Stateless Hash-Based Digital Signature Standard"
authors = ["Eric Schorn <eschorn@integritychain.com>"]
documentation = "https://docs.rs/fips205"
categories = ["cryptography", "no-std"]
repository = "https://github.com/integritychain/fips205"
keywords = ["FIPS", "FIPS205", "hash", "signature"]
rust-version = "1.70"
[lib]
crate-type = ["staticlib", "cdylib"]
bench = false
name = "fips205"
[dependencies.fips205]
path = ".."
version = "0.1.2"

12
ffi/README.md Normal file
View file

@ -0,0 +1,12 @@
FIPS 205 FFI
Thank you to Daniel Kahn Gillmor <dkg@fifthhorseman.net> for the FIPS 203 example.
Currently only implemented for `slh_dsa_sha2_128f` until A) more testing has been developed, B) better integration with build flow.
~~~
$ cd ffi # here
$ cargo build
$ cd tests
$ make
~~~

50
ffi/fips205.h Normal file
View file

@ -0,0 +1,50 @@
#ifndef FIPS205_FIPS205_H
#define FIPS205_FIPS205_H
#include <stdint.h>
typedef uint8_t slh_dsa_err;
const slh_dsa_err SLH_DSA_OK = 0;
const slh_dsa_err SLH_DSA_NULL_PTR_ERROR = 1;
const slh_dsa_err SLH_DSA_SERIALIZATION_ERROR = 2;
const slh_dsa_err SLH_DSA_DESERIALIZATION_ERROR = 3;
const slh_dsa_err SLH_DSA_KEYGEN_ERROR = 4;
const slh_dsa_err SLH_DSA_SIGN_ERROR = 5;
const slh_dsa_err SLH_DSA_VERIFY_ERROR = 6;
typedef struct slh_dsa_sha2_128f_public_key {
uint8_t data[32];
} slh_dsa_sha2_128f_public_key;
typedef struct slh_dsa_sha2_128f_private_key {
uint8_t data[64];
} slh_dsa_sha2_128f_private_key;
typedef struct slh_dsa_sha2_128f_signature {
uint8_t data[17088];
} slh_dsa_sha2_128f_signature;
#ifdef __cplusplus
extern "C" {
#endif
slh_dsa_err slh_dsa_sha2_128f_keygen(slh_dsa_sha2_128f_public_key *public_out,
slh_dsa_sha2_128f_private_key *private_out);
slh_dsa_err slh_dsa_sha2_128f_sign(const uint8_t *message_buf,
int message_length,
const slh_dsa_sha2_128f_private_key *private,
slh_dsa_sha2_128f_signature *signature_out);
slh_dsa_err slh_dsa_sha2_128f_verify(const uint8_t *message_buf,
int message_length,
const slh_dsa_sha2_128f_public_key *public,
const slh_dsa_sha2_128f_signature *signature_out);
#ifdef __cplusplus
} // extern "C"
#endif
#endif //FIPS205_FIPS205_H

104
ffi/src/lib.rs Normal file
View file

@ -0,0 +1,104 @@
use std::convert::TryInto;
use fips205;
use fips205::traits::{KeyGen, SerDes, Signer, Verifier};
use std::os::raw::c_int;
pub const SLH_DSA_OK: u8 = 0;
pub const SLH_DSA_NULL_PTR_ERROR: u8 = 1;
pub const SLH_DSA_SERIALIZATION_ERROR: u8 = 2;
pub const SLH_DSA_DESERIALIZATION_ERROR: u8 = 3;
pub const SLH_DSA_KEYGEN_ERROR: u8 = 4;
pub const SLH_DSA_SIGN_ERROR: u8 = 5;
pub const SLH_DSA_VERIFY_ERROR: u8 = 6;
#[repr(C)]
pub struct slh_dsa_message {
data: [u8],
}
// slh_dsa_sha2_128f
#[repr(C)]
pub struct slh_dsa_sha2_128f_private_key {
data: [u8; fips205::slh_dsa_sha2_128f::SK_LEN],
}
#[repr(C)]
pub struct slh_dsa_sha2_128f_public_key {
data: [u8; fips205::slh_dsa_sha2_128f::PK_LEN],
}
#[repr(C)]
pub struct slh_dsa_sha2_128f_signature {
data: [u8; fips205::slh_dsa_sha2_128f::SIG_LEN],
}
#[no_mangle]
pub extern "C" fn slh_dsa_sha2_128f_keygen(
public_out: Option<&mut slh_dsa_sha2_128f_public_key>,
private_out: Option<&mut slh_dsa_sha2_128f_private_key>,
) -> u8 {
//use fips205::traits::{KeyGen, SerDes};
let (Some(public_out), Some(private_out)) = (public_out, private_out) else {
return SLH_DSA_NULL_PTR_ERROR;
};
let Ok((pk, sk)) = fips205::slh_dsa_sha2_128f::KG::try_keygen_vt() else {
return SLH_DSA_KEYGEN_ERROR;
};
public_out.data = pk.into_bytes();
private_out.data = sk.into_bytes();
return SLH_DSA_OK;
}
#[no_mangle]
pub extern "C" fn slh_dsa_sha2_128f_sign(
message_buf: *const u8,
message_len: c_int,
private_key: Option<&mut slh_dsa_sha2_128f_private_key>,
signature_out: Option<&mut slh_dsa_sha2_128f_signature>,
) -> u8 {
let (Some(private_key), Some(signature_out)) = (private_key, signature_out) else {
return SLH_DSA_NULL_PTR_ERROR;
};
if message_buf.is_null() {return SLH_DSA_NULL_PTR_ERROR};
let message = unsafe { std::slice::from_raw_parts(message_buf, message_len.try_into().unwrap()) };
let Ok(sk) = fips205::slh_dsa_sha2_128f::PrivateKey::try_from_bytes(&private_key.data) else {
return SLH_DSA_DESERIALIZATION_ERROR;
};
let Ok(sig) = sk.try_sign_ct(&message, true) else {
return SLH_DSA_SIGN_ERROR;
};
signature_out.data = sig;
return SLH_DSA_OK;
}
#[no_mangle]
pub extern "C" fn slh_dsa_sha2_128f_verify(
message_buf: *const u8,
message_len: c_int,
public_key: Option<&mut slh_dsa_sha2_128f_public_key>,
signature: Option<&mut slh_dsa_sha2_128f_signature>,
) -> u8 {
let (Some(public_key), Some(signature)) = (public_key, signature) else {
return SLH_DSA_NULL_PTR_ERROR;
};
let message = unsafe { std::slice::from_raw_parts(message_buf, message_len.try_into().unwrap()) };
let Ok(sk) = fips205::slh_dsa_sha2_128f::PublicKey::try_from_bytes(&public_key.data) else {
return SLH_DSA_DESERIALIZATION_ERROR;
};
let res = sk.try_verify_vt(&message, &signature.data);
if res.is_ok() && res.unwrap() { SLH_DSA_OK } else { SLH_DSA_VERIFY_ERROR }
}

19
ffi/tests/Makefile Normal file
View file

@ -0,0 +1,19 @@
SO_LOCATION = ../../target/debug
FEATURES = slh_dsa_sha2_128f
FRAMES = public_key private_key signature sign verify keygen
BASELINES=$(foreach sz, $(FEATURES), baseline-$(sz))
CHECKS=$(foreach sz, $(FEATURES), runtest-$(sz))
check: $(CHECKS)
runtest-%: baseline-%
LD_LIBRARY_PATH=$(SO_LOCATION) ./$<
baseline-%: baseline.c ../fips205.h
$(CC) -o $@ -g -D SLHDSA=$* $(foreach v, $(FRAMES),-D SLHDSA_$(v)=$*_$(v)) -Werror -Wall -pedantic -L $(SO_LOCATION) $< -Wall -lfips205
clean:
rm -f $(BASELINES)
.PHONY: clean check

42
ffi/tests/baseline.c Normal file
View file

@ -0,0 +1,42 @@
#include <stdio.h>
#include "../fips205.h"
int main(int argc, const char **argv) {
SLHDSA_public_key public;
SLHDSA_private_key private;
SLHDSA_signature sig;
uint8_t message[] = { 0x00, 0x11, 0x22 };
// Happy path
if (SLHDSA_keygen(&public, &private)) return 1;
if (SLHDSA_sign(message, sizeof message, &private, &sig)) return 1;
if (SLHDSA_verify(message, sizeof message, &public, &sig)) return 1;
// Verify should fail now
message[0] = 99;
if (SLHDSA_verify(message, sizeof message, &public, &sig) != SLH_DSA_VERIFY_ERROR) return 1;
// Null parameters
if (SLHDSA_keygen(&public, NULL) != SLH_DSA_NULL_PTR_ERROR) {
fprintf (stderr, "keygen should have failed with NULL private key\n");
return 1;
}
if (SLHDSA_keygen(NULL, &private) != SLH_DSA_NULL_PTR_ERROR) {
fprintf (stderr, "keygen should have failed with NULL public key\n");
return 1;
}
if (SLHDSA_sign(NULL, sizeof message, &private, &sig) != SLH_DSA_NULL_PTR_ERROR) {
fprintf (stderr, "sign should have failed with NULL message\n");
return 1;
}
if (SLHDSA_sign(message, sizeof message, NULL, &sig) != SLH_DSA_NULL_PTR_ERROR) {
fprintf (stderr, "sign should have failed with NULL private key\n");
return 1;
}
if (SLHDSA_sign(message, sizeof message, &private, NULL) != SLH_DSA_NULL_PTR_ERROR) {
fprintf (stderr, "sign should have failed with NULL signature\n");
return 1;
}
}