I am writing a small blockchain simulation program in Rust and am struggling with the ECDSA signing key so I can sign transactions that are added to the blockchain.
I have the following key pair configured in a file called crypto.rs
:
pub struct KeyPair {
pub name: String,
pub public_key: String,
private_key: String,
}
and the following implementation to generate the keys:
impl KeyPair {
pub fn generate(name: String, key_store: &mut KeyStore) -> KeyPair {
// private key first
let secret = SecretKey::random(&mut OsRng);
let private_key = hex::encode(&secret.to_bytes());
// then public key
let public_key = hex::encode(secret.public_key().to_sec1_bytes());
let key_pair: KeyPair = KeyPair {
name,
public_key,
private_key,
};
// store a clone and return the original for further operations
key_store.append_key_pair(key_pair.clone());
key_store.write();
key_pair
}
}
A test generates the following in a file called key_store.json
:
{
"keys": [
{
"name": "TEST",
"public_key": "040eda3b0d1b30a78cc830ab9e69dc25a52d5448c3c8228511481a67b1f6420bc55beb4efc297a49a657cfe08b7873293e78fef553724453f55eea3656a6369d5a",
"private_key": "ccb789f12826fd81b45f27489b73ac5650f46f9b8f0281922f459be515f018b6"
}
]
}
Now to sign a transaction hash I need to get the signing key from the private key (SecretKey) but nothing I'm doing seems to work.
I've tried to extract the signature using this function but it panics with signature error
:
pub fn get_signature(&self, hash: &String) -> String {
let private_key = self.get_private_key();
let signing_key = match private_key.parse::<SigningKey>() {
Ok(key) => key,
Err(e) => panic!("Error parsing signing key from private key: {}", e),
};
let signature: Signature = signing_key.sign(&hash.as_bytes());
signature.to_string()
}
I was using this SO article to help understand the implementation:
How can I export (to store) an ecdsa key pair in Rust?
this implementation uses pem
but I have chosen to use a byte string converted to hex as this is a common way to store keys.
This attempt results in a panic with this message Error parsing bytes to String: invalid utf-8 sequence of 1 bytes from index 0
:
pub fn get_signature(&self, hash: &String) -> String {
let private_key: String = self.get_private_key();
let decoded: Vec<u8> = match hex::decode(&private_key) {
Ok(bytes) => bytes,
Err(e) => panic!("Error decoding bytes: {}", e),
};
let str_from_bytes: String = match String::from_utf8(decoded) {
Ok(s) => s,
Err(e) => panic!("Error parsing bytes to String: {}", e),
};
let signing_key = match str_from_bytes.parse::<SigningKey>() {
Ok(key) => key,
Err(e) => panic!("Error parsing signing key from private key: {}", e),
};
let signature: Signature = signing_key.sign(&hash.as_bytes());
signature.to_string()
}
Does anyone know how to get the signing key from a SecretKey after converting to hex?
Thanks
After experimenting this function returns a signature and the signing key used for verifying the signature later
pub fn get_signature(&self, hash: &String) -> (Signature, SigningKey) {
let private_key = self.get_private_key();
let key_bytes = hex::decode(&private_key).unwrap();
let signing_key = SigningKey::from_slice(key_bytes.as_slice()).unwrap();
let signature: Signature = signing_key.sign(&hash.as_bytes());
(signature, signing_key)
}
Using from_slice
to initialise the SigningKey appears to work:
SigningKey::from_slice(key_bytes.as_slice()).unwrap()
For completeness the accompanying verify
function looks like this:
pub fn verify(&self, signature: Signature, signing_key: SigningKey, hash: String) -> bool {
let verifying_key = VerifyingKey::from(&signing_key);
let verified = match verifying_key.verify(&hash.as_bytes(), &signature) {
Ok(_res) => true,
Err(_) => false,
};
verified
}
Comments and alternative solutions are very welcome as I am not 100% that this is the best way to do it but this works well enough.