Export EC public key using Rust openssl - rust

I'm just starting out with rust and playing with a toy encryption library following docs at https://docs.rs/openssl/0.10.28/openssl/. I'd like to generate an elliptic-curve private+public keypair and print them in der or pem formats. I found it pretty straightforward to do with the private key
use openssl::ec::{EcKey,EcGroup};
use openssl::nid::Nid;
pub fn generate_keypair() {
let group = EcGroup::from_curve_name(Nid::SECP256K1).unwrap();
let key = EcKey::generate(&group).unwrap();
println!("{:?}", key.private_key_to_der().unwrap()); // can use pem instead and print as utf8-string
}
However there doesn't seem to be any method like public_key_to_der for EcKey to export a public key, even debug-printing it doesn't work:
let public = key.public_key();
println!("{:?}", public);
gives a compilation error
openssl::ec::EcPointRef` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug`

use openssl::ec::{EcKey,EcGroup, EcPoint};
use openssl::nid::Nid;
fn key_from_public_key() {
let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
let key = EcKey::generate(&group).unwrap();
let mut ctx = openssl::bn::BigNumContext::new().unwrap();
println!("private eckey = {:?}", key.private_key());
let bytes = key.public_key().to_bytes(&group,
openssl::ec::PointConversionForm::COMPRESSED, &mut ctx).unwrap();
println!("public key = {:?}", bytes);
drop(key);
let public_key = EcPoint::from_bytes(&group, &bytes, &mut ctx).unwrap();
let ec_key = EcKey::from_public_key(&group, &public_key).unwrap();
assert!(ec_key.check_key().is_ok());
}

Ok, looks like I need to wrap it in PKey first using pkey::from_ec_key.

Related

What type should I use to deserialize Legion ECS' World?

I want to deserialize the legion's world but don't know what type should I use. This is my deserializing function:
pub fn deserialize(path: &str) -> World {
let registry = get_registry();
let data_raw = pollster::block_on(load_string(path)).expect("Unable to load file");
let mut deserializer = ron::from_str(data_raw.as_str()).expect("Unable to deserialze the file");
let entity_serializer = Canon::default();
registry.as_deserialize(&entity_serializer).deserialize(&mut deserializer).unwrap()
}
As you can see, the deserializer has no type.
This might not help but this is the serialization function that I implemented:
pub fn serialize(world: &World, path: &str) {
let registry = get_registry();
let entity_serializer = Canon::default();
let serializable = world.as_serializable(any(), &registry, &entity_serializer);
let ron = ron::to_string(
&serializable
).expect("Cannot Serialize World!");
let mut file = File::create(path).expect("Unable to create file");
file.write_all(ron.as_bytes()).expect("Unable to write it to the file");
}
I'm using serde and ron.
deserialize method in question comes from DeserializeSeed trait, so its argument have to be something implementing Deserializer. In case of ron, the type to use is ron::Deserializer (&mut ron::Deserializer, to be precise), which can be created with Deserializer::from_str.
Therefore, this code should work:
pub fn deserialize(path: &str) -> World {
let registry = get_registry();
let data_raw = pollster::block_on(load_string(path)).expect("Unable to load file");
let mut deserializer = ron::Deserializer::from_str(data_raw.as_str()).expect("Unable to deserialze the file");
let entity_serializer = Canon::default();
registry.as_deserialize(&entity_serializer).deserialize(&mut deserializer).unwrap()
}

How to generate the public key from Polkadot Address from Substrate Side

