Skip to content

Commit

Permalink
Merge pull request #5 from polyphony-chat/4-openssl
Browse files Browse the repository at this point in the history
Basic CSR generation
  • Loading branch information
bitfl0wer authored Mar 4, 2024
2 parents be1be02 + 568a52b commit 7b653ef
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 74 deletions.
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ crate-type = ["rlib", "cdylib", "staticlib"]
der = { version = "0.7.8", features = ["pem"] }
spki = "0.7.3"
thiserror = "1.0.57"
x509-cert = { version = "0.2.5", default-features = false, features = ["pem"] }
openssl = "0.10.64"
x509-cert = { version = "0.2.5", default-features = false }

[dev-dependencies]
ed25519-dalek = { version = "2.1.1", features = ["rand_core", "signature"] }
Expand Down
14 changes: 14 additions & 0 deletions examples/ed25519_basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,20 @@ impl Signature for Ed25519Signature {
parameters: None,
}
}

fn from_bitstring(signature: &[u8]) -> Self {
let mut signature_vec = signature.to_vec();
signature_vec.resize(64, 0);
let signature_array: [u8; 64] = {
let mut array = [0; 64];
array.copy_from_slice(&signature_vec[..]);
array
};
Self {
signature: Ed25519DalekSignature::from_bytes(&signature_array),
algorithm: Self::algorithm_identifier(),
}
}
}

// The `SignatureBitStringEncoding` trait is used to convert a signature to a bit string. We implement
Expand Down
43 changes: 38 additions & 5 deletions examples/ed25519_csr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,19 @@ use polyproto::signature::Signature;
use rand::rngs::OsRng;
use spki::{AlgorithmIdentifierOwned, ObjectIdentifier, SignatureBitStringEncoding};
use thiserror::Error;
use x509_cert::attr::Attributes;
use x509_cert::name::RdnSequence;
use x509_cert::request::CertReq;

/// The following example uses the same setup as in ed25519_basic.rs, but in its main method, it
/// creates a certificate signing request (CSR) and writes it to a file. The CSR is created from a
/// polyproto ID CSR, which is a wrapper around a PKCS #10 CSR.
///
/// If you have openssl installed, you can inspect the CSR by running:
///
/// ```sh
/// openssl req -in cert.csr -verify
/// ```
fn main() {
let mut csprng = rand::rngs::OsRng;
Expand All @@ -23,13 +35,20 @@ fn main() {
println!();

let _csr = polyproto::certs::idcsr::IdCsr::new(
RdnSequence::from_str("CN=flori,DC=www,DC=polyphony,DC=chat").unwrap(),
priv_key,
SessionId::new(Ia5String::try_from(String::from("value")).unwrap()).unwrap(),
&RdnSequence::from_str("CN=flori,DC=www,DC=polyphony,DC=chat,[email protected],uniqueIdentifier=client1").unwrap(),
&priv_key,
&Attributes::new(),
)
.unwrap()
.to_der()
.unwrap();

let certrequest = CertReq::from(_csr.try_into().unwrap());
println!("Certrequest der bytes: {:?}", certrequest.to_der().unwrap());
let data = certrequest.to_der().unwrap();
let file_name_with_extension = "cert.csr";
std::fs::write(file_name_with_extension, &data).unwrap();

// TODO: The attributes are still missing. CA Certificates and Actor Certificates should have
// their respective set of capabilities
}

// As mentioned in the README, we start by implementing the signature trait.
Expand Down Expand Up @@ -62,6 +81,20 @@ impl Signature for Ed25519Signature {
parameters: None,
}
}

fn from_bitstring(signature: &[u8]) -> Self {
let mut signature_vec = signature.to_vec();
signature_vec.resize(64, 0);
let signature_array: [u8; 64] = {
let mut array = [0; 64];
array.copy_from_slice(&signature_vec[..]);
array
};
Self {
signature: Ed25519DalekSignature::from_bytes(&signature_array),
algorithm: Self::algorithm_identifier(),
}
}
}

// The `SignatureBitStringEncoding` trait is used to convert a signature to a bit string. We implement
Expand Down
134 changes: 68 additions & 66 deletions src/certs/idcsr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

