Add wtp logic

This commit is contained in:
J. Ayo Akinyele 2019-08-26 11:08:49 -04:00
parent e36ad50857
commit 3c0f087ff6
6 changed files with 167 additions and 63 deletions

View File

@ -21,6 +21,7 @@ libc = "*"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_with = "1.0"
serde_bytes = "0.11.2"
time = "*"
secp256k1 = { version = "0.15.0", features = ["rand", "serde"] }
curve25519-dalek = { version = "1", features = ["serde"] }

View File

@ -63,19 +63,16 @@ class Libbolt(object):
self.lib.ffishim_bidirectional_customer_close.argtypes = (c_void_p, c_void_p)
self.lib.ffishim_bidirectional_customer_close.restype = c_void_p
self.lib.ffishim_bidirectional_merchant_close.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p)
self.lib.ffishim_bidirectional_merchant_close.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p, c_void_p)
self.lib.ffishim_bidirectional_merchant_close.restype = c_void_p
# ON-CHAIN BOLT LOGIC / WTPs
self.lib.ffishim_bidirectional_verify_open_channel.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p)
self.lib.ffishim_bidirectional_verify_open_channel.restype = c_void_p
self.lib.ffishim_bidirectional_wtp_verify_cust_close_message.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p)
self.lib.ffishim_bidirectional_wtp_verify_cust_close_message.restype = c_void_p
#self.lib.ffishim_bidirectional_verify_close_channel.argtypes = (c_void_p, c_void_p)
#self.lib.ffishim_bidirectional_verify_close_channel.restype = c_void_p
# self.lib.ffishim_bidirectional_resolve.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p, c_void_p)
# self.lib.ffishim_bidirectional_resolve.restype = c_void_p
self.lib.ffishim_bidirectional_wtp_verify_merch_close_message.argtypes = (c_void_p, c_void_p, c_void_p)
self.lib.ffishim_bidirectional_wtp_verify_merch_close_message.restype = c_void_p
self.lib.ffishim_free_string.argtypes = (c_void_p, )
@ -163,55 +160,40 @@ class Libbolt(object):
output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8'))
return output_dictionary.get('cust_close')
def bidirectional_merchant_close(self, channel_state, channel_token, cust_close, merch_state):
def bidirectional_merchant_close(self, channel_state, channel_token, address, cust_close, merch_state):
output_string = self.lib.ffishim_bidirectional_merchant_close(channel_state.encode(), channel_token.encode(),
cust_close.encode(), merch_state.encode())
address.encode(), cust_close.encode(), merch_state.encode())
output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8'))
return (output_dictionary.get('wpk'), output_dictionary.get('revoke_token'), output_dictionary.get('error'))
return (output_dictionary.get('wpk'), output_dictionary.get('merch_close'), output_dictionary.get('error'))
# WTP logic
def wtp_get_wallet(self, cust_state):
cust_state_dict = self.interperate_json_string_as_dictionary(cust_state)
cust_state_dict = self._interperate_json_string_as_dictionary(cust_state)
return json.dumps(cust_state_dict.get("wpk")), json.dumps(cust_state_dict.get("wallet"))
def wtp_get_close_token(self, cust_close):
cust_close_dict = self.interperate_json_string_as_dictionary(cust_close)
cust_close_dict = self._interperate_json_string_as_dictionary(cust_close)
return json.dumps(cust_close_dict.get("signature"))
def wtp_verify_open_channel(self, channel_token, wpk, cust_close_wallet, close_token):
output_string = self.lib.ffishim_bidirectional_verify_open_channel(channel_token.encode(), wpk.encode(), cust_close_wallet.encode(), close_token.encode())
def wtp_verify_cust_close_message(self, channel_token, wpk, cust_close_wallet, close_token):
output_string = self.lib.ffishim_bidirectional_wtp_verify_cust_close_message(channel_token.encode(),
wpk.encode(),
cust_close_wallet.encode(),
close_token.encode())
output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8'))
return output_dictionary.get('result')
# def bidirectional_resolve(self, pp, cust_data, merch_data, cust_closure, merch_closure):
# output_string = self.lib.ffishim_bidirectional_resolve( pp.encode(), cust_data.encode(), merch_data.encode(), cust_closure.encode(), merch_closure.encode())
# output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8'))
# return (int(output_dictionary['new_b0_cust']), int(output_dictionary['new_b0_merch']))
#
# # --------------------------------------------
# def commit_scheme_decommit(self, csp, commitment, x):
# output_string = self.lib.ffishim_commit_scheme_decommit(csp.encode(), commitment.encode(), x.encode())
# output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8'))
# if output_dictionary['return_value'] == 'true':
# return True
# return False
def wtp_verify_merch_close_message(self, channel_token, wpk, merch_close):
output_string = self.lib.ffishim_bidirectional_wtp_verify_merch_close_message(channel_token.encode(),
wpk.encode(),
merch_close.encode())
output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8'))
return output_dictionary.get('result')
def interperate_json_string_as_dictionary(self, json_string):
def _interperate_json_string_as_dictionary(self, json_string):
return ast.literal_eval(json_string)
def util_convert_int_list_to_hex_string(self, dictionary):
return "".join(["{0:02x}".format(x) for x in dictionary])
# def util_extract_public_key_from_keypair(self, keypair):
# # Interperate the input keypair struct as a dictionary and then extract
# dictionary = self.interperate_json_string_as_dictionary(keypair)
# return json.dumps(dictionary['pk'])
#
# def util_extract_pub_bases_from_keypair(self, keypair):
# dictionary = self.interperate_json_string_as_dictionary(keypair)
# return json.dumps(dictionary['bases'])
if platform == 'darwin':
prefix = 'lib'
ext = 'dylib'
@ -285,6 +267,8 @@ def run_unit_test():
(cust_state, is_pay_valid) = libbolt.bidirectional_pay_verify_payment_token(channel_state, cust_state, pay_token)
print("Pay token is valid: ", is_pay_valid)
old_cust_close = libbolt.bidirectional_customer_close(channel_state, cust_state)
# make a payment
amount = 10
(payment_proof2, new_cust_state2) = libbolt.bidirectional_pay_generate_payment_proof(channel_state, cust_state, amount)
@ -312,8 +296,20 @@ def run_unit_test():
print("Cust close msg: ", cust_close)
print("<========================================>")
merch_close_tokens = libbolt.bidirectional_merchant_close(channel_state, channel_token, cust_close, merch_state)
print("Merch close tokens: ", merch_close_tokens)
# normal case: no action b/c cust close is valid
address = "11" * 32
merch_close = libbolt.bidirectional_merchant_close(channel_state, channel_token, address, cust_close, merch_state)
print("Customer initiated - Merch close msg: ", merch_close)
print("<========================================>")
# common case: merchant catches customer double spending
address = "11" * 32
merch_wpk, merch_close_msg, _ = libbolt.bidirectional_merchant_close(channel_state, channel_token, address, old_cust_close, merch_state)
print("Double spend - Merch close msg: ", merch_close_msg)
merch_close_valid = libbolt.wtp_verify_merch_close_message(channel_token, merch_wpk, merch_close_msg)
print("Merchant close msg valid: ", merch_close_valid)
print("<========================================>")
print("<========================================>")
wpk, cust_close_wallet = libbolt.wtp_get_wallet(cust_state)
@ -321,9 +317,11 @@ def run_unit_test():
print("close-msg wallet = ", cust_close_wallet)
cust_close_token = libbolt.wtp_get_close_token(cust_close)
print("close token: ", cust_close_token)
print("Valid channel opening: ", libbolt.wtp_verify_open_channel(channel_token, wpk, cust_close_wallet, cust_close_token))
print("Valid channel opening: ", libbolt.wtp_verify_cust_close_message(channel_token, wpk, cust_close_wallet, cust_close_token))
# TODO: merch close when cust_close represents correct channel state
print("Invalid channel opening: ", libbolt.wtp_verify_open_channel(channel_token, revoked_wpk, cust_close_wallet, cust_close_token))
print("Invalid channel opening: ", libbolt.wtp_verify_cust_close_message(channel_token, revoked_wpk, cust_close_wallet, cust_close_token))
print("<========================================>")
if __name__ == "__main__":
run_unit_test()