I'm generating a Public key from PolkadotJS as follows
const keyring = new Keyring({ type: "sr25519" });
const account = keyring.addFromUri("//Bob", { name: "Bob default" });
// encoded public key
let public_key = keyring.encodeAddress(account.publicKey, 42);
console.log(public_key);
I am adding the type of public_key as "public_key": "Vec<u8>",
I am reading the public key from Substrate Node as follows
// pk_raw is a Vec<u8> array
let pk = str::from_utf8(pk_raw.as_ref()).unwrap()
// the above returns `5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty`
I need to generate the public key from this value. I tried with the followings
ed25519::Public::try_from(&*pk_raw).unwrap();
// above throws error since the data length is not equals to 32
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
if data.len() == 32 {
let mut inner = [0u8; 32];
inner.copy_from_slice(data);
Ok(Public(inner))
} else {
Err(())
}
}
Is there a way to generate the public key using 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty from Substrate Rust Side?
You can use something like this:
use sp_core::crypto::Ss58Codec;
ed25519::Public::from_ss58check("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty").expect("Valid address")

How to hash a binary file in Rust

In rust, using sha256 = "1.0.2" (or similar), how do I hash a binary file (i.e. a tar.gz archive)?
I'm trying to get the sha256 of that binary file.
This doesn't work:
fn hash() {
let file = "file.tar.gz";
let computed_hash = sha256::digest_file(std::path::Path::new(file)).unwrap();
computed_hash
}
the output is:
...
Error { kind: InvalidData, message: "stream did not contain valid UTF-8" }
The sha2 crate upon which depends supports hashing Readable objects without needing to read the entire file into memory. See the example in the hashes readme.
use sha2::{Sha256, Digest};
use std::{io, fs};
let mut hasher = Sha256::new();
let mut file = fs::File::open("file.tar.gz")?;
let bytes_written = io::copy(&mut file, &mut hasher)?;
let hash_bytes = hasher.finalize();
Edit:
Upgrading to sha256 = "1.0.3" should fix this
The issue is that digest_file is internally reading the file to a String, which requires that it contains valid UTF-8, which is obviously not what you want in this case.
Instead, you could read the file in as bytes and pass that into sha256::digest_bytes:
let bytes = std::fs::read(path).unwrap(); // Vec<u8>
let hash = sha256::digest_bytes(&bytes);
Here's an implementation using the sha2 crate that doesn't read the entire file into memory, and doesn't depend on the ring crate. In my case, ring isn't pure rust, which leads to cross-compilation difficulties.
use data_encoding::HEXLOWER;
use sha2::{Digest, Sha256};
use std::fs::File;
use std::io::{BufReader, Read};
use std::path::{Path, PathBuf};
/// calculates sha256 digest as lowercase hex string
fn sha256_digest(path: &PathBuf) -> Result<String> {
let input = File::open(path)?;
let mut reader = BufReader::new(input);
let digest = {
let mut hasher = Sha256::new();
let mut buffer = [0; 1024];
loop {
let count = reader.read(&mut buffer)?;
if count == 0 { break }
hasher.update(&buffer[..count]);
}
hasher.finalize()
};
Ok(HEXLOWER.encode(digest.as_ref()))
}

How do I set the Initialization Vector (IV) in Rust's `aes` crate? (AES-128 CBC)