use std::marker::PhantomData;

use der::asn1::{BitString, Uint};
use der::{Decode, Encode, Length};
use der::asn1::{BitString, SetOfVec, Uint};
use der::{Decode, Encode};
use spki::{AlgorithmIdentifierOwned, SubjectPublicKeyInfoOwned};
use x509_cert::attr::Attributes;
use x509_cert::name::Name;
use x509_cert::request::{CertReq, CertReqInfo};

use crate::key::{PrivateKey, PublicKey};
use crate::signature::Signature;
Expand Down Expand Up @@ -53,28 +55,16 @@ impl<S: Signature> IdCsr<S> {
/// - **subject_unique_id**: [Uint], subject (actor) session ID. MUST NOT exceed 32 characters
/// in length.
pub fn new(
subject: Name,
signing_key: impl PrivateKey<S>,
subject_session_id: SessionId,
subject: &Name,
signing_key: &impl PrivateKey<S>,
attributes: &Attributes,
) -> Result<IdCsr<S>, Error> {
subject.validate()?;
subject_session_id.validate()?;
let inner_csr =
IdCsrInner::<S>::new(subject, signing_key.pubkey(), subject_session_id.clone())?;

let version_bytes = Uint::new(&[inner_csr.version as u8])?.to_der()?;
let subject_bytes = inner_csr.subject.to_der()?;
let spki_bytes =
SubjectPublicKeyInfoOwned::from(inner_csr.subject_public_key_info.clone()).to_der()?;
let session_id_bytes = subject_session_id.to_der()?;

let mut to_sign = Vec::new();
to_sign.extend(version_bytes);
to_sign.extend(subject_bytes);
to_sign.extend(spki_bytes);
to_sign.extend(session_id_bytes);

let signature = signing_key.sign(&to_sign);
let inner_csr = IdCsrInner::<S>::new(subject, signing_key.pubkey(), attributes)?;
let cert_req_info = CertReqInfo::from(inner_csr);
let signature = signing_key.sign(&cert_req_info.to_der()?);
let inner_csr = IdCsrInner::<S>::try_from(cert_req_info)?;

let signature_algorithm = S::algorithm_identifier();

Ok(IdCsr {
Expand All @@ -86,13 +76,11 @@ impl<S: Signature> IdCsr<S> {

pub fn valid_actor_csr(&self) -> Result<(), Error> {
self.inner_csr.subject.validate()?;
self.inner_csr.subject_session_id.validate()?;
todo!()
}

pub fn valid_home_server_csr(&self) -> Result<(), Error> {
self.inner_csr.subject.validate()?;
self.inner_csr.subject_session_id.validate()?;
todo!()
}
}
Expand All @@ -115,8 +103,9 @@ pub struct IdCsrInner<S: Signature> {
pub subject: Name,
/// The subjects' public key and related metadata.
pub subject_public_key_info: PublicKeyInfo,
/// The session ID of the client. No two valid certificates may exist for one session ID.
pub subject_session_id: SessionId,
/// attributes is a collection of attributes providing additional
/// information about the subject of the certificate.
pub attributes: Attributes,
phantom_data: PhantomData<S>,
}

Expand All @@ -129,9 +118,9 @@ impl<S: Signature> IdCsrInner<S> {
///
/// The length of `subject_session_id` MUST NOT exceed 32.
pub fn new(
subject: Name,
subject: &Name,
public_key: &impl PublicKey<S>,
subject_session_id: SessionId,
attributes: &Attributes,
) -> Result<IdCsrInner<S>, Error> {
subject.validate()?;

Expand All @@ -142,59 +131,72 @@ impl<S: Signature> IdCsrInner<S> {
)?,
};

let subject = subject.clone();
let attributes = attributes.clone();

Ok(IdCsrInner {
version: PkcsVersion::V1,
subject,
subject_public_key_info,
subject_session_id,
attributes,
phantom_data: PhantomData,
})
}
}

impl<S: Signature> Encode for IdCsrInner<S> {
// TODO: Test this
fn encoded_len(&self) -> der::Result<Length> {
let len_version = Uint::new(&[self.version as u8])?.encoded_len()?;
let len_subject = self.subject.encoded_len()?;
let spki_converted: SubjectPublicKeyInfoOwned = self.subject_public_key_info.clone().into();
let len_spki = spki_converted.encoded_len()?;
let len_ssid = self.subject_session_id.encoded_len()?;
len_spki + len_subject + len_ssid + len_version
}
impl<S: Signature> TryFrom<CertReq> for IdCsr<S> {
type Error = Error;

// TODO: Test this
fn encode(&self, encoder: &mut impl der::Writer) -> der::Result<()> {
let uint_version = Uint::new(&[self.version as u8])?;
let spki_converted: SubjectPublicKeyInfoOwned = self.subject_public_key_info.clone().into();
uint_version.encode(encoder)?;
self.subject.encode(encoder)?;
spki_converted.encode(encoder)?;
self.subject_session_id.encode(encoder)?;
Ok(())
fn try_from(value: CertReq) -> Result<Self, Error> {
Ok(IdCsr {
inner_csr: IdCsrInner::try_from(value.info)?,
signature_algorithm: value.algorithm,
// TODO: raw_bytes() or as_bytes()?
signature: S::from_bitstring(value.signature.raw_bytes()),
})
}
}

impl<S: Signature> Encode for IdCsr<S> {
// TODO: Test this
fn encoded_len(&self) -> der::Result<Length> {
let len_inner = self.inner_csr.encoded_len()?;
let len_signature_algorithm = AlgorithmIdentifierOwned {
oid: self.signature_algorithm.oid,
parameters: self.signature_algorithm.parameters.clone(),
}
.encoded_len()?;
let len_signature = self.signature.to_bitstring()?.encoded_len()?;
len_inner + len_signature_algorithm + len_signature
impl<S: Signature> TryFrom<CertReqInfo> for IdCsrInner<S> {
type Error = Error;

fn try_from(value: CertReqInfo) -> Result<Self, Self::Error> {
let rdn_sequence = value.subject;
rdn_sequence.validate()?;
let public_key = PublicKeyInfo {
algorithm: value.public_key.algorithm,
public_key_bitstring: value.public_key.subject_public_key,
};

Ok(IdCsrInner {
version: PkcsVersion::V1,
subject: rdn_sequence,
subject_public_key_info: public_key,
attributes: value.attributes,
phantom_data: PhantomData,
})
}
}

impl<S: Signature> TryFrom<IdCsr<S>> for CertReq {
type Error = Error;

// TODO: Test this
fn encode(&self, encoder: &mut impl der::Writer) -> der::Result<()> {
self.inner_csr.encode(encoder)?;
self.signature_algorithm.clone().encode(encoder)?;
self.signature.to_bitstring()?.encode(encoder)?;
Ok(())
fn try_from(value: IdCsr<S>) -> Result<Self, Self::Error> {
Ok(CertReq {
info: value.inner_csr.into(),
algorithm: value.signature_algorithm,
signature: value.signature.to_bitstring()?,
})
}
}

//TODO: Implement decode trait
impl<S: Signature> From<IdCsrInner<S>> for CertReqInfo {
fn from(value: IdCsrInner<S>) -> Self {
CertReqInfo {
version: x509_cert::request::Version::V1,
subject: value.subject,
public_key: value.subject_public_key_info.into(),
attributes: SetOfVec::new(),
}
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ use std::fmt::Debug;

use thiserror::Error;

#[derive(Error, Debug, PartialEq, Clone)]
#[derive(Error, Debug, Clone)]
pub enum Error {
#[error("Conversion from TbsCertificate to IdCertTbs failed")]
TbsCertToIdCert(#[from] TbsCertToIdCert),
Expand Down
2 changes: 2 additions & 0 deletions src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ pub trait Signature: PartialEq + Eq + SignatureBitStringEncoding {
fn as_signature(&self) -> &Self::Signature;
/// The [AlgorithmIdentifierOwned] associated with this signature
fn algorithm_identifier() -> AlgorithmIdentifierOwned;
/// From a bit string signature value, create a new [Self]
fn from_bitstring(signature: &[u8]) -> Self;
}

0 comments on commit 7b653ef

Please sign in to comment.