如何在 Rust 中进行加密签名以避免 Python 调用
How to do cryptographic signature in Rust to avoid Python call
为什么在这段代码的底部,我需要使用以下模式:
let a = urlpath.to_string();
let b = nonce.to_string();
let c = ordertype.to_string();
let d = pair.to_string();
let e = price.to_string();
let f = type_.to_string();
let g = volume.to_string();
let h = api_sec.to_string();
let kwargs = vec![("cmd", "account_balance"), ("urlpath", &a), ("nonce", &b), ("ordertype", &c), ("pair", &d), ("price", &e), ("type", &f), ("volume", &g), ("secret", &h)];
如果我替换vec中的变量&a!使用 &urlpath.to_string() 然后它失败说一个临时值正在被删除并且稍后使用它。
但是,无论我是否添加额外的 let 语句,该表达式的计算结果不是相同的吗?我怎样才能使它更符合 Rust 的习惯?
use std::{time::{SystemTime, UNIX_EPOCH}};
use pyo3::types::IntoPyDict;
fn main() -> PyResult<()> {
let urlpath = "/0/private/Balance";
println!("{}", urlpath);
let api_sec = "<REPLACE>";
println!("{}", api_sec);
let nonce = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_millis();
println!("{}", nonce);
let ordertype = "limit";
println!("{}", ordertype);
let pair = "XBTUSD";
println!("{}", pair);
let price: i32 = 37500;
println!("{}", price);
let type_ = "buy";
println!("{}", type_);
let volume = 1.25;
println!("{}", volume);
Python::with_gil(|py| {
let fun: Py<PyAny> = PyModule::from_code(
py,
"
import urllib.parse
import hashlib
import hmac
import base64
def get_kraken_signature(*args, **kwargs):
if args != ():
print('called with args', args)
if kwargs != {}:
print('called with kwargs', kwargs)
if args == () and kwargs == {}:
print('called with no arguments')
if kwargs[\"cmd\"] == \"account_balance\":
urlpath = kwargs[\"urlpath\"]
data = {
\"nonce\": kwargs[\"nonce\"],
}
secret = kwargs[\"secret\"]
elif kwargs[\"cmd\"] == \"send_order\":
urlpath = kwargs[\"urlpath\"]
data = {
\"nonce\": kwargs[\"nonce\"],
\"ordertype\": kwargs[\"ordertype\"],
\"pair\": kwargs[\"pair\"],
\"price\": kwargs[\"price\"],
\"type\": kwargs[\"type\"],
\"volume\": kwargs[\"volume\"],
}
secret = kwargs[\"secret\"]
else:
exit(0)
postdata = urllib.parse.urlencode(data)
encoded = (str(data['nonce']) + postdata).encode()
message = urlpath.encode() + hashlib.sha256(encoded).digest()
mac = hmac.new(base64.b64decode(secret), message, hashlib.sha512)
sigdigest = base64.b64encode(mac.digest())
print(\"API-Sign: {}\".format(sigdigest.decode()))
return sigdigest.decode()
",
"",
"",
)?.getattr("get_kraken_signature")?.into();
let a = urlpath.to_string();
let b = nonce.to_string();
let c = ordertype.to_string();
let d = pair.to_string();
let e = price.to_string();
let f = type_.to_string();
let g = volume.to_string();
let h = api_sec.to_string();
let kwargs = vec![("cmd", "account_balance"), ("urlpath", &a), ("nonce", &b), ("ordertype", &c), ("pair", &d), ("price", &e), ("type", &f), ("volume", &g), ("secret", &h)];
let result = fun.call(py, (), Some(kwargs.into_py_dict(py)))?;
println!("{}", result);
Ok(())
})
}
奖励:问题的第二部分是如何用惯用的 Rust 重写 Python 部分?我已经尝试过但失败了,所以如果任何加密专家可以提供帮助,那将会很有帮助。
第一部分:(解释)
因为 a
是值的所有者,并且您正在使用所有者传递引用,即使在执行后它仍将保留在范围内,但在您直接传递 &urlpath.to_string()
的情况下,没有任何所有者,一旦执行结束,该值将被删除,并且将有一个悬空引用,这是消息的原因。
第二部分:(Python 到 rust 转换)
我不是加密专家,但我尝试转换您提供的没有条件部分的相同脚本,并匹配 python 和 rust.
中的输出
extern crate url;
extern crate base64;
// use std::time::{SystemTime, UNIX_EPOCH};
use url::form_urlencoded;
use sha2::{Sha256, Digest};
extern crate ring;
extern crate data_encoding;
use ring::hmac;
use data_encoding::BASE64;
use std::collections::HashMap;
fn main() {
let urlpath = String::from("/0/private/Balance");
// let nonce = SystemTime::now()
// .duration_since(UNIX_EPOCH)
// .expect("Time went backwards")
// .as_millis();
let nonce: &str = &(1645371362680 as i64).to_string();
let mut data = HashMap::new();
data.insert("nonce", nonce);
let postdata: String = form_urlencoded::Serializer::new(String::new())
.extend_pairs(data.iter())
.finish();
let encoded = format!("{}{}", nonce, postdata);
let message: Vec<u8> = [urlpath.as_bytes(), Sha256::digest(encoded.as_bytes()).as_ref()].concat();
let secret_key = String::from("secret");
let signed_key = hmac::Key::new(hmac::HMAC_SHA512, secret_key.as_bytes());
let signature = hmac::sign(&signed_key, &message);
let b64_encoded_sig = BASE64.encode(signature.as_ref());
println!("Output: {}", b64_encoded_sig);
}
为什么在这段代码的底部,我需要使用以下模式:
let a = urlpath.to_string();
let b = nonce.to_string();
let c = ordertype.to_string();
let d = pair.to_string();
let e = price.to_string();
let f = type_.to_string();
let g = volume.to_string();
let h = api_sec.to_string();
let kwargs = vec![("cmd", "account_balance"), ("urlpath", &a), ("nonce", &b), ("ordertype", &c), ("pair", &d), ("price", &e), ("type", &f), ("volume", &g), ("secret", &h)];
如果我替换vec中的变量&a!使用 &urlpath.to_string() 然后它失败说一个临时值正在被删除并且稍后使用它。
但是,无论我是否添加额外的 let 语句,该表达式的计算结果不是相同的吗?我怎样才能使它更符合 Rust 的习惯?
use std::{time::{SystemTime, UNIX_EPOCH}};
use pyo3::types::IntoPyDict;
fn main() -> PyResult<()> {
let urlpath = "/0/private/Balance";
println!("{}", urlpath);
let api_sec = "<REPLACE>";
println!("{}", api_sec);
let nonce = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_millis();
println!("{}", nonce);
let ordertype = "limit";
println!("{}", ordertype);
let pair = "XBTUSD";
println!("{}", pair);
let price: i32 = 37500;
println!("{}", price);
let type_ = "buy";
println!("{}", type_);
let volume = 1.25;
println!("{}", volume);
Python::with_gil(|py| {
let fun: Py<PyAny> = PyModule::from_code(
py,
"
import urllib.parse
import hashlib
import hmac
import base64
def get_kraken_signature(*args, **kwargs):
if args != ():
print('called with args', args)
if kwargs != {}:
print('called with kwargs', kwargs)
if args == () and kwargs == {}:
print('called with no arguments')
if kwargs[\"cmd\"] == \"account_balance\":
urlpath = kwargs[\"urlpath\"]
data = {
\"nonce\": kwargs[\"nonce\"],
}
secret = kwargs[\"secret\"]
elif kwargs[\"cmd\"] == \"send_order\":
urlpath = kwargs[\"urlpath\"]
data = {
\"nonce\": kwargs[\"nonce\"],
\"ordertype\": kwargs[\"ordertype\"],
\"pair\": kwargs[\"pair\"],
\"price\": kwargs[\"price\"],
\"type\": kwargs[\"type\"],
\"volume\": kwargs[\"volume\"],
}
secret = kwargs[\"secret\"]
else:
exit(0)
postdata = urllib.parse.urlencode(data)
encoded = (str(data['nonce']) + postdata).encode()
message = urlpath.encode() + hashlib.sha256(encoded).digest()
mac = hmac.new(base64.b64decode(secret), message, hashlib.sha512)
sigdigest = base64.b64encode(mac.digest())
print(\"API-Sign: {}\".format(sigdigest.decode()))
return sigdigest.decode()
",
"",
"",
)?.getattr("get_kraken_signature")?.into();
let a = urlpath.to_string();
let b = nonce.to_string();
let c = ordertype.to_string();
let d = pair.to_string();
let e = price.to_string();
let f = type_.to_string();
let g = volume.to_string();
let h = api_sec.to_string();
let kwargs = vec![("cmd", "account_balance"), ("urlpath", &a), ("nonce", &b), ("ordertype", &c), ("pair", &d), ("price", &e), ("type", &f), ("volume", &g), ("secret", &h)];
let result = fun.call(py, (), Some(kwargs.into_py_dict(py)))?;
println!("{}", result);
Ok(())
})
}
奖励:问题的第二部分是如何用惯用的 Rust 重写 Python 部分?我已经尝试过但失败了,所以如果任何加密专家可以提供帮助,那将会很有帮助。
第一部分:(解释)
因为 a
是值的所有者,并且您正在使用所有者传递引用,即使在执行后它仍将保留在范围内,但在您直接传递 &urlpath.to_string()
的情况下,没有任何所有者,一旦执行结束,该值将被删除,并且将有一个悬空引用,这是消息的原因。
第二部分:(Python 到 rust 转换)
我不是加密专家,但我尝试转换您提供的没有条件部分的相同脚本,并匹配 python 和 rust.
extern crate url;
extern crate base64;
// use std::time::{SystemTime, UNIX_EPOCH};
use url::form_urlencoded;
use sha2::{Sha256, Digest};
extern crate ring;
extern crate data_encoding;
use ring::hmac;
use data_encoding::BASE64;
use std::collections::HashMap;
fn main() {
let urlpath = String::from("/0/private/Balance");
// let nonce = SystemTime::now()
// .duration_since(UNIX_EPOCH)
// .expect("Time went backwards")
// .as_millis();
let nonce: &str = &(1645371362680 as i64).to_string();
let mut data = HashMap::new();
data.insert("nonce", nonce);
let postdata: String = form_urlencoded::Serializer::new(String::new())
.extend_pairs(data.iter())
.finish();
let encoded = format!("{}{}", nonce, postdata);
let message: Vec<u8> = [urlpath.as_bytes(), Sha256::digest(encoded.as_bytes()).as_ref()].concat();
let secret_key = String::from("secret");
let signed_key = hmac::Key::new(hmac::HMAC_SHA512, secret_key.as_bytes());
let signature = hmac::sign(&signed_key, &message);
let b64_encoded_sig = BASE64.encode(signature.as_ref());
println!("Output: {}", b64_encoded_sig);
}