certs be good now (maybe)

This commit is contained in:
c0repwn3r 2023-02-27 10:04:10 -05:00
parent b8ca91f8e9
commit cf9efb0ccf
Signed by: core
GPG Key ID: FDBF740DADDCEECF
10 changed files with 99 additions and 74 deletions

46
Cargo.lock generated
View File

@ -172,15 +172,6 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
"generic-array",
]
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.10.3" version = "0.10.3"
@ -291,7 +282,7 @@ dependencies = [
"hmac", "hmac",
"percent-encoding", "percent-encoding",
"rand", "rand",
"sha2 0.10.6", "sha2",
"subtle", "subtle",
"time 0.3.17", "time 0.3.17",
"version_check", "version_check",
@ -515,7 +506,7 @@ version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
dependencies = [ dependencies = [
"block-buffer 0.10.3", "block-buffer",
"crypto-common", "crypto-common",
"subtle", "subtle",
] ]
@ -565,7 +556,7 @@ dependencies = [
"curve25519-dalek 4.0.0-rc.1", "curve25519-dalek 4.0.0-rc.1",
"ed25519", "ed25519",
"serde", "serde",
"sha2 0.10.6", "sha2",
"zeroize", "zeroize",
] ]
@ -1862,19 +1853,6 @@ dependencies = [
"digest 0.10.6", "digest 0.10.6",
] ]
[[package]]
name = "sha2"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [
"block-buffer 0.9.0",
"cfg-if",
"cpufeatures",
"digest 0.9.0",
"opaque-debug",
]
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.6" version = "0.10.6"
@ -1886,16 +1864,6 @@ dependencies = [
"digest 0.10.6", "digest 0.10.6",
] ]
[[package]]
name = "sha256"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "328169f167261957e83d82be47f9e36629e257c62308129033d7f7e7c173d180"
dependencies = [
"hex",
"sha2 0.9.9",
]
[[package]] [[package]]
name = "sharded-slab" name = "sharded-slab"
version = "0.1.4" version = "0.1.4"
@ -2021,7 +1989,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"sha1", "sha1",
"sha2 0.10.6", "sha2",
"smallvec", "smallvec",
"sqlformat", "sqlformat",
"sqlx-rt", "sqlx-rt",
@ -2044,7 +2012,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"sha2 0.10.6", "sha2",
"sqlx-core", "sqlx-core",
"sqlx-rt", "sqlx-rt",
"syn", "syn",
@ -2352,7 +2320,7 @@ dependencies = [
"qrcodegen", "qrcodegen",
"rand", "rand",
"sha1", "sha1",
"sha2 0.10.6", "sha2",
"url", "url",
"urlencoding", "urlencoding",
] ]
@ -2457,7 +2425,7 @@ dependencies = [
"ipnet", "ipnet",
"pem", "pem",
"quick-protobuf", "quick-protobuf",
"sha256", "sha2",
"x25519-dalek", "x25519-dalek",
] ]

View File

@ -13,4 +13,4 @@ ed25519-dalek = "2.0.0-pre.0"
ipnet = "2.7.1" ipnet = "2.7.1"
quick-protobuf = "0.8.1" quick-protobuf = "0.8.1"
hex = "0.4.3" hex = "0.4.3"
sha256 = "1.1.2" sha2 = "0.10.6"

1
trifid-pki/bad.hex.crt Normal file
View File

@ -0,0 +1 @@
0aaa010a0774657374696e67121b8182845080feffff0f828284508080fcff0f83828450808080f80f1a1b8182844880ffffff0f8282844880feffff0f838284488080fcff0f220b746573742d67726f757031220b746573742d67726f757032220b746573742d67726f75703328888cf39f0630808df39f063a20313233343536373839306162636564666768696a3132333435363738393061624a101234567890abcedfabcd1234567890ab1220313233343536373839306162636564666768696a313233343536373839306162

1
trifid-pki/hex.crt Normal file
View File

@ -0,0 +1 @@
0a490a1765337465616d20496e7465726e616c204e6574776f726b28959ebf9c06309585c4ab063a20afa70a07c8a639f10e7ed97c438eda027ffce5358fc451d07c6f05d04b0128e740011240740f1efa96432a7372321f9fa697674a8d3caf078262c3ac7769b7f961971ff2b0ee544810a6f15d266a37c49c886f70bdbca67a917b14c63c3ab3525d5a0900

View File

