-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #44 from stakwork/crypter-ffi
crypter ffi bindings for kotlin
- Loading branch information
Showing
10 changed files
with
645 additions
and
5 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 |
---|---|---|
|
@@ -6,4 +6,4 @@ Cargo.lock | |
sphinx-key/Cargo.lock | ||
notes.md | ||
test-flash | ||
.env | ||
.env |
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 |
---|---|---|
|
@@ -10,7 +10,8 @@ members = [ | |
|
||
exclude = [ | ||
"sphinx-key", | ||
"crypter" | ||
"crypter", | ||
"crypter-ffi" | ||
] | ||
|
||
[patch.crates-io] | ||
|
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,25 @@ | ||
[package] | ||
name = "crypter-ffi" | ||
version = "0.1.0" | ||
authors = ["Evan Feenstra <[email protected]>"] | ||
edition = "2018" | ||
|
||
[lib] | ||
crate-type = ["cdylib"] | ||
name = "crypter" | ||
|
||
[dependencies] | ||
sphinx-key-crypter = { path = "../crypter" } | ||
uniffi = "0.19.2" | ||
hex = "0.4.3" | ||
thiserror = "1.0.31" | ||
uniffi_macros = "0.11.0" | ||
|
||
[build-dependencies] | ||
uniffi_build = "0.19.2" | ||
|
||
[patch.crates-io] | ||
getrandom = { version = "0.2", git = "https://github.com/esp-rs-compat/getrandom.git" } | ||
secp256k1 = { git = "https://github.com/Evanfeenstra/rust-secp256k1", branch = "v0.22.0-new-rand" } | ||
lightning = { git = "https://github.com/Evanfeenstra/rust-lightning", branch = "v0.0.108-branch" } | ||
|
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,3 @@ | ||
fn main() { | ||
uniffi_build::generate_scaffolding("./src/crypter.udl").unwrap(); | ||
} |
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,10 @@ | ||
uniffi-bindgen --version | ||
should match the uniffi version in Cargo.toml | ||
|
||
uniffi-bindgen generate src/crypter.udl --language kotlin | ||
|
||
uniffi-bindgen generate src/crypter.udl --language swift | ||
|
||
### manually build the C ffi | ||
|
||
uniffi-bindgen scaffolding src/crypter.udl |
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,19 @@ | ||
[Error] | ||
enum CrypterError { | ||
"DeriveSharedSecret", | ||
"Encrypt", | ||
"Decrypt", | ||
"BadPubkey", | ||
"BadSecret", | ||
"BadNonce", | ||
"BadCiper", | ||
}; | ||
|
||
namespace crypter { | ||
[Throws=CrypterError] | ||
string derive_shared_secret(string their_pubkey, string my_secret_key); | ||
[Throws=CrypterError] | ||
string encrypt(string plaintext, string secret, string nonce); | ||
[Throws=CrypterError] | ||
string decrypt(string ciphertext, string secret); | ||
}; |
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,98 @@ | ||
mod parse; | ||
|
||
use sphinx_key_crypter::chacha::{decrypt as chacha_decrypt, encrypt as chacha_encrypt}; | ||
use sphinx_key_crypter::ecdh::derive_shared_secret_from_slice; | ||
|
||
uniffi_macros::include_scaffolding!("crypter"); | ||
|
||
pub type Result<T> = std::result::Result<T, CrypterError>; | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum CrypterError { | ||
#[error("Failed to derive shared secret")] | ||
DeriveSharedSecret, | ||
#[error("Failed to encrypt")] | ||
Encrypt, | ||
#[error("Failed to decrypt")] | ||
Decrypt, | ||
#[error("Bad pubkey")] | ||
BadPubkey, | ||
#[error("Bad secret")] | ||
BadSecret, | ||
#[error("Bad nonce")] | ||
BadNonce, | ||
#[error("Bad cipher")] | ||
BadCiper, | ||
} | ||
|
||
// their_pubkey: 33 bytes | ||
// my_secret_key: 32 bytes | ||
// return shared secret: 32 bytes | ||
pub fn derive_shared_secret(their_pubkey: String, my_secret_key: String) -> Result<String> { | ||
let pubkey = parse::parse_public_key_string(their_pubkey)?; | ||
let secret_key = parse::parse_secret_string(my_secret_key)?; | ||
let secret = match derive_shared_secret_from_slice(pubkey, secret_key) { | ||
Ok(s) => s, | ||
Err(_) => return Err(CrypterError::DeriveSharedSecret), | ||
}; | ||
Ok(hex::encode(secret)) | ||
} | ||
|
||
// plaintext: 32 bytes | ||
// secret: 32 bytes | ||
// nonce: 8 bytes | ||
// return ciphertext: 56 bytes | ||
pub fn encrypt(plaintext: String, secret: String, nonce: String) -> Result<String> { | ||
let plain = parse::parse_secret_string(plaintext)?; | ||
let sec = parse::parse_secret_string(secret)?; | ||
let non = parse::parse_nonce_string(nonce)?; | ||
let cipher = match chacha_encrypt(plain, sec, non) { | ||
Ok(c) => c, | ||
Err(_) => return Err(CrypterError::Encrypt), | ||
}; | ||
Ok(hex::encode(cipher)) | ||
} | ||
|
||
// ciphertext: 56 bytes | ||
// secret: 32 bytes | ||
// return plaintext: 32 bytes | ||
pub fn decrypt(ciphertext: String, secret: String) -> Result<String> { | ||
let cipher = parse::parse_cipher_string(ciphertext)?; | ||
let sec = parse::parse_secret_string(secret)?; | ||
let plain = match chacha_decrypt(cipher, sec) { | ||
Ok(c) => c, | ||
Err(_) => return Err(CrypterError::Decrypt), | ||
}; | ||
Ok(hex::encode(plain)) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::{decrypt, derive_shared_secret, encrypt, Result}; | ||
|
||
#[test] | ||
fn test_crypter() -> Result<()> { | ||
let sk1 = "86c8977989592a97beb409bc27fde76e981ce3543499fd61743755b832e92a3e"; | ||
let pk1 = "0362a684901b8d065fb034bc44ea972619a409aeafc2a698016a74f6eee1008aca"; | ||
|
||
let sk2 = "21c2d41c7394b0a87dae89576bee2552aedb54a204cdcdbf5cdceb0b4c1c2a17"; | ||
let pk2 = "027dd6297aff570a409fe05032b6e1dab39f309daa8c438a65c32e3d7b4722b7c3"; | ||
|
||
// derive shared secrets | ||
let sec1 = derive_shared_secret(pk2.to_string(), sk1.to_string())?; | ||
let sec2 = derive_shared_secret(pk1.to_string(), sk2.to_string())?; | ||
assert_eq!(sec1, sec2); | ||
|
||
// encrypt plaintext with sec1 | ||
let plaintext = "59ff446bec1d96dc7d1a69232cd69ca409e069294e983df7f1e3e5fb3c95c41c"; | ||
let nonce = "0da01cc0c0a73ad3"; | ||
let cipher = encrypt(plaintext.to_string(), sec1, nonce.to_string())?; | ||
|
||
// decrypt with sec2 | ||
let plain = decrypt(cipher, sec2)?; | ||
assert_eq!(plaintext, plain); | ||
|
||
println!("PLAINTEXT MATCHES!"); | ||
Ok(()) | ||
} | ||
} |
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,65 @@ | ||
use crate::{Result, CrypterError}; | ||
|
||
use sphinx_key_crypter::ecdh::PUBLIC_KEY_LEN; | ||
use sphinx_key_crypter::chacha::{NONCE_END_LEN, KEY_LEN, CIPHER_LEN}; | ||
use std::convert::TryInto; | ||
|
||
pub(crate) fn parse_secret_string(sk: String) -> Result<[u8; KEY_LEN]> { | ||
if sk.len() != KEY_LEN * 2 { | ||
return Err(CrypterError::BadSecret) | ||
} | ||
let secret_key_bytes: Vec<u8> = match hex::decode(sk) { | ||
Ok(sk) => sk, | ||
Err(_) => return Err(CrypterError::BadSecret), | ||
}; | ||
let secret_key: [u8; KEY_LEN] = match secret_key_bytes.try_into() { | ||
Ok(sk) => sk, | ||
Err(_) => return Err(CrypterError::BadSecret), | ||
}; | ||
Ok(secret_key) | ||
} | ||
|
||
pub(crate) fn parse_public_key_string(pk: String) -> Result<[u8; PUBLIC_KEY_LEN]> { | ||
if pk.len() != PUBLIC_KEY_LEN * 2 { | ||
return Err(CrypterError::BadPubkey) | ||
} | ||
let pubkey_bytes: Vec<u8> = match hex::decode(pk) { | ||
Ok(pk) => pk, | ||
Err(_) => return Err(CrypterError::BadPubkey), | ||
}; | ||
let pubkey: [u8; PUBLIC_KEY_LEN] = match pubkey_bytes.try_into() { | ||
Ok(pk) => pk, | ||
Err(_) => return Err(CrypterError::BadPubkey), | ||
}; | ||
Ok(pubkey) | ||
} | ||
|
||
pub(crate) fn parse_nonce_string(n: String) -> Result<[u8; NONCE_END_LEN]> { | ||
if n.len() != NONCE_END_LEN * 2 { | ||
return Err(CrypterError::BadNonce) | ||
} | ||
let nonce_bytes: Vec<u8> = match hex::decode(n) { | ||
Ok(n) => n, | ||
Err(_) => return Err(CrypterError::BadNonce), | ||
}; | ||
let nonce: [u8; NONCE_END_LEN] = match nonce_bytes.try_into() { | ||
Ok(n) => n, | ||
Err(_) => return Err(CrypterError::BadNonce), | ||
}; | ||
Ok(nonce) | ||
} | ||
|
||
pub(crate) fn parse_cipher_string(c: String) -> Result<[u8; CIPHER_LEN]> { | ||
if c.len() != CIPHER_LEN * 2 { | ||
return Err(CrypterError::BadCiper) | ||
} | ||
let cipher_bytes: Vec<u8> = match hex::decode(c) { | ||
Ok(n) => n, | ||
Err(_) => return Err(CrypterError::BadCiper), | ||
}; | ||
let cipher: [u8; CIPHER_LEN] = match cipher_bytes.try_into() { | ||
Ok(n) => n, | ||
Err(_) => return Err(CrypterError::BadCiper), | ||
}; | ||
Ok(cipher) | ||
} |
Oops, something went wrong.