-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This PR adds a very barebones FFI support to the crate. * All memory managed by caller! * No types exported across the FFI boundary! A demo program and Makefile is presented to see how it all fits together.
- Loading branch information
Showing
11 changed files
with
395 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,3 +26,8 @@ report | |
Cargo.lock | ||
*.log | ||
|
||
# Generated during build | ||
/libtari/tari_crypto.h | ||
|
||
# C-demo output | ||
bin/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
ifeq ($(shell uname),Darwin) | ||
LDFLAGS := -Ltarget/release/ | ||
else | ||
LDFLAGS := -Ltarget/release/ | ||
endif | ||
|
||
SRC = libtari | ||
BIN = bin | ||
PWD = $(shell pwd) | ||
|
||
CC=cc | ||
|
||
CFLAGS = | ||
|
||
clean: | ||
rm $(SRC)/tari_crypto.h | ||
rm $(BIN)/demo | ||
|
||
$(LIB)/tari_crypto.h target/release/libtari_crypto.a: | ||
cargo build --features=ffi --release | ||
|
||
target/debug/libtari_crypto.a: | ||
cargo build --features=ffi | ||
|
||
$(BIN)/demo: $(LIB)/tari_crypto.h target/release/libtari_crypto.a | ||
mkdir -p $(BIN) | ||
$(CC) $(SRC)/demo.c $(LDFLAGS) -ltari_crypto -o $@ | ||
|
||
demo: $(BIN)/demo | ||
|
||
ffi: target/debug/libtari_crypto.a | ||
|
||
ffi-release: target/release/libtari_crypto.a |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Copyright 2020. The Tari Project | ||
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the | ||
// following conditions are met: | ||
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following | ||
// disclaimer. | ||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the | ||
// following disclaimer in the documentation and/or other materials provided with the distribution. | ||
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote | ||
// products derived from this software without specific prior written permission. | ||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE | ||
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
|
||
use cbindgen::Config; | ||
use std::{env, path::Path}; | ||
|
||
fn main() { | ||
let needs_ffi = env::var("CARGO_FEATURE_FFI").is_ok(); | ||
if needs_ffi { | ||
generate_ffi_header(); | ||
} | ||
generate_ffi_header(); | ||
} | ||
|
||
fn generate_ffi_header() { | ||
let crate_env = env::var("CARGO_MANIFEST_DIR").unwrap(); | ||
let crate_path = Path::new(&crate_env); | ||
let config = Config::from_root_or_default(crate_path); | ||
cbindgen::Builder::new() | ||
.with_crate(crate_path.to_str().unwrap()) | ||
.with_config(config) | ||
.generate() | ||
.expect("Unable to generate bindings") | ||
.write_to_file("libtari/tari_crypto.h"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
language = "C" | ||
include_version = true | ||
|
||
[parse] | ||
parse_deps = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
#include "tari_crypto.h" | ||
#include <stdio.h> | ||
|
||
void print_key(uint8_t key[]) { | ||
int i; | ||
for (i = 0; i < KEY_LENGTH; i++) { | ||
printf("%02X", key[i]); | ||
} | ||
printf("\n"); | ||
} | ||
|
||
/* | ||
* This demo generates a key pair, signs a message and then validates the signature. | ||
* All memory in this FFI is managed by the caller. In this demo, the data is kept on the stack, and so explicit | ||
* memory management is not done, but in general, you have to allocate and free memory yourself. | ||
*/ | ||
int main() { | ||
const char *ver = version(); | ||
printf("Tari Crypto (v%s)\n", ver); | ||
|
||
uint8_t pub_key[KEY_LENGTH], priv_key[KEY_LENGTH]; | ||
|
||
int code = random_keypair(&priv_key, &pub_key); | ||
if (code) { | ||
printf("Error code: %d\n", code); | ||
return code; | ||
} | ||
printf("Keys generated\n"); | ||
print_key(priv_key); | ||
print_key(pub_key); | ||
|
||
// Sign and verify message | ||
const char msg[] = "Hello world\0"; | ||
const char invalid[] = "Hullo world\0"; | ||
|
||
uint8_t r[KEY_LENGTH], sig[KEY_LENGTH]; | ||
|
||
code = sign(&priv_key, &msg[0], &r, &sig); | ||
if (code) { | ||
printf("Error code: %d\n", code); | ||
return code; | ||
} | ||
|
||
// Demonstrate error handling | ||
char *err_msg = malloc( sizeof(char) * ( 128 + 1 ) ); | ||
lookup_error_message(-1, &err_msg[0], 128); | ||
printf("The error message for code -1 is \"%s\"\n", err_msg); | ||
|
||
printf("Signed message\n"); | ||
print_key(r); | ||
print_key(sig); | ||
|
||
printf("Check (invalid) signature.."); | ||
if (verify(&pub_key, &invalid[0], &r, &sig, &code)) { | ||
printf("Oh no. This should have failed\n"); | ||
} else { | ||
printf("The signature is invalid, as expected\n"); | ||
} | ||
|
||
printf("Check signature.."); | ||
if (verify(&pub_key, &msg[0], &r, &sig, &code)) { | ||
printf("SUCCESS\n"); | ||
} else { | ||
printf("FAILED\n"); | ||
} | ||
return code; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// Copyright 2020. The Tari Project | ||
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the | ||
// following conditions are met: | ||
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following | ||
// disclaimer. | ||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the | ||
// following disclaimer in the documentation and/or other materials provided with the distribution. | ||
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote | ||
// products derived from this software without specific prior written permission. | ||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE | ||
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
|
||
use std::{ | ||
os::raw::{c_char, c_int}, | ||
ptr, | ||
slice, | ||
}; | ||
|
||
/// Looks up the error message associated with the given error code. | ||
/// | ||
/// This function returns 0 on successful execution, or an error code on a failure. | ||
#[no_mangle] | ||
pub unsafe extern "C" fn lookup_error_message(code: c_int, buffer: *mut c_char, length: c_int) -> c_int { | ||
if buffer.is_null() { | ||
return NULL_POINTER; | ||
} | ||
|
||
let error_message = get_error_message(code).to_string(); | ||
let buffer = slice::from_raw_parts_mut(buffer as *mut u8, length as usize); | ||
|
||
if error_message.len() >= buffer.len() { | ||
return BUFFER_TOO_SMALL; | ||
} | ||
|
||
ptr::copy_nonoverlapping(error_message.as_ptr(), buffer.as_mut_ptr(), error_message.len()); | ||
|
||
// Add a trailing null so people using the string as a `char *` don't | ||
// accidentally read into garbage. | ||
buffer[error_message.len()] = 0; | ||
|
||
error_message.len() as c_int | ||
} | ||
|
||
pub const OK: i32 = 0; | ||
pub const NULL_POINTER: i32 = -1; | ||
pub const BUFFER_TOO_SMALL: i32 = -2; | ||
pub const INVALID_SECRET_KEY_SER: i32 = -1000; | ||
pub const SIGNING_ERROR: i32 = -1100; | ||
pub const STR_CONV_ERR: i32 = -2000; | ||
|
||
pub fn get_error_message(code: i32) -> &'static str { | ||
match code { | ||
OK => "The operation completed without errors.", | ||
NULL_POINTER => "A null pointer was passed as an input pointer", | ||
BUFFER_TOO_SMALL => "The provided buffer was too small", | ||
INVALID_SECRET_KEY_SER => "Invalid secret key representation.", | ||
SIGNING_ERROR => "Error creating signature", | ||
STR_CONV_ERR => "String conversion error", | ||
_ => "Unknown error code.", | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// Copyright 2020. The Tari Project | ||
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the | ||
// following conditions are met: | ||
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following | ||
// disclaimer. | ||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the | ||
// following disclaimer in the documentation and/or other materials provided with the distribution. | ||
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote | ||
// products derived from this software without specific prior written permission. | ||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE | ||
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
|
||
use crate::{ | ||
ffi::error::{INVALID_SECRET_KEY_SER, NULL_POINTER, OK, SIGNING_ERROR, STR_CONV_ERR}, | ||
hash::blake2::Blake256, | ||
keys::{PublicKey, SecretKey}, | ||
ristretto::{RistrettoPublicKey, RistrettoSchnorr, RistrettoSecretKey}, | ||
}; | ||
use digest::Digest; | ||
use libc::c_char; | ||
use rand::rngs::OsRng; | ||
use std::{ffi::CStr, os::raw::c_int}; | ||
use tari_utilities::ByteArray; | ||
|
||
const KEY_LENGTH: usize = 32; | ||
|
||
type KeyArray = [u8; KEY_LENGTH]; | ||
|
||
/// Generate a new key pair and copies the values into the provided arrays. | ||
/// | ||
/// If `pub_key` is null, then only a private key is generated. | ||
/// The *caller* must manage memory for the results. Besides checking for null values, this function assumes that at | ||
/// least `KEY_LENGTH` bytes have been allocated in `priv_key` and `pub_key`. | ||
#[no_mangle] | ||
pub unsafe extern "C" fn random_keypair(priv_key: *mut KeyArray, pub_key: *mut KeyArray) -> c_int { | ||
if priv_key.is_null() && pub_key.is_null() { | ||
return NULL_POINTER; | ||
} | ||
if pub_key.is_null() { | ||
let k = RistrettoSecretKey::random(&mut OsRng); | ||
(*priv_key).copy_from_slice(k.as_bytes()); | ||
} else { | ||
let (k, p) = RistrettoPublicKey::random_keypair(&mut OsRng); | ||
(*priv_key).copy_from_slice(k.as_bytes()); | ||
(*pub_key).copy_from_slice(p.as_bytes()); | ||
} | ||
OK | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn sign( | ||
priv_key: *const KeyArray, | ||
msg: *const c_char, | ||
nonce: *mut KeyArray, | ||
signature: *mut KeyArray, | ||
) -> c_int | ||
{ | ||
if nonce.is_null() || signature.is_null() || priv_key.is_null() || msg.is_null() { | ||
return NULL_POINTER; | ||
} | ||
let k = match RistrettoSecretKey::from_bytes(&(*priv_key)) { | ||
Ok(k) => k, | ||
_ => return INVALID_SECRET_KEY_SER, | ||
}; | ||
let r = RistrettoSecretKey::random(&mut OsRng); | ||
let msg = match CStr::from_ptr(msg).to_str() { | ||
Ok(s) => s, | ||
_ => return STR_CONV_ERR, | ||
}; | ||
let challenge = Blake256::digest(msg.as_bytes()).to_vec(); | ||
let sig = match RistrettoSchnorr::sign(k, r, &challenge) { | ||
Ok(sig) => sig, | ||
_ => return SIGNING_ERROR, | ||
}; | ||
(*nonce).copy_from_slice(sig.get_public_nonce().as_bytes()); | ||
(*signature).copy_from_slice(sig.get_signature().as_bytes()); | ||
OK | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn verify( | ||
pub_key: *const KeyArray, | ||
msg: *const c_char, | ||
pub_nonce: *mut KeyArray, | ||
signature: *mut KeyArray, | ||
err_code: *mut c_int, | ||
) -> bool | ||
{ | ||
if pub_key.is_null() || msg.is_null() || pub_nonce.is_null() || signature.is_null() { | ||
*err_code = NULL_POINTER; | ||
return false; | ||
} | ||
let pk = match RistrettoPublicKey::from_bytes(&(*pub_key)) { | ||
Ok(k) => k, | ||
_ => { | ||
*err_code = INVALID_SECRET_KEY_SER; | ||
return false; | ||
}, | ||
}; | ||
let r_pub = match RistrettoPublicKey::from_bytes(&(*pub_nonce)) { | ||
Ok(r) => r, | ||
_ => return false, | ||
}; | ||
let sig = match RistrettoSecretKey::from_bytes(&(*signature)) { | ||
Ok(s) => s, | ||
_ => return false, | ||
}; | ||
let msg = match CStr::from_ptr(msg).to_str() { | ||
Ok(s) => s, | ||
_ => return false, | ||
}; | ||
let sig = RistrettoSchnorr::new(r_pub, sig); | ||
let challenge = Blake256::digest(msg.as_bytes()); | ||
let challenge = match RistrettoSecretKey::from_bytes(challenge.as_slice()) { | ||
Ok(e) => e, | ||
_ => return false, | ||
}; | ||
sig.verify(&pk, &challenge) | ||
} |
Oops, something went wrong.