use data_encoding::BASE64;
use reqwest::Request;
use ring::digest::{Context, Digest, SHA256};
use ring::hmac;
pub struct SignatureInput {
pub private_key: String,
pub nonce: String,
pub encoded_payload: String,
pub uri_path: String,
}
impl SignatureInput {
pub fn sign(self) -> String {
let digest = Self::take_sha(self.nonce, self.encoded_payload);
let key = Self::build_hmac_key(self.private_key);
let signature = Self::generate_hmac(key, digest.as_ref(), self.uri_path);
signature
}
fn take_sha(nonce: String, encoded_payload: String) -> Digest {
let mut context = Context::new(&SHA256);
let concat = nonce + &encoded_payload;
context.update(concat.as_bytes());
context.finish()
}
fn build_hmac_key(private_key: String) -> hmac::Key {
let secret_str = BASE64.decode(private_key.as_bytes()).unwrap();
hmac::Key::new(hmac::HMAC_SHA512, &secret_str)
}
fn generate_hmac(key: hmac::Key, digest: &[u8], uri_path: String) -> String {
let uri_bytes = uri_path.as_bytes();
let hmac_input = &[uri_bytes, digest].concat();
let tag = hmac::sign(&key, hmac_input);
let tag_bytes = tag.as_ref();
BASE64.encode(tag_bytes)
}
}
pub fn get_kraken_signature(nonce: String, private_key: String, req: &Request) -> String {
let path = req.url().path();
let req_body = req.body().unwrap().as_bytes().unwrap().to_vec();
let body_str = String::from_utf8(req_body).unwrap();
let signature = SignatureInput {
private_key,
nonce,
encoded_payload: body_str,
uri_path: path.to_owned(),
};
signature.sign()
}
#[cfg(test)]
mod test {
use super::SignatureInput;
use pretty_assertions::assert_eq;
#[test]
fn test_signature() {
let sig = SignatureInput {
private_key: "kQH5HW/8p1uGOVjbgWA7FunAmGO8lsSUXNsu3eow76sz84Q18fWxnyRzBHCd3pd5nE9qa99HAZtuZuj6F1huXg==".to_owned(),
nonce: "1616492376594".to_owned(),
encoded_payload: "nonce=1616492376594&ordertype=limit&pair=XBTUSD&price=37500&type=buy&volume=1.25".to_owned(),
uri_path: "/0/private/AddOrder".to_owned(),
};
let expected = "4/dpxb3iT4tp/ZCVEwSnEsLxx0bqyhLpdfOpc6fn7OR8+UClSV5n9E6aSS8MPtnRfp32bAb0nmbRn6H8ndwLUQ==".to_owned();
let observed = sig.sign();
assert_eq!(expected, observed);
}
}