@ -0,0 +1,5 @@
-----BEGIN NEBULA CERTIFICATE-----
CkkKF2UzdGVhbSBJbnRlcm5hbCBOZXR3b3JrKJWev5wGMJWFxKsGOiCvpwoHyKY5
8Q5+2XxDjtoCf/zlNY/EUdB8bwXQSwEo50ABEkB0Dx76lkMqc3IyH5+ml2dKjTyv
B4Jiw6x3abf5YZcf8rDuVEgQpvFdJmo3xJyIb3C9vKZ6kXsUxjw6s1JdWgkA
-----END NEBULA CERTIFICATE-----

View File

@ -10,11 +10,12 @@ use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
use ed25519_dalek::ed25519::pkcs8::spki::der::Encode; use ed25519_dalek::ed25519::pkcs8::spki::der::Encode;
use ipnet::{Ipv4Net}; use ipnet::{Ipv4Net};
use pem::Pem; use pem::Pem;
use quick_protobuf::{BytesReader, MessageRead, Writer}; use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer};
use sha256::digest; use sha2::Sha256;
use x25519_dalek::{PublicKey, StaticSecret}; use x25519_dalek::{PublicKey, StaticSecret};
use crate::ca::NebulaCAPool; use crate::ca::NebulaCAPool;
use crate::cert_codec::{RawNebulaCertificate, RawNebulaCertificateDetails}; use crate::cert_codec::{RawNebulaCertificate, RawNebulaCertificateDetails};
use sha2::Digest;
/// The length, in bytes, of public keys /// The length, in bytes, of public keys
pub const PUBLIC_KEY_LENGTH: i32 = 32; pub const PUBLIC_KEY_LENGTH: i32 = 32;
@ -31,6 +32,7 @@ pub const ED25519_PRIVATE_KEY_BANNER: &str = "NEBULA ED25519 PRIVATE KEY";
pub const ED25519_PUBLIC_KEY_BANNER: &str = "NEBULA ED25519 PUBLIC KEY"; pub const ED25519_PUBLIC_KEY_BANNER: &str = "NEBULA ED25519 PUBLIC KEY";
/// A Nebula PKI certificate /// A Nebula PKI certificate
#[derive(Debug)]
pub struct NebulaCertificate { pub struct NebulaCertificate {
/// The signed data of this certificate /// The signed data of this certificate
pub details: NebulaCertificateDetails, pub details: NebulaCertificateDetails,
@ -39,6 +41,7 @@ pub struct NebulaCertificate {
} }
/// The signed details contained in a Nebula PKI certificate /// The signed details contained in a Nebula PKI certificate
#[derive(Debug)]
pub struct NebulaCertificateDetails { pub struct NebulaCertificateDetails {
/// The name of the identity this certificate was issued for /// The name of the identity this certificate was issued for
pub name: String, pub name: String,
@ -107,6 +110,26 @@ fn map_cidr_pairs(pairs: &[u32]) -> Result<Vec<Ipv4Net>, Box<dyn Error>> {
Ok(res_vec) Ok(res_vec)
} }
impl Display for NebulaCertificate {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "NebulaCertificate {{")?;
writeln!(f, " Details {{")?;
writeln!(f, " Name: {}", self.details.name)?;
writeln!(f, " Ips: {:?}", self.details.ips)?;
writeln!(f, " Subnets: {:?}", self.details.subnets)?;
writeln!(f, " Groups: {:?}", self.details.groups)?;
writeln!(f, " Not before: {:?}", self.details.not_before)?;
writeln!(f, " Not after: {:?}", self.details.not_after)?;
writeln!(f, " Is CA: {}", self.details.is_ca)?;
writeln!(f, " Issuer: {}", self.details.issuer)?;
writeln!(f, " Public key: {}", hex::encode(self.details.public_key))?;
writeln!(f, " }}")?;
writeln!(f, " Fingerprint: {}", self.sha256sum().unwrap())?;
writeln!(f, " Signature: {}", hex::encode(self.signature.clone()))?;
writeln!(f, "}}")
}
}
/// Given a protobuf-encoded certificate bytearray, deserialize it into a `NebulaCertificate` object. /// Given a protobuf-encoded certificate bytearray, deserialize it into a `NebulaCertificate` object.
/// # Errors /// # Errors
/// This function will return an error if there is a protobuf parsing error, or if the certificate data is invalid. /// This function will return an error if there is a protobuf parsing error, or if the certificate data is invalid.
@ -442,7 +465,9 @@ impl NebulaCertificate {
let mut out = vec![]; let mut out = vec![];
let mut writer = Writer::new(&mut out); let mut writer = Writer::new(&mut out);
writer.write_message(&raw_cert)?; raw_cert.write_message(&mut writer)?;
println!("{:?}", hex::encode(out.clone()));
Ok(out) Ok(out)
} }
@ -465,7 +490,10 @@ impl NebulaCertificate {
pub fn sha256sum(&self) -> Result<String, Box<dyn Error>> { pub fn sha256sum(&self) -> Result<String, Box<dyn Error>> {
let pbuf_bytes = self.serialize()?; let pbuf_bytes = self.serialize()?;
Ok(digest(&pbuf_bytes[..])) let mut hasher = Sha256::new();
hasher.update(pbuf_bytes);
Ok(hex::encode(hasher.finalize()))
} }
} }

View File

@ -7,7 +7,7 @@
#![allow(unknown_lints)] #![allow(unknown_lints)]
#![allow(clippy::all)] #![allow(clippy::all)]
#![cfg_attr(rustfmt, rustfmt_skip)] #![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(clippy::wildcard_imports)] #![allow(clippy::pedantic)]
use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result};
use quick_protobuf::sizeofs::*; use quick_protobuf::sizeofs::*;
@ -36,17 +36,17 @@ impl<'a> MessageRead<'a> for RawNebulaCertificate {
} }
impl MessageWrite for RawNebulaCertificate { impl MessageWrite for RawNebulaCertificate {
fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
if let Some(ref s) = self.Details { w.write_with_tag(10, |w| w.write_message(s))?; }
if !self.Signature.is_empty() { w.write_with_tag(18, |w| w.write_bytes(&**&self.Signature))?; }
Ok(())
}
fn get_size(&self) -> usize { fn get_size(&self) -> usize {
0 0
+ self.Details.as_ref().map_or(0, |m| 1 + sizeof_len((m).get_size())) + self.Details.as_ref().map_or(0, |m| 1 + sizeof_len((m).get_size()))
+ if self.Signature.is_empty() { 0 } else { 1 + sizeof_len((&self.Signature).len()) } + if self.Signature.is_empty() { 0 } else { 1 + sizeof_len((&self.Signature).len()) }
} }
fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
if let Some(ref s) = self.Details { w.write_with_tag(10, |w| w.write_message(s))?; }
if !self.Signature.is_empty() { w.write_with_tag(18, |w| w.write_bytes(&**&self.Signature))?; }
Ok(())
}
} }
#[allow(clippy::derive_partial_eq_without_eq)] #[allow(clippy::derive_partial_eq_without_eq)]
@ -85,12 +85,24 @@ impl<'a> MessageRead<'a> for RawNebulaCertificateDetails {
} }
} }
#[allow(clippy::cast_sign_loss)]
impl MessageWrite for RawNebulaCertificateDetails { impl MessageWrite for RawNebulaCertificateDetails {
fn get_size(&self) -> usize {
0
+ if self.Name == String::default() { 0 } else { 1 + sizeof_len((&self.Name).len()) }
+ if self.Ips.is_empty() { 0 } else { 1 + sizeof_len(self.Ips.iter().map(|s| sizeof_varint(*(s) as u64)).sum::<usize>()) }
+ if self.Subnets.is_empty() { 0 } else { 1 + sizeof_len(self.Subnets.iter().map(|s| sizeof_varint(*(s) as u64)).sum::<usize>()) }
+ self.Groups.iter().map(|s| 1 + sizeof_len((s).len())).sum::<usize>()
+ if self.NotBefore == 0i64 { 0 } else { 1 + sizeof_varint(*(&self.NotBefore) as u64) }
+ if self.NotAfter == 0i64 { 0 } else { 1 + sizeof_varint(*(&self.NotAfter) as u64) }
+ if self.PublicKey.is_empty() { 0 } else { 1 + sizeof_len((&self.PublicKey).len()) }
+ if self.IsCA == false { 0 } else { 1 + sizeof_varint(*(&self.IsCA) as u64) }
+ if self.Issuer.is_empty() { 0 } else { 1 + sizeof_len((&self.Issuer).len()) }
}
fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> { fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
if self.Name != String::default() { w.write_with_tag(10, |w| w.write_string(&**&self.Name))?; } if self.Name != String::default() { w.write_with_tag(10, |w| w.write_string(&**&self.Name))?; }
w.write_packed_with_tag(18, &self.Ips, |w, m| w.write_uint32(*m), &|m| sizeof_varint(u64::from(*(m))))?; w.write_packed_with_tag(18, &self.Ips, |w, m| w.write_uint32(*m), &|m| sizeof_varint(*(m) as u64))?;
w.write_packed_with_tag(26, &self.Subnets, |w, m| w.write_uint32(*m), &|m| sizeof_varint(u64::from(*(m))))?; w.write_packed_with_tag(26, &self.Subnets, |w, m| w.write_uint32(*m), &|m| sizeof_varint(*(m) as u64))?;
for s in &self.Groups { w.write_with_tag(34, |w| w.write_string(&**s))?; } for s in &self.Groups { w.write_with_tag(34, |w| w.write_string(&**s))?; }
if self.NotBefore != 0i64 { w.write_with_tag(40, |w| w.write_int64(*&self.NotBefore))?; } if self.NotBefore != 0i64 { w.write_with_tag(40, |w| w.write_int64(*&self.NotBefore))?; }
if self.NotAfter != 0i64 { w.write_with_tag(48, |w| w.write_int64(*&self.NotAfter))?; } if self.NotAfter != 0i64 { w.write_with_tag(48, |w| w.write_int64(*&self.NotAfter))?; }
@ -99,18 +111,5 @@ impl MessageWrite for RawNebulaCertificateDetails {
if !self.Issuer.is_empty() { w.write_with_tag(74, |w| w.write_bytes(&**&self.Issuer))?; } if !self.Issuer.is_empty() { w.write_with_tag(74, |w| w.write_bytes(&**&self.Issuer))?; }
Ok(()) Ok(())
} }
fn get_size(&self) -> usize {
0
+ if self.Name == String::default() { 0 } else { 1 + sizeof_len((&self.Name).len()) }
+ if self.Ips.is_empty() { 0 } else { 1 + sizeof_len(self.Ips.iter().map(|s| sizeof_varint(u64::from(*(s)))).sum::<usize>()) }
+ if self.Subnets.is_empty() { 0 } else { 1 + sizeof_len(self.Subnets.iter().map(|s| sizeof_varint(u64::from(*(s)))).sum::<usize>()) }
+ self.Groups.iter().map(|s| 1 + sizeof_len((s).len())).sum::<usize>()
+ if self.NotBefore == 0i64 { 0 } else { 1 + sizeof_varint(*(&self.NotBefore) as u64) }
+ if self.NotAfter == 0i64 { 0 } else { 1 + sizeof_varint(*(&self.NotAfter) as u64) }
+ if self.PublicKey.is_empty() { 0 } else { 1 + sizeof_len((&self.PublicKey).len()) }
+ if self.IsCA == false { 0 } else { 1 + sizeof_varint(u64::from(*(&self.IsCA))) }
+ if self.Issuer.is_empty() { 0 } else { 1 + sizeof_len((&self.Issuer).len()) }
}
} }

