Create DER encoded ASN.1 with pre-encoded DER parts in Rust - rust

I want to DER encode the following ASN.1 structure in Rust:
issuerAndSubject ::= SEQUENCE {
issuer Name,
subject Name
}
The Name structure is defined in RFC5280 and complex. I don't want to redefine this in Rust.
I already have an issuer and subject created in rust-openssl:
let issuer: &X509NameRef = get_issuer();
let subject: &X509NameRef = get_subject();
I can easily get the DER encoding for these objects:
let issuer_der = issuer.to_der().unwrap();
let subject_der = subject.to_der().unwrap();
but I need to insert them in a SEQUENCE. My preference would be to use Rust's asn1 crate, which allows for creating SEQUENCEs:
let ias_der = asn1::write(|w| {
w.write_element(
&asn1::SequenceWriter::new(&|w| {
w.write_element(&issuer_der); // I know, this doesn't work, but
w.write_element(&subject_der); // how do I do it?
}));
});
Is there a way to create a sequence with the DER-encoded data?

You can just use the dervice macro and use a simple structure:
#[derive(asn1::Asn1Read, asn1::Asn1Write, Debug)]
struct Signature {
issuer: u64,
subject: u64,
}
fn main() {
let data = asn1::write_single(&Signature{issuer: 0, subject: 1});
let sig: Signature = asn1::parse_single::<Signature>(data.as_slice()).unwrap();
let data = asn1::write_single(&sig);
eprintln!("{:?}", sig);
eprintln!("{:?}", data);
}

Related

Rust appending unicode escape before creating new struct

I am reading from a file and then creating a struct from those values.
let file = File::open(self.get_state_name().add(".sky")).unwrap();
let reader = BufReader::new(file);
for (_, line) in reader.lines().enumerate() {
let line = line.unwrap();
let key_value = line.split("`").collect::<Vec<&str>>();
let key = key_value[0].to_string();
let data = key_value[1].to_string();
self.set(key, data);
}
Set function creates a new struct named model
let model = Model::new(key, data);
New function just returns a struct named model:
pub fn new(key: String, data: String) -> Model {
Model { key, data }
}
Value of key is prefixed with unicode escapes like:
Model {
key: "\u{f}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}Test",
data: "Test Data",
},
Update:
Tried saving only ascii characters:
pub fn new(key: String, data: String) -> Model {
let key = key.replace(|c: char| !c.is_ascii(), "");
println!("Key: {}", key);
Model { key, data }
}
Update:
Saving file
let mut file = File::create(name.add(".sky")).unwrap();
for i in &data {
let data = i.to_string();
let bytes = bincode::serialize(&data).unwrap();
file.write_all(&bytes).expect("Unable to write to file");
}
.to_string() methods on struct
pub(crate) fn to_string(&self) -> String {
format!("{}`{}\n", self.key, self.data)
}
Here is key is without unicode escapes. It happens during Model { key, data } line.
Same doesn't happen when directly setting value not reading from file.
How to remove this and why is this happening?
You are writing the code using bincode::serialize but read back the data not with bincode::deserialize but using BufReader.
In order to properly serialize the string in a binary fashion, the encoder adds additional information about the data it stores.
If you know that only strings compatible with BufReader#lines will be processed, you can also use String#as_bytes when writing it to a file. Note that this will cause problems for some inputs, notably newline characters and others.

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()))
}

Export EC public key using Rust openssl

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.

RSA key pair signing and verification of JSON web tokens

I'm trying to sign and verify JSON web tokens in Rust using the openssl and jsonwebtoken crates.
use serde::{Deserialize, Serialize};
use jsonwebtoken::{ encode, Header, EncodingKey, Algorithm };
use jsonwebtoken::errors;
#[derive(Serialize, Deserialize)]
pub struct Claims {
email: String
}
pub fn mk_token (claims: &Claims, encoding_key: &EncodingKey) -> Result<String, errors::Error> {
Ok(encode(&Header::new(Algorithm::RS256), claims, encoding_key)?)
}
#[cfg(test)]
mod tests {
use super::*;
use jsonwebtoken::{ decode, DecodingKey, Validation };
use openssl::rsa::Rsa;
use openssl::pkey::{ PKey };
#[test]
fn test_mk_token() {
let rsa = Rsa::generate(2048).unwrap();
let pkey = PKey::from_rsa(rsa).unwrap();
let private_der = pkey.private_key_to_der().unwrap();
let public_der = pkey.public_key_to_der().unwrap();
let encoding_key = EncodingKey::from_rsa_der(&private_der);
let decoding_key = DecodingKey::from_rsa_der(&public_der);
let test_claims = Claims {
email: "a#b.c".to_string()
};
let token = match mk_token(&test_claims, &encoding_key) {
Ok(t) => t,
Err(_) => {
panic!()
}
};
let token_data = match decode::<Claims>(
&token,
&decoding_key,
&Validation::new(Algorithm::RS256)
) {
Ok(t) => t,
Err(e) => {
eprintln!("{}", e);
panic!("Could not decode")
}
};
println!("{}", token_data.claims.email)
}
}
The test reports an InvalidSignature error, but I really can't understand why.
How to sign and verify json web tokens with DER format keys?
:) this might few years late, but might help others stuck similarly
Use the pem to get the public key to be used during decoding.
Here's the rust snippet
let rsa = Rsa::generate(2048).unwrap();
let pkey = PKey::from_rsa(rsa).unwrap();
let public_pem = pkey.public_key_to_pem().unwrap();
let decoding_key = DecodingKey::from_rsa_pem(&public_pem).unwrap();
let token_data = match decode::<Claims>(
token,
&decoding_key,
&Validation::new(Algorithm::RS256)
) {
Ok(c) => c,
Err(err) => {
println!("err: {:?}", err.kind());
panic!()
}
};

Sending "application/x-www-form-urlencoded" data in place of JSON over network

The definition of struct which I use to serialize over network
pub struct NetworkData {
id: String,
status: String,
details: <Data Structure>,
}
Now there's a function which accepts this structure, serializes it
and sends over the network.
fn send_data(data: NetworkData ...) -> ... {
let data = serde_json::to_string(&data).expect("serialize issue");
let mut request = Request::new(reqwest::Method::POST, url);
*request.body_mut() = Some(data.into());
self.inner
.execute(request)
...
}
Now I want to send "x-www-form-urlencoded" data over network which should
change this function as follows :-
fn send_data(data: NetworkData ...) -> ... {
// How should I change this?????
//let data = serde_json::to_string(&data).expect("serialize issue");
let mut request = Request::new(reqwest::Method::POST, url);
let content_type = HeaderValue::from_str(&format!("{}", "application/x-www-form-urlencoded",))
.expect("Header value creation bug");
request
.headers_mut()
.insert(header::CONTENT_TYPE, content_type);
*request.body_mut() = Some(data.into());
self.inner
.execute(request)
...
}
But how should I organize my "data" to fit into this picture.
You can most likely use the serde_urlencoded crate in exactly the same way you did with the JSON.
I have no idea what your <Data Structure> looks like, since you haven't provided it, but the serde_urlencoded crate only supports primitive types, so if you have more fancy things, you'll have to come up with your own transformation; x-www-form-urlencoded is just a set of key=value pairs. Anyway, here's a working sample:
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct NetworkData {
id: String,
status: String,
data: u32,
}
fn main() {
let data = NetworkData {
id: "ID".into(),
status: "Status".into(),
data: 42,
};
let data = serde_urlencoded::to_string(&data).expect("serialize issue");
println!("{}", data);
}
playground

Resources