As the title suggests. I can create a new Aes128 cipher, but I've checked the documentation and found nothing that might allow me to provide an IV. Am I missing something obvious?
let cipher = Aes128::new(key);
let mut block = file_blocks[0].clone();
cipher.decrypt_block(&mut block);
You can use crate aes and block_modes.
Like this, but this test will panic because I used unwrap() and didn't set effective 'key', 'iv' and 'encrypted_data';
Cargo.toml
base64 = "0.13.0"
aes = "0.7.4"
block-modes = "0.8.1"
lib.rs
use aes::Aes128;
use block_modes::block_padding::Pkcs7;
use block_modes::{BlockMode, Cbc};
// create an alias for convenience
type Aes128Cbc = Cbc<Aes128, Pkcs7>;
/// Use [key](https://en.wikipedia.org/wiki/Key_(cryptography)) and [initialization vector](https://en.wikipedia.org/wiki/Initialization_vector) to decrypt data encrypt by [aes128](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard); <br />
/// 使用密钥([百度百科](https://baike.baidu.com/item/%E5%AF%86%E9%92%A5) | [维基百科](https://zh.wikipedia.org/wiki/%E5%AF%86%E9%92%A5))和初始化向量([百度百科](https://baike.baidu.com/item/%E5%88%9D%E5%A7%8B%E5%8C%96%E5%90%91%E9%87%8F) | [维基百科](https://zh.wikipedia.org/wiki/%E5%88%9D%E5%A7%8B%E5%90%91%E9%87%8F))来解密根据 aes128([百度百科](https://baike.baidu.com/item/AES%E5%8A%A0%E5%AF%86%E6%A0%87%E5%87%86) | [维基百科](https://zh.wikipedia.org/wiki/%E9%AB%98%E7%BA%A7%E5%8A%A0%E5%AF%86%E6%A0%87%E5%87%86)) 进行加密的数据;
pub fn decrypt_aes128(key: &[u8], iv: &[u8], data: &[u8]) -> Vec<u8> {
let mut encrypted_data = data.clone().to_owned();
let cipher = Aes128Cbc::new_from_slices(&key, &iv).unwrap();
cipher.decrypt(&mut encrypted_data).unwrap().to_vec()
}
#[test]
fn test_demo_decrypt() {
let key = "something";
let iv = "something";
let data = "something";
let key = base64::decode(key).unwrap();
let iv = base64::decode(iv).unwrap();
let data = base64::decode(data).unwrap();
let result = decrypt_aes128(&key, &iv, &data);
let _result = std::str::from_utf8(&result).unwrap().to_string();
}

Rust pattern matching with an existing binding?

I am learning Rust from yesterday. The following code is simple --
use encoding_rs::Encoding;
use std::fs;
use std::fs::File;
use std::io::BufReader;
use std::io::Read;
use std::option::Option;
use std::path::Path;
extern crate encoding_rs;
extern crate encoding_rs_io;
fn main() {
let mut reader = BufReader::new(file);
let mut bom: [u8; 3] = [0; 3];
// read BOM
if let Ok(_) = reader.read_exact(&mut bom) {
// sniff BOM
// Because Rust disallows NULLs, hence I declare `Option<&Encoding>` to store the result of encoding.
let mut enc: Option<&Encoding> = None;
match Encoding::for_bom(&bom) {
Some((encoding, _)) => {
// <-- Some((enc, _))
enc = Some(encoding);
}
None => {
if let Some(encoding) = Encoding::for_label("UTF-8".as_bytes()) {
enc = Some(encoding);
}
}
}
if let Some(encoding) = enc {
println!("{:?}", encoding);
}
}
}
It opens a text file, and try to analyze its encoding by parsing BOM(Byte Order Marker). If Encoding::for_bom does not return an encoding, the code will take use UTF-8 as default.
I dislike unwrap() because it always assume there is a valid result
My question is : is there a way to do pattern matching and put the result directly into an existing mutable binding?
e.g. Change Some((encoding, _)) to Some((enc, _)) hence I don't need the line of enc = Some(encoding)
Many rust constructs can be used as expressions, i.e. they can return a value. So if every branch of your match returns a value of the same type, you can assign it directly into a variable. It does not need to be mutable unless you plan to change it later.
let mut reader = BufReader::new(file);
let mut bom: [u8; 3] = [0; 3];
if let Ok(_) = reader.read_exact(&mut bom) {
let enc = match Encoding::for_bom(&bom) {
Some((encoding, _)) => Some(encoding),
None => Encoding::for_label("UTF-8".as_bytes()),
};
if let Some(encoding) = enc {
println!("{:?}", encoding);
}
}
I'd use a combination of map and or_else:
let enc = Encoding::for_bom(&bom)
.map(|t| t.0)
.or_else(|| Encoding::for_label ("UTF-8".as_bytes()));
Or (clearer but slightly longer):
let enc = Encoding::for_bom(&bom)
.map(|(e, _)| e)
.or_else(|| Encoding::for_label ("UTF-8".as_bytes()));

Resources