View File

@ -17,6 +17,8 @@
#![allow(clippy::module_name_repetitions)] #![allow(clippy::module_name_repetitions)]
extern crate core;
pub mod ca; pub mod ca;
pub mod cert; pub mod cert;
pub(crate) mod cert_codec; pub(crate) mod cert_codec;

View File

@ -1,17 +1,22 @@
#![allow(clippy::unwrap_used)] #![allow(clippy::unwrap_used)]
#![allow(clippy::expect_used)] #![allow(clippy::expect_used)]
use std::fs;
use crate::netmask; use crate::netmask;
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use std::time::{Duration, SystemTime}; use std::ops::Add;
use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH};
use ipnet::Ipv4Net; use ipnet::Ipv4Net;
use crate::cert::{deserialize_nebula_certificate, NebulaCertificate, NebulaCertificateDetails}; use crate::cert::{deserialize_nebula_certificate, NebulaCertificate, NebulaCertificateDetails};
use std::str::FromStr; use std::str::FromStr;
/// This is a cert that we (e3team) actually use in production, and it's a known-good certificate.
pub const KNOWN_GOOD_CERT: &[u8; 258] = b"-----BEGIN NEBULA CERTIFICATE-----\nCkkKF2UzdGVhbSBJbnRlcm5hbCBOZXR3b3JrKJWev5wGMJWFxKsGOiCvpwoHyKY5\n8Q5+2XxDjtoCf/zlNY/EUdB8bwXQSwEo50ABEkB0Dx76lkMqc3IyH5+ml2dKjTyv\nB4Jiw6x3abf5YZcf8rDuVEgQpvFdJmo3xJyIb3C9vKZ6kXsUxjw6s1JdWgkA\n-----END NEBULA CERTIFICATE-----";
#[test] #[test]
fn certificate_serialization() { fn certificate_serialization() {
let before = SystemTime::now() - Duration::from_secs(60); let before = round_systime_to_secs(SystemTime::now() - Duration::from_secs(60)).unwrap();
let after = SystemTime::now() + Duration::from_secs(60); let after = round_systime_to_secs(SystemTime::now() + Duration::from_secs(60)).unwrap();
let pub_key = b"1234567890abcedfghij1234567890ab"; let pub_key = b"1234567890abcedfghij1234567890ab";
let cert = NebulaCertificate { let cert = NebulaCertificate {
@ -20,10 +25,10 @@ fn certificate_serialization() {
ips: vec![ ips: vec![
netmask!("10.1.1.1", "255.255.255.0"), netmask!("10.1.1.1", "255.255.255.0"),
netmask!("10.1.1.2", "255.255.0.0"), netmask!("10.1.1.2", "255.255.0.0"),
netmask!("10.1.1.3", "255.0.255.0") netmask!("10.1.1.3", "255.0.0.0")
], ],
subnets: vec![ subnets: vec![
netmask!("9.1.1.1", "255.0.255.0"), netmask!("9.1.1.1", "255.255.255.128"),
netmask!("9.1.1.2", "255.255.255.0"), netmask!("9.1.1.2", "255.255.255.0"),
netmask!("9.1.1.3", "255.255.0.0") netmask!("9.1.1.3", "255.255.0.0")
], ],
@ -32,13 +37,15 @@ fn certificate_serialization() {
not_after: after, not_after: after,
public_key: *pub_key, public_key: *pub_key,
is_ca: false, is_ca: false,
issuer: "1234567890abcedfghij1234567890ab".to_string(), issuer: "1234567890abcedfabcd1234567890ab".to_string(),
}, },
signature: b"1234567890abcedfghij1234567890ab".to_vec(), signature: b"1234567890abcedfghij1234567890ab".to_vec(),
}; };
let bytes = cert.serialize().unwrap(); let bytes = cert.serialize().unwrap();
fs::write("bad.hex.crt", hex::encode(bytes.clone())).unwrap();
let deserialized = deserialize_nebula_certificate(&bytes).unwrap(); let deserialized = deserialize_nebula_certificate(&bytes).unwrap();
/* /*
assert.Equal(t, nc.Details.Name, nc2.Details.Name) assert.Equal(t, nc.Details.Name, nc2.Details.Name)
@ -49,7 +56,9 @@ assert.Equal(t, nc.Details.Name, nc2.Details.Name)
*/ */
assert_eq!(cert.signature, deserialized.signature); assert_eq!(cert.signature, deserialized.signature);
assert_eq!(cert.details.name, deserialized.details.name); assert_eq!(cert.details.name, deserialized.details.name);
assert_eq!(cert.details.not_before, deserialized.details.not_after); assert_eq!(cert.details.not_before, deserialized.details.not_before);
assert_eq!(cert.details.not_after, deserialized.details.not_after);
assert_eq!(cert.details.public_key, deserialized.details.public_key);
} }
#[macro_export] #[macro_export]
@ -57,4 +66,9 @@ macro_rules! netmask {
($ip:expr,$mask:expr) => { ($ip:expr,$mask:expr) => {
Ipv4Net::with_netmask(Ipv4Addr::from_str($ip).unwrap(), Ipv4Addr::from_str($mask).unwrap()).unwrap() Ipv4Net::with_netmask(Ipv4Addr::from_str($ip).unwrap(), Ipv4Addr::from_str($mask).unwrap()).unwrap()
}; };
}
fn round_systime_to_secs(time: SystemTime) -> Result<SystemTime, SystemTimeError> {
let secs = time.duration_since(UNIX_EPOCH)?.as_secs();
Ok(SystemTime::UNIX_EPOCH.add(Duration::from_secs(secs)))
} }

7
trifid-pki/test_cert.crt Normal file
View File

@ -0,0 +1,7 @@
-----BEGIN NEBULA CERTIFICATE-----
zwEKqgEKB3Rlc3RpbmcSG4GChFCA/v//D4KChFCAgPz/D4OChFCAgID4DxobgYKE
SID///8PgoKESID+//8Pg4KESICA/P8PIgt0ZXN0LWdyb3VwMSILdGVzdC1ncm91
cDIiC3Rlc3QtZ3JvdXAzKLDU8p8GMKjV8p8GOiAxMjM0NTY3ODkwYWJjZWRmZ2hp
ajEyMzQ1Njc4OTBhYkoQEjRWeJCrzt+rzRI0VniQqxIgMTIzNDU2Nzg5MGFiY2Vk
ZmdoaWoxMjM0NTY3ODkwYWI=
-----END NEBULA CERTIFICATE-----