View File

@ -15,7 +15,7 @@ use pairing::bls12_381::{Bls12};
use ff::PrimeField;
use cl::{BlindKeyPair, KeyPair, Signature, PublicParams, setup};
use ped92::{CSParams, Commitment, CSMultiParams};
use util::{hash_pubkey_to_fr, convert_int_to_fr, hash_to_fr, RevokedMessage};
use util::{hash_pubkey_to_fr, convert_int_to_fr, hash_to_fr, RevokedMessage, hash_to_slice};
use rand::Rng;
use std::collections::HashMap;
use std::fmt::Display;
@ -104,7 +104,7 @@ pub struct ChannelState<E: Engine> {
pub struct ChannelToken<E: Engine> {
pub pk_c: Option<secp256k1::PublicKey>, // pk_c
pub pk_m: secp256k1::PublicKey, // pk_m
pub cl_pk_m: cl::PublicKey<E>, // PK_m
pub cl_pk_m: cl::PublicKey<E>, // PK_m (used for verifying blind signatures)
pub mpk: cl::PublicParams<E>, // mpk for PK_m
pub comParams: CSMultiParams<E>,
}
@ -239,7 +239,6 @@ impl<E: Engine> CustomerState<E> {
let ct_db= HashMap::new();
let pt_db= HashMap::new();
//println!("Customer wallet formed -> now returning the structure to the caller.");
return CustomerState {
name: name,
pk_c: pk_c,
@ -290,7 +289,6 @@ impl<E: Engine> CustomerState<E> {
let is_valid = pk.verify(&mpk, &close_wallet, &unblind_close_token);
if is_valid {
// record the unblinded close token
//cp.pub_params.keypair
self.close_tokens.insert( self.index, unblind_close_token);
}
return is_valid;
@ -417,18 +415,47 @@ impl<E: Engine> CustomerState<E> {
}
// pub name: String,
// pub pk_c: secp256k1::PublicKey,
// sk_c: secp256k1::SecretKey,
// pub cust_balance: i32, //
// pub merch_balance: i32,
// pub wpk: secp256k1::PublicKey, // keypair bound to the wallet
// wsk: secp256k1::SecretKey,
// old_kp: Option<WalletKeyPair>, // old wallet key pair
// t: E::Fr, // randomness used to form the commitment
// wallet: Wallet<E>, // vector of field elements that represent wallet
// pub w_com: Commitment<E>, // commitment to the current state of the wallet
// index: i32,
// close_tokens: HashMap<i32, Signature<E>>,
// pay_tokens: HashMap<i32, Signature<E>>
impl<E: Engine> fmt::Display for CustomerState<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut content = String::new();
content = format!("pk = {}\n", &self.pk_c);
content = format!("name = {}\n", &self.name);
content = format!("{}pk = {}\n", content, &self.pk_c);
content = format!("{}sk = {}\n", content, &self.sk_c);
content = format!("{}cust-bal = {}\n", content, &self.cust_balance);
content = format!("{}merch-bal = {}\n", content, &self.merch_balance);
// add remaining fields
content = format!("{}wpk = {}\nwsk = {}\n", content, &self.wpk, &self.wsk);
if (!self.old_kp.is_none()) {
let old_kp = self.old_kp.unwrap();
content = format!("{}revoked: wpk = {}\nrevoked: wsk = {}\n", content, &old_kp.wpk, &old_kp.wsk);
}
content = format!("{}t = {}\n", content, &self.t);
write!(f, "CustomerState : (\n{}\n)", &content)
}
}
#[derive(Clone, Serialize, Deserialize)]
pub struct ChannelcloseM {
pub address: String,
pub revoke: Option<secp256k1::Signature>,
pub signature: secp256k1::Signature
}
///
/// Merchant wallet (NEW)
@ -562,6 +589,19 @@ impl<E: Engine> MerchantState<E> {
Err(BoltError::new("verify_revoke_token - Failed to verify the revoke token for wpk!"))
}
pub fn sign_revoke_message(&self, address: String, revoke_token: &Option<secp256k1::Signature>) -> ChannelcloseM {
let secp = secp256k1::Secp256k1::signing_only();
let mut msg = Vec::new();
msg.extend(address.as_bytes());
if !revoke_token.is_none() {
let r = revoke_token.unwrap().serialize_der().to_vec();
msg.extend(r);
}
let msg2 = secp256k1::Message::from_slice(&hash_to_slice(&msg) ).unwrap();
let merch_sig = secp.sign(&msg2, &self.sk);
return ChannelcloseM { address: address.clone(), revoke: revoke_token.clone(), signature: merch_sig };
}
}
#[cfg(test)]
@ -612,10 +652,6 @@ mod tests {
let amount = 10;
let (pay_proof, new_com, old_wpk, new_cw) = cust_state.generate_payment(rng, &channel, amount);
// println!("{}", new_com);
// println!("wpk => {}", old_wpk);
// println!("{}", new_cw);
// new pay_token is not sent until revoke_token is obtained from the customer
let new_close_token = merch_state.verify_payment(rng, &channel, &pay_proof, &new_com, &old_wpk, amount).unwrap();

View File

@ -11,6 +11,7 @@ pub mod ffishim {
use libc::c_char;
use std::ffi::{CStr, CString};
use std::str;
use channels::ChannelcloseM;
fn error_message(s: String) -> *mut c_char {
let ser = ["{\'error\':\'", serde_json::to_string(&s).unwrap().as_str(), "\'}"].concat();
@ -324,8 +325,11 @@ pub mod ffishim {
}
#[no_mangle]
pub extern fn ffishim_bidirectional_merchant_close(ser_channel_state: *mut c_char, ser_channel_token: *mut c_char,
ser_cust_close: *mut c_char, ser_merch_state: *mut c_char) -> *mut c_char {
pub extern fn ffishim_bidirectional_merchant_close(ser_channel_state: *mut c_char,
ser_channel_token: *mut c_char,
ser_address: *const c_char,
ser_cust_close: *mut c_char,
ser_merch_state: *mut c_char) -> *mut c_char {
// Deserialize the channel state
let channel_state: bidirectional::ChannelState<Bls12> = deserialize_object(ser_channel_state);
// Deserialize the channel token
@ -335,20 +339,26 @@ pub mod ffishim {
// Deserialize the merch wallet
let merch_state: bidirectional::MerchantState<Bls12> = deserialize_object(ser_merch_state);
// Deserialize the destination address as a string
let ser_addr_bytes = unsafe { CStr::from_ptr(ser_address).to_bytes() };
let address: &str = str::from_utf8(ser_addr_bytes).unwrap(); // make sure the bytes are UTF-8
let option = bidirectional::merchant_close(&channel_state, &channel_token, &cust_close, &merch_state);
let keys = match option {
Ok(n) => n.unwrap(),
Err(err) => return error_message(err),
};
let merch_close: bidirectional::ChannelcloseM = merch_state.sign_revoke_message(address.to_string(), &keys.revoke_token);
let ser = ["{\'wpk\':\'", serde_json::to_string(&keys.wpk).unwrap().as_str(),
"\', \'revoke_token\':\'", serde_json::to_string(&keys.revoke_token).unwrap().as_str(), "\'}"].concat();
"\', \'merch_close\':\'", serde_json::to_string(&merch_close).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_verify_open_channel(ser_channel_token: *mut c_char,
pub extern fn ffishim_bidirectional_wtp_verify_cust_close_message(ser_channel_token: *mut c_char,
ser_wpk: *mut c_char,
ser_close_msg: *mut c_char,
ser_close_token: *mut c_char) -> *mut c_char {
@ -362,7 +372,26 @@ pub mod ffishim {
// Deserialize the close token
let close_token: bidirectional::Signature<Bls12> = deserialize_object(ser_close_token);
// check the signatures
let token_valid = bidirectional::verify_open_channel(&channel_token, &wpk, &close_msg, &close_token);
let token_valid = bidirectional::wtp_verify_cust_close_message(&channel_token, &wpk, &close_msg, &close_token);
let ser = ["{\'result\':\'", serde_json::to_string(&token_valid).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_wtp_verify_merch_close_message(ser_channel_token: *mut c_char, ser_wpk: *mut c_char, ser_merch_close: *mut c_char) -> *mut c_char {
// Deserialize the channel token
let channel_token: bidirectional::ChannelToken<Bls12> = deserialize_object(ser_channel_token);
// Deserialize the wpk
let wpk: secp256k1::PublicKey = deserialize_object(ser_wpk);
// Deserialize the merch close
//let revoke_token: secp256k1::Signature = deserialize_object(ser_revoke_token);
let merch_close: bidirectional::ChannelcloseM = deserialize_object(ser_merch_close);
let revoke_token_valid = bidirectional::wtp_verify_revoke_message(&channel_token, &wpk, &merch_close.revoke.unwrap());
let merch_close_valid = bidirectional::wtp_verify_merch_close_message(&channel_token, &merch_close);
let token_valid = revoke_token_valid && merch_close_valid;
let ser = ["{\'result\':\'", serde_json::to_string(&token_valid).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()

View File

@ -124,11 +124,12 @@ pub mod bidirectional {
use serde::{Serialize, Deserialize};
use std::sync::mpsc::channel;
use util::RevokedMessage;
use util::{RevokedMessage, hash_to_slice};
pub use ped92::Commitment;
pub use cl::{PublicKey, Signature};
pub use BoltResult;
pub use channels::{ChannelState, ChannelToken, CustomerState, MerchantState, PubKeyMap, ChannelParams, BoltError, ResultBoltSig};
pub use channels::{ChannelState, ChannelToken, CustomerState, MerchantState, ChannelcloseM,
PubKeyMap, ChannelParams, BoltError, ResultBoltSig};
pub use nizk::{CommitmentProof, Proof};
pub use wallet::Wallet;
pub use cl::PublicParams;
@ -430,6 +431,7 @@ pub mod bidirectional {
let msg = secp256k1::Message::from_slice(&revoke_msg.hash_to_slice()).unwrap();
// verify that the revocation token is valid
if secp.verify(&msg, &revoke_token, &wpk).is_ok() {
// compute signature on
return Ok(Some(revoked_state.clone()));
}
}
@ -440,10 +442,14 @@ pub mod bidirectional {
Err(String::from("merchant_close - Customer close message not valid!"))
}
// pub fn wtp_sign_merch_close_message<E: Engine>(address: &Vec<u8>, revoke_token: &Option<secp256k1::Signature>, merch_state: &MerchantState<E>) -> ChannelcloseM {
// return merch_state.sign_revoke_message(&address, &revoke_token);
// }
///
/// WTP for validating that a close_token was well-formed
/// Used in open-channel WTP for validating that a close_token is a valid signature under <
///
pub fn verify_open_channel<E: Engine>(channel_token: &ChannelToken<E>, wpk: &secp256k1::PublicKey, close_msg: &wallet::Wallet<E>, close_token: &Signature<E>) -> bool {
pub fn wtp_verify_cust_close_message<E: Engine>(channel_token: &ChannelToken<E>, wpk: &secp256k1::PublicKey, close_msg: &wallet::Wallet<E>, close_token: &Signature<E>) -> bool {
// close_msg => <pkc> || <wpk> || <balance-cust> || <balance-merch> || CLOSE
// close_token = regular CL signature on close_msg
// channel_token => <pk_c, CL_PK_m, pk_m, mpk, comParams>
@ -459,7 +465,33 @@ pub mod bidirectional {
return pkc_thesame && wpk_thesame && channel_token.cl_pk_m.verify(&channel_token.mpk, &close_msg.as_fr_vec(), &close_token);
}
///
/// Used in merch-close WTP for validating that revoke_token is a valid signature under <wpk> and the <revoked || wpk> message
///
pub fn wtp_verify_revoke_message<E: Engine>(channel_token: &ChannelToken<E>, wpk: &secp256k1::PublicKey, revoke_token: &secp256k1::Signature) -> bool {
let secp = secp256k1::Secp256k1::verification_only();
let revoke_msg = RevokedMessage::new(String::from("revoked"), wpk.clone());
let msg = secp256k1::Message::from_slice(&revoke_msg.hash_to_slice()).unwrap();
// verify that the revocation token is valid with respect to revoked || wpk
return secp.verify(&msg, &revoke_token, &wpk).is_ok();
}
///
/// Used in merch-close WTP for validating that merch_sig is a valid signature under <merch_pk> on <dest_addr || revoke-token> message
///
pub fn wtp_verify_merch_close_message<E: Engine>(channel_token: &ChannelToken<E>, merch_close: &ChannelcloseM) -> bool {
let secp = secp256k1::Secp256k1::verification_only();
let mut msg = Vec::new();
msg.extend(merch_close.address.as_bytes());
if !merch_close.revoke.is_none() {
// serialize signature in DER format
let r = merch_close.revoke.unwrap().serialize_der().to_vec();
msg.extend(r);
}
let msg2 = secp256k1::Message::from_slice(&hash_to_slice(&msg)).unwrap();
// verify that merch sig is valid with respect to dest_address
return secp.verify(&msg2, &merch_close.signature, &channel_token.pk_m).is_ok();
}
}
#[cfg(all(test, feature = "unstable"))]

View File

@ -114,6 +114,14 @@ pub fn hash_buffer_to_fr<'a, E: Engine>(prefix: &'a str, buf: &[u8; 64]) -> E::F
return result.unwrap();
}
pub fn hash_to_slice(input_buf: &Vec<u8>) -> [u8; 32] {
let sha2_digest = sha512::hash(input_buf.as_slice());
let mut hash_buf: [u8; 32] = [0; 32];
hash_buf.copy_from_slice(&sha2_digest[0..32]);
return hash_buf;
}
#[derive(Clone, Serialize, Deserialize)]
pub struct RevokedMessage {