1176 lines
37 KiB
Rust
1176 lines
37 KiB
Rust
#![allow(clippy::unwrap_used)]
|
|
#![allow(clippy::expect_used)]
|
|
|
|
use crate::ca::NebulaCAPool;
|
|
use crate::cert::{
|
|
deserialize_ed25519_private, deserialize_ed25519_public, deserialize_ed25519_public_many,
|
|
deserialize_nebula_certificate, deserialize_nebula_certificate_from_pem,
|
|
deserialize_x25519_private, deserialize_x25519_public, serialize_ed25519_private,
|
|
serialize_ed25519_public, serialize_x25519_private, serialize_x25519_public,
|
|
CertificateValidity, NebulaCertificate, NebulaCertificateDetails,
|
|
};
|
|
use crate::cert_codec::{RawNebulaCertificate, RawNebulaCertificateDetails};
|
|
use crate::netmask;
|
|
use ed25519_dalek::{SigningKey, VerifyingKey};
|
|
use ipnet::Ipv4Net;
|
|
use quick_protobuf::{MessageWrite, Writer};
|
|
use rand::rngs::OsRng;
|
|
use std::net::Ipv4Addr;
|
|
use std::ops::{Add, Sub};
|
|
use std::str::FromStr;
|
|
use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH};
|
|
|
|
/// 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]
|
|
fn certificate_serialization() {
|
|
let before = round_systime_to_secs(SystemTime::now() - Duration::from_secs(60)).unwrap();
|
|
let after = round_systime_to_secs(SystemTime::now() + Duration::from_secs(60)).unwrap();
|
|
let pub_key = b"1234567890abcedfghij1234567890ab";
|
|
|
|
let cert = NebulaCertificate {
|
|
details: NebulaCertificateDetails {
|
|
name: "testing".to_string(),
|
|
ips: vec![
|
|
netmask!("10.1.1.1", "255.255.255.0"),
|
|
netmask!("10.1.1.2", "255.255.0.0"),
|
|
netmask!("10.1.1.3", "255.0.0.0"),
|
|
],
|
|
subnets: vec![
|
|
netmask!("9.1.1.1", "255.255.255.128"),
|
|
netmask!("9.1.1.2", "255.255.255.0"),
|
|
netmask!("9.1.1.3", "255.255.0.0"),
|
|
],
|
|
groups: vec![
|
|
"test-group1".to_string(),
|
|
"test-group2".to_string(),
|
|
"test-group3".to_string(),
|
|
],
|
|
not_before: before,
|
|
not_after: after,
|
|
public_key: *pub_key,
|
|
is_ca: false,
|
|
issuer: "1234567890abcedfabcd1234567890ab".to_string(),
|
|
},
|
|
signature: b"1234567890abcedfghij1234567890ab".to_vec(),
|
|
};
|
|
|
|
let bytes = cert.serialize().unwrap();
|
|
|
|
let deserialized = deserialize_nebula_certificate(&bytes).unwrap();
|
|
|
|
assert_eq!(cert.signature, deserialized.signature);
|
|
assert_eq!(cert.details.name, deserialized.details.name);
|
|
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);
|
|
assert_eq!(cert.details.is_ca, deserialized.details.is_ca);
|
|
|
|
assert_eq!(cert.details.ips.len(), deserialized.details.ips.len());
|
|
for item in &cert.details.ips {
|
|
assert!(
|
|
deserialized.details.ips.contains(item),
|
|
"deserialized does not contain from source"
|
|
);
|
|
}
|
|
|
|
assert_eq!(
|
|
cert.details.subnets.len(),
|
|
deserialized.details.subnets.len()
|
|
);
|
|
for item in &cert.details.subnets {
|
|
assert!(
|
|
deserialized.details.subnets.contains(item),
|
|
"deserialized does not contain from source"
|
|
);
|
|
}
|
|
|
|
assert_eq!(cert.details.groups.len(), deserialized.details.groups.len());
|
|
for item in &cert.details.groups {
|
|
assert!(
|
|
deserialized.details.groups.contains(item),
|
|
"deserialized does not contain from source"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn certificate_serialization_pem() {
|
|
let before = round_systime_to_secs(SystemTime::now() - Duration::from_secs(60)).unwrap();
|
|
let after = round_systime_to_secs(SystemTime::now() + Duration::from_secs(60)).unwrap();
|
|
let pub_key = b"1234567890abcedfghij1234567890ab";
|
|
|
|
let cert = NebulaCertificate {
|
|
details: NebulaCertificateDetails {
|
|
name: "testing".to_string(),
|
|
ips: vec![
|
|
netmask!("10.1.1.1", "255.255.255.0"),
|
|
netmask!("10.1.1.2", "255.255.0.0"),
|
|
netmask!("10.1.1.3", "255.0.0.0"),
|
|
],
|
|
subnets: vec![
|
|
netmask!("9.1.1.1", "255.255.255.128"),
|
|
netmask!("9.1.1.2", "255.255.255.0"),
|
|
netmask!("9.1.1.3", "255.255.0.0"),
|
|
],
|
|
groups: vec![
|
|
"test-group1".to_string(),
|
|
"test-group2".to_string(),
|
|
"test-group3".to_string(),
|
|
],
|
|
not_before: before,
|
|
not_after: after,
|
|
public_key: *pub_key,
|
|
is_ca: false,
|
|
issuer: "1234567890abcedfabcd1234567890ab".to_string(),
|
|
},
|
|
signature: b"1234567890abcedfghij1234567890ab".to_vec(),
|
|
};
|
|
|
|
let bytes = cert.serialize_to_pem().unwrap();
|
|
|
|
let deserialized = deserialize_nebula_certificate_from_pem(&bytes).unwrap();
|
|
|
|
assert_eq!(cert.signature, deserialized.signature);
|
|
assert_eq!(cert.details.name, deserialized.details.name);
|
|
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);
|
|
assert_eq!(cert.details.is_ca, deserialized.details.is_ca);
|
|
|
|
assert_eq!(cert.details.ips.len(), deserialized.details.ips.len());
|
|
for item in &cert.details.ips {
|
|
assert!(
|
|
deserialized.details.ips.contains(item),
|
|
"deserialized does not contain from source"
|
|
);
|
|
}
|
|
|
|
assert_eq!(
|
|
cert.details.subnets.len(),
|
|
deserialized.details.subnets.len()
|
|
);
|
|
for item in &cert.details.subnets {
|
|
assert!(
|
|
deserialized.details.subnets.contains(item),
|
|
"deserialized does not contain from source"
|
|
);
|
|
}
|
|
|
|
assert_eq!(cert.details.groups.len(), deserialized.details.groups.len());
|
|
for item in &cert.details.groups {
|
|
assert!(
|
|
deserialized.details.groups.contains(item),
|
|
"deserialized does not contain from source"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn cert_signing() {
|
|
let before = round_systime_to_secs(SystemTime::now() - Duration::from_secs(60)).unwrap();
|
|
let after = round_systime_to_secs(SystemTime::now() + Duration::from_secs(60)).unwrap();
|
|
let pub_key = b"1234567890abcedfghij1234567890ab";
|
|
|
|
let mut cert = NebulaCertificate {
|
|
details: NebulaCertificateDetails {
|
|
name: "testing".to_string(),
|
|
ips: vec![
|
|
netmask!("10.1.1.1", "255.255.255.0"),
|
|
netmask!("10.1.1.2", "255.255.0.0"),
|
|
netmask!("10.1.1.3", "255.0.0.0"),
|
|
],
|
|
subnets: vec![
|
|
netmask!("9.1.1.1", "255.255.255.128"),
|
|
netmask!("9.1.1.2", "255.255.255.0"),
|
|
netmask!("9.1.1.3", "255.255.0.0"),
|
|
],
|
|
groups: vec![
|
|
"test-group1".to_string(),
|
|
"test-group2".to_string(),
|
|
"test-group3".to_string(),
|
|
],
|
|
not_before: before,
|
|
not_after: after,
|
|
public_key: *pub_key,
|
|
is_ca: false,
|
|
issuer: "1234567890abcedfabcd1234567890ab".to_string(),
|
|
},
|
|
signature: b"1234567890abcedfghij1234567890ab".to_vec(),
|
|
};
|
|
|
|
let mut csprng = OsRng;
|
|
let key = SigningKey::generate(&mut csprng);
|
|
|
|
assert!(cert.check_signature(&key.verifying_key()).is_err());
|
|
cert.sign(&key).unwrap();
|
|
assert!(cert.check_signature(&key.verifying_key()).unwrap());
|
|
}
|
|
|
|
#[test]
|
|
fn cert_expiry() {
|
|
let cert = NebulaCertificate {
|
|
details: NebulaCertificateDetails {
|
|
name: String::new(),
|
|
ips: vec![],
|
|
subnets: vec![],
|
|
groups: vec![],
|
|
not_before: SystemTime::now().sub(Duration::from_secs(60)),
|
|
not_after: SystemTime::now().add(Duration::from_secs(60)),
|
|
public_key: [0u8; 32],
|
|
is_ca: false,
|
|
issuer: String::new(),
|
|
},
|
|
signature: vec![],
|
|
};
|
|
|
|
assert!(cert.expired(SystemTime::now().add(Duration::from_secs(60 * 60 * 60))));
|
|
assert!(cert.expired(SystemTime::now().sub(Duration::from_secs(60 * 60 * 60))));
|
|
assert!(!cert.expired(SystemTime::now()));
|
|
}
|
|
|
|
#[test]
|
|
fn cert_display() {
|
|
let cert = NebulaCertificate {
|
|
details: NebulaCertificateDetails {
|
|
name: String::new(),
|
|
ips: vec![],
|
|
subnets: vec![],
|
|
groups: vec![],
|
|
not_before: SystemTime::now().sub(Duration::from_secs(60)),
|
|
not_after: SystemTime::now().add(Duration::from_secs(60)),
|
|
public_key: [0u8; 32],
|
|
is_ca: false,
|
|
issuer: String::new(),
|
|
},
|
|
signature: vec![],
|
|
};
|
|
println!("{cert}");
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn cert_deserialize_empty_bytes() {
|
|
deserialize_nebula_certificate(&[]).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn cert_deserialize_unpaired_ips() {
|
|
let broken_cert = RawNebulaCertificate {
|
|
Details: Some(RawNebulaCertificateDetails {
|
|
Name: String::new(),
|
|
Ips: vec![0],
|
|
Subnets: vec![],
|
|
Groups: vec![],
|
|
NotBefore: 0,
|
|
NotAfter: 0,
|
|
PublicKey: vec![],
|
|
IsCA: false,
|
|
Issuer: vec![],
|
|
}),
|
|
Signature: vec![],
|
|
};
|
|
let mut bytes = vec![];
|
|
let mut writer = Writer::new(&mut bytes);
|
|
broken_cert.write_message(&mut writer).unwrap();
|
|
|
|
deserialize_nebula_certificate(&bytes).unwrap_err();
|
|
}
|
|
|
|
#[test]
|
|
fn cert_deserialize_unpaired_subnets() {
|
|
let broken_cert = RawNebulaCertificate {
|
|
Details: Some(RawNebulaCertificateDetails {
|
|
Name: String::new(),
|
|
Ips: vec![],
|
|
Subnets: vec![0],
|
|
Groups: vec![],
|
|
NotBefore: 0,
|
|
NotAfter: 0,
|
|
PublicKey: vec![],
|
|
IsCA: false,
|
|
Issuer: vec![],
|
|
}),
|
|
Signature: vec![],
|
|
};
|
|
let mut bytes = vec![];
|
|
let mut writer = Writer::new(&mut bytes);
|
|
broken_cert.write_message(&mut writer).unwrap();
|
|
|
|
deserialize_nebula_certificate(&bytes).unwrap_err();
|
|
}
|
|
|
|
#[test]
|
|
fn cert_deserialize_wrong_pubkey_len() {
|
|
let broken_cert = RawNebulaCertificate {
|
|
Details: Some(RawNebulaCertificateDetails {
|
|
Name: String::new(),
|
|
Ips: vec![],
|
|
Subnets: vec![],
|
|
Groups: vec![],
|
|
NotBefore: 0,
|
|
NotAfter: 0,
|
|
PublicKey: vec![0u8; 31],
|
|
IsCA: false,
|
|
Issuer: vec![],
|
|
}),
|
|
Signature: vec![],
|
|
};
|
|
let mut bytes = vec![];
|
|
let mut writer = Writer::new(&mut bytes);
|
|
broken_cert.write_message(&mut writer).unwrap();
|
|
|
|
deserialize_nebula_certificate(&bytes).unwrap_err();
|
|
|
|
assert!(deserialize_nebula_certificate_from_pem(&[0u8; 32]).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn x25519_serialization() {
|
|
let bytes = [0u8; 32];
|
|
assert_eq!(
|
|
deserialize_x25519_private(&serialize_x25519_private(&bytes)).unwrap(),
|
|
bytes
|
|
);
|
|
assert!(deserialize_x25519_private(&[0u8; 32]).is_err());
|
|
assert_eq!(
|
|
deserialize_x25519_public(&serialize_x25519_public(&bytes)).unwrap(),
|
|
bytes
|
|
);
|
|
assert!(deserialize_x25519_public(&[0u8; 32]).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn ed25519_serialization() {
|
|
let bytes = [0u8; 64];
|
|
let bytes2 = [0u8; 32];
|
|
assert_eq!(
|
|
deserialize_ed25519_private(&serialize_ed25519_private(&bytes)).unwrap(),
|
|
bytes
|
|
);
|
|
assert!(deserialize_ed25519_private(&[0u8; 32]).is_err());
|
|
assert_eq!(
|
|
deserialize_ed25519_public(&serialize_ed25519_public(&bytes2)).unwrap(),
|
|
bytes2
|
|
);
|
|
assert!(deserialize_ed25519_public(&[0u8; 64]).is_err());
|
|
|
|
let mut bytes = vec![];
|
|
bytes.append(&mut serialize_ed25519_public(&[0u8; 32]));
|
|
bytes.append(&mut serialize_ed25519_public(&[1u8; 32]));
|
|
let deser = deserialize_ed25519_public_many(&bytes).unwrap();
|
|
assert_eq!(deser[0], [0u8; 32]);
|
|
assert_eq!(deser[1], [1u8; 32]);
|
|
|
|
bytes.append(&mut serialize_ed25519_public(&[1u8; 33]));
|
|
deserialize_ed25519_public_many(&bytes).unwrap_err();
|
|
}
|
|
|
|
#[test]
|
|
fn cert_verify() {
|
|
let (ca_cert, ca_key, _ca_pub) = test_ca_cert(
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 10)).unwrap(),
|
|
vec![],
|
|
vec![],
|
|
vec!["groupa".to_string()],
|
|
);
|
|
|
|
let (cert, _, _) = test_cert(
|
|
&ca_cert,
|
|
&ca_key,
|
|
SystemTime::now(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![],
|
|
vec![],
|
|
vec![],
|
|
);
|
|
|
|
let mut ca_pool = NebulaCAPool::new();
|
|
ca_pool
|
|
.add_ca_certificate(&ca_cert.serialize_to_pem().unwrap())
|
|
.unwrap();
|
|
|
|
let fingerprint = cert.sha256sum().unwrap();
|
|
ca_pool.blocklist_fingerprint(&fingerprint);
|
|
|
|
assert!(matches!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::Blocklisted
|
|
));
|
|
|
|
ca_pool.reset_blocklist();
|
|
|
|
assert!(matches!(
|
|
cert.verify(
|
|
SystemTime::now() + Duration::from_secs(60 * 60 * 60),
|
|
&ca_pool
|
|
)
|
|
.unwrap(),
|
|
CertificateValidity::RootCertExpired
|
|
));
|
|
assert!(matches!(
|
|
cert.verify(
|
|
SystemTime::now() + Duration::from_secs(60 * 60 * 6),
|
|
&ca_pool
|
|
)
|
|
.unwrap(),
|
|
CertificateValidity::CertExpired
|
|
));
|
|
|
|
let (cert_with_bad_group, _, _) = test_cert(
|
|
&ca_cert,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
in_a_minute(),
|
|
vec![],
|
|
vec![],
|
|
vec!["group-not-present on parent".to_string()],
|
|
);
|
|
assert_eq!(
|
|
cert_with_bad_group
|
|
.verify(SystemTime::now(), &ca_pool)
|
|
.unwrap(),
|
|
CertificateValidity::GroupNotPresentOnSigner
|
|
);
|
|
|
|
let (cert_with_good_group, _, _) = test_cert(
|
|
&ca_cert,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
in_a_minute(),
|
|
vec![],
|
|
vec![],
|
|
vec!["groupa".to_string()],
|
|
);
|
|
assert_eq!(
|
|
cert_with_good_group
|
|
.verify(SystemTime::now(), &ca_pool)
|
|
.unwrap(),
|
|
CertificateValidity::Ok
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn cert_verify_ip() {
|
|
let ca_ip_1 = Ipv4Net::from_str("10.0.0.0/16").unwrap();
|
|
let ca_ip_2 = Ipv4Net::from_str("192.168.0.0/24").unwrap();
|
|
|
|
let (ca, ca_key, _ca_pub) = test_ca_cert(
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 10)).unwrap(),
|
|
vec![ca_ip_1, ca_ip_2],
|
|
vec![],
|
|
vec![],
|
|
);
|
|
|
|
let ca_pem = ca.serialize_to_pem().unwrap();
|
|
|
|
let mut ca_pool = NebulaCAPool::new();
|
|
ca_pool.add_ca_certificate(&ca_pem).unwrap();
|
|
|
|
// ip is outside the network
|
|
let cip1 = netmask!("10.1.0.0", "255.255.255.0");
|
|
let cip2 = netmask!("192.198.0.1", "255.255.0.0");
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![cip1, cip2],
|
|
vec![],
|
|
vec![],
|
|
);
|
|
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::IPNotPresentOnSigner
|
|
);
|
|
|
|
// ip is outside the network - reversed order from above
|
|
let cip1 = netmask!("192.198.0.1", "255.255.255.0");
|
|
let cip2 = netmask!("10.1.0.0", "255.255.255.0");
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![cip1, cip2],
|
|
vec![],
|
|
vec![],
|
|
);
|
|
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::IPNotPresentOnSigner
|
|
);
|
|
|
|
// ip is within the network but mask is outside
|
|
let cip1 = netmask!("10.0.1.0", "255.254.0.0");
|
|
let cip2 = netmask!("192.168.0.1", "255.255.255.0");
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![cip1, cip2],
|
|
vec![],
|
|
vec![],
|
|
);
|
|
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::IPNotPresentOnSigner
|
|
);
|
|
|
|
// ip is within the network but mask is outside - reversed order from above
|
|
let cip1 = netmask!("192.168.0.1", "255.255.255.0");
|
|
let cip2 = netmask!("10.0.1.0", "255.254.0.0");
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![cip1, cip2],
|
|
vec![],
|
|
vec![],
|
|
);
|
|
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::IPNotPresentOnSigner
|
|
);
|
|
|
|
// ip and mask are within the network
|
|
let cip1 = netmask!("10.0.1.0", "255.255.0.0");
|
|
let cip2 = netmask!("192.168.0.1", "255.255.255.128");
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![cip1, cip2],
|
|
vec![],
|
|
vec![],
|
|
);
|
|
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::Ok
|
|
);
|
|
|
|
// Exact matches
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![ca_ip_1, ca_ip_2],
|
|
vec![],
|
|
vec![],
|
|
);
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::Ok
|
|
);
|
|
|
|
// Exact matches reversed
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![ca_ip_2, ca_ip_1],
|
|
vec![],
|
|
vec![],
|
|
);
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::Ok
|
|
);
|
|
|
|
// Exact matches reversed with just one
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![ca_ip_2],
|
|
vec![],
|
|
vec![],
|
|
);
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::Ok
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn cert_verify_subnet() {
|
|
let ca_ip_1 = Ipv4Net::from_str("10.0.0.0/16").unwrap();
|
|
let ca_ip_2 = Ipv4Net::from_str("192.168.0.0/24").unwrap();
|
|
|
|
let (ca, ca_key, _ca_pub) = test_ca_cert(
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 10)).unwrap(),
|
|
vec![],
|
|
vec![ca_ip_1, ca_ip_2],
|
|
vec![],
|
|
);
|
|
|
|
let ca_pem = ca.serialize_to_pem().unwrap();
|
|
|
|
let mut ca_pool = NebulaCAPool::new();
|
|
ca_pool.add_ca_certificate(&ca_pem).unwrap();
|
|
|
|
// ip is outside the network
|
|
let cip1 = netmask!("10.1.0.0", "255.255.255.0");
|
|
let cip2 = netmask!("192.198.0.1", "255.255.0.0");
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![],
|
|
vec![cip1, cip2],
|
|
vec![],
|
|
);
|
|
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::SubnetNotPresentOnSigner
|
|
);
|
|
|
|
// ip is outside the network - reversed order from above
|
|
let cip1 = netmask!("192.198.0.1", "255.255.255.0");
|
|
let cip2 = netmask!("10.1.0.0", "255.255.255.0");
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![],
|
|
vec![cip1, cip2],
|
|
vec![],
|
|
);
|
|
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::SubnetNotPresentOnSigner
|
|
);
|
|
|
|
// ip is within the network but mask is outside
|
|
let cip1 = netmask!("10.0.1.0", "255.254.0.0");
|
|
let cip2 = netmask!("192.168.0.1", "255.255.255.0");
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![],
|
|
vec![cip1, cip2],
|
|
vec![],
|
|
);
|
|
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::SubnetNotPresentOnSigner
|
|
);
|
|
|
|
// ip is within the network but mask is outside - reversed order from above
|
|
let cip1 = netmask!("192.168.0.1", "255.255.255.0");
|
|
let cip2 = netmask!("10.0.1.0", "255.254.0.0");
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![cip1, cip2],
|
|
vec![],
|
|
vec![],
|
|
);
|
|
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::SubnetNotPresentOnSigner
|
|
);
|
|
|
|
// ip and mask are within the network
|
|
let cip1 = netmask!("10.0.1.0", "255.255.0.0");
|
|
let cip2 = netmask!("192.168.0.1", "255.255.255.128");
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![],
|
|
vec![cip1, cip2],
|
|
vec![],
|
|
);
|
|
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::Ok
|
|
);
|
|
|
|
// Exact matches
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![],
|
|
vec![ca_ip_1, ca_ip_2],
|
|
vec![],
|
|
);
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::Ok
|
|
);
|
|
|
|
// Exact matches reversed
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![],
|
|
vec![ca_ip_2, ca_ip_1],
|
|
vec![],
|
|
);
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::Ok
|
|
);
|
|
|
|
// Exact matches reversed with just one
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
round_systime_to_secs(SystemTime::now()).unwrap(),
|
|
round_systime_to_secs(SystemTime::now() + Duration::from_secs(60 * 60 * 5)).unwrap(),
|
|
vec![],
|
|
vec![ca_ip_2],
|
|
vec![],
|
|
);
|
|
assert_eq!(
|
|
cert.verify(SystemTime::now(), &ca_pool).unwrap(),
|
|
CertificateValidity::Ok
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn cert_private_key() {
|
|
let (ca, ca_key, _) =
|
|
test_ca_cert(SystemTime::now(), SystemTime::now(), vec![], vec![], vec![]);
|
|
ca.verify_private_key(&ca_key.to_keypair_bytes()).unwrap();
|
|
|
|
let (_, ca_key2, _) =
|
|
test_ca_cert(SystemTime::now(), SystemTime::now(), vec![], vec![], vec![]);
|
|
ca.verify_private_key(&ca_key2.to_keypair_bytes())
|
|
.unwrap_err();
|
|
|
|
let (cert, priv_key, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
SystemTime::now(),
|
|
SystemTime::now(),
|
|
vec![],
|
|
vec![],
|
|
vec![],
|
|
);
|
|
cert.verify_private_key(&priv_key.to_bytes()).unwrap();
|
|
|
|
let (cert2, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
SystemTime::now(),
|
|
SystemTime::now(),
|
|
vec![],
|
|
vec![],
|
|
vec![],
|
|
);
|
|
cert2.verify_private_key(&priv_key.to_bytes()).unwrap_err();
|
|
}
|
|
|
|
#[test]
|
|
fn capool_from_pem() {
|
|
let no_newlines = b"# Current provisional, Remove once everything moves over to the real root.
|
|
-----BEGIN NEBULA CERTIFICATE-----
|
|
CkAKDm5lYnVsYSByb290IGNhKJfap9AFMJfg1+YGOiCUQGByMuNRhIlQBOyzXWbL
|
|
vcKBwDhov900phEfJ5DN3kABEkDCq5R8qBiu8sl54yVfgRcQXEDt3cHr8UTSLszv
|
|
bzBEr00kERQxxTzTsH8cpYEgRoipvmExvg8WP8NdAJEYJosB
|
|
-----END NEBULA CERTIFICATE-----
|
|
# root-ca01
|
|
-----BEGIN NEBULA CERTIFICATE-----
|
|
CkMKEW5lYnVsYSByb290IGNhIDAxKJL2u9EFMJL86+cGOiDPXMH4oU6HZTk/CqTG
|
|
BVG+oJpAoqokUBbI4U0N8CSfpUABEkB/Pm5A2xyH/nc8mg/wvGUWG3pZ7nHzaDMf
|
|
8/phAUt+FLzqTECzQKisYswKvE3pl9mbEYKbOdIHrxdIp95mo4sF
|
|
-----END NEBULA CERTIFICATE-----";
|
|
let with_newlines =
|
|
b"# Current provisional, Remove once everything moves over to the real root.
|
|
-----BEGIN NEBULA CERTIFICATE-----
|
|
CkAKDm5lYnVsYSByb290IGNhKJfap9AFMJfg1+YGOiCUQGByMuNRhIlQBOyzXWbL
|
|
vcKBwDhov900phEfJ5DN3kABEkDCq5R8qBiu8sl54yVfgRcQXEDt3cHr8UTSLszv
|
|
bzBEr00kERQxxTzTsH8cpYEgRoipvmExvg8WP8NdAJEYJosB
|
|
-----END NEBULA CERTIFICATE-----
|
|
# root-ca01
|
|
-----BEGIN NEBULA CERTIFICATE-----
|
|
CkMKEW5lYnVsYSByb290IGNhIDAxKJL2u9EFMJL86+cGOiDPXMH4oU6HZTk/CqTG
|
|
BVG+oJpAoqokUBbI4U0N8CSfpUABEkB/Pm5A2xyH/nc8mg/wvGUWG3pZ7nHzaDMf
|
|
8/phAUt+FLzqTECzQKisYswKvE3pl9mbEYKbOdIHrxdIp95mo4sF
|
|
-----END NEBULA CERTIFICATE-----
|
|
|
|
";
|
|
let expired = b"# expired certificate
|
|
-----BEGIN NEBULA CERTIFICATE-----
|
|
CjkKB2V4cGlyZWQouPmWjQYwufmWjQY6ILCRaoCkJlqHgv5jfDN4lzLHBvDzaQm4
|
|
vZxfu144hmgjQAESQG4qlnZi8DncvD/LDZnLgJHOaX1DWCHHEh59epVsC+BNgTie
|
|
WH1M9n4O7cFtGlM6sJJOS+rCVVEJ3ABS7+MPdQs=
|
|
-----END NEBULA CERTIFICATE-----";
|
|
|
|
let pool_a = NebulaCAPool::new_from_pem(no_newlines).unwrap();
|
|
assert_eq!(
|
|
pool_a.cas["c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522"]
|
|
.details
|
|
.name,
|
|
"nebula root ca".to_string()
|
|
);
|
|
assert_eq!(
|
|
pool_a.cas["5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd"]
|
|
.details
|
|
.name,
|
|
"nebula root ca 01".to_string()
|
|
);
|
|
assert!(!pool_a.expired);
|
|
|
|
let pool_b = NebulaCAPool::new_from_pem(with_newlines).unwrap();
|
|
assert_eq!(
|
|
pool_b.cas["c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522"]
|
|
.details
|
|
.name,
|
|
"nebula root ca".to_string()
|
|
);
|
|
assert_eq!(
|
|
pool_b.cas["5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd"]
|
|
.details
|
|
.name,
|
|
"nebula root ca 01".to_string()
|
|
);
|
|
assert!(!pool_b.expired);
|
|
|
|
let pool_c = NebulaCAPool::new_from_pem(expired).unwrap();
|
|
assert!(pool_c.expired);
|
|
assert_eq!(
|
|
pool_c.cas["152070be6bb19bc9e3bde4c2f0e7d8f4ff5448b4c9856b8eccb314fade0229b0"]
|
|
.details
|
|
.name,
|
|
"expired"
|
|
);
|
|
|
|
let mut pool_d = NebulaCAPool::new_from_pem(with_newlines).unwrap();
|
|
pool_d.add_ca_certificate(expired).unwrap();
|
|
assert_eq!(
|
|
pool_d.cas["c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522"]
|
|
.details
|
|
.name,
|
|
"nebula root ca".to_string()
|
|
);
|
|
assert_eq!(
|
|
pool_d.cas["5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd"]
|
|
.details
|
|
.name,
|
|
"nebula root ca 01".to_string()
|
|
);
|
|
assert_eq!(
|
|
pool_d.cas["152070be6bb19bc9e3bde4c2f0e7d8f4ff5448b4c9856b8eccb314fade0229b0"]
|
|
.details
|
|
.name,
|
|
"expired"
|
|
);
|
|
assert!(pool_d.expired);
|
|
assert_eq!(pool_d.get_fingerprints().len(), 3);
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! netmask {
|
|
($ip:expr,$mask:expr) => {
|
|
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)))
|
|
}
|
|
|
|
fn test_ca_cert(
|
|
before: SystemTime,
|
|
after: SystemTime,
|
|
ips: Vec<Ipv4Net>,
|
|
subnets: Vec<Ipv4Net>,
|
|
groups: Vec<String>,
|
|
) -> (NebulaCertificate, SigningKey, VerifyingKey) {
|
|
let mut csprng = OsRng;
|
|
let key = SigningKey::generate(&mut csprng);
|
|
let pub_key = key.verifying_key();
|
|
|
|
let mut cert = NebulaCertificate {
|
|
details: NebulaCertificateDetails {
|
|
name: "TEST_CA".to_string(),
|
|
ips,
|
|
subnets,
|
|
groups,
|
|
not_before: before,
|
|
not_after: after,
|
|
public_key: pub_key.to_bytes(),
|
|
is_ca: true,
|
|
issuer: String::new(),
|
|
},
|
|
signature: vec![],
|
|
};
|
|
cert.sign(&key).unwrap();
|
|
(cert, key, pub_key)
|
|
}
|
|
|
|
#[test]
|
|
fn test_deserialize_ed25519_private() {
|
|
let priv_key = b"-----BEGIN NEBULA ED25519 PRIVATE KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
|
-----END NEBULA ED25519 PRIVATE KEY-----";
|
|
let short_key = b"-----BEGIN NEBULA ED25519 PRIVATE KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
-----END NEBULA ED25519 PRIVATE KEY-----";
|
|
let invalid_banner = b"-----BEGIN NOT A NEBULA PRIVATE KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
|
-----END NOT A NEBULA PRIVATE KEY-----";
|
|
let invalid_pem = b"-BEGIN NEBULA ED25519 PRIVATE KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
|
-END NEBULA ED25519 PRIVATE KEY-----";
|
|
|
|
deserialize_ed25519_private(priv_key).unwrap();
|
|
|
|
deserialize_ed25519_private(short_key).unwrap_err();
|
|
|
|
deserialize_ed25519_private(invalid_banner).unwrap_err();
|
|
|
|
deserialize_ed25519_private(invalid_pem).unwrap_err();
|
|
}
|
|
|
|
#[test]
|
|
fn test_deserialize_x25519_private() {
|
|
let priv_key = b"-----BEGIN NEBULA X25519 PRIVATE KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
|
-----END NEBULA X25519 PRIVATE KEY-----";
|
|
let short_key = b"-----BEGIN NEBULA X25519 PRIVATE KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
|
-----END NEBULA X25519 PRIVATE KEY-----";
|
|
let invalid_banner = b"-----BEGIN NOT A NEBULA PRIVATE KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
|
-----END NOT A NEBULA PRIVATE KEY-----";
|
|
let invalid_pem = b"-BEGIN NEBULA X25519 PRIVATE KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
|
-END NEBULA X25519 PRIVATE KEY-----";
|
|
|
|
deserialize_x25519_private(priv_key).unwrap();
|
|
|
|
deserialize_x25519_private(short_key).unwrap_err();
|
|
|
|
deserialize_x25519_private(invalid_banner).unwrap_err();
|
|
|
|
deserialize_x25519_private(invalid_pem).unwrap_err();
|
|
}
|
|
|
|
#[test]
|
|
fn test_pem_deserialization() {
|
|
let good_cert = b"# A good cert
|
|
-----BEGIN NEBULA CERTIFICATE-----
|
|
CkAKDm5lYnVsYSByb290IGNhKJfap9AFMJfg1+YGOiCUQGByMuNRhIlQBOyzXWbL
|
|
vcKBwDhov900phEfJ5DN3kABEkDCq5R8qBiu8sl54yVfgRcQXEDt3cHr8UTSLszv
|
|
bzBEr00kERQxxTzTsH8cpYEgRoipvmExvg8WP8NdAJEYJosB
|
|
-----END NEBULA CERTIFICATE-----";
|
|
let bad_banner = b"-----BEGIN NOT A NEBULA CERTIFICATE-----
|
|
CkAKDm5lYnVsYSByb290IGNhKJfap9AFMJfg1+YGOiCUQGByMuNRhIlQBOyzXWbL
|
|
vcKBwDhov900phEfJ5DN3kABEkDCq5R8qBiu8sl54yVfgRcQXEDt3cHr8UTSLszv
|
|
bzBEr00kERQxxTzTsH8cpYEgRoipvmExvg8WP8NdAJEYJosB
|
|
-----END NOT A NEBULA CERTIFICATE-----";
|
|
let invalid_pem = b"# Not a valid PEM format
|
|
-BEGIN NEBULA CERTIFICATE-----
|
|
CkAKDm5lYnVsYSByb290IGNhKJfap9AFMJfg1+YGOiCUQGByMuNRhIlQBOyzXWbL
|
|
vcKBwDhov900phEfJ5DN3kABEkDCq5R8qBiu8sl54yVfgRcQXEDt3cHr8UTSLszv
|
|
bzBEr00kERQxxTzTsH8cpYEgRoipvmExvg8WP8NdAJEYJosB
|
|
-END NEBULA CERTIFICATE----";
|
|
|
|
// success
|
|
deserialize_nebula_certificate_from_pem(good_cert).unwrap();
|
|
|
|
// fail because invalid banner
|
|
deserialize_nebula_certificate_from_pem(bad_banner).unwrap_err();
|
|
|
|
// fail because nonsense pem
|
|
deserialize_nebula_certificate_from_pem(invalid_pem).unwrap_err();
|
|
}
|
|
|
|
#[test]
|
|
fn test_deserialize_ed25519_public() {
|
|
let pub_key = b"-----BEGIN NEBULA ED25519 PUBLIC KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
|
-----END NEBULA ED25519 PUBLIC KEY-----";
|
|
let short_key = b"-----BEGIN NEBULA ED25519 PUBLIC KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
|
-----END NEBULA ED25519 PUBLIC KEY-----";
|
|
let invalid_banner = b"-----BEGIN NOT A NEBULA PUBLIC KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
|
-----END NOT A NEBULA PUBLIC KEY-----";
|
|
let invalid_pem = b"-BEGIN NEBULA ED25519 PUBLIC KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
|
-END NEBULA ED25519 PUBLIC KEY-----";
|
|
|
|
deserialize_ed25519_public(pub_key).unwrap();
|
|
|
|
deserialize_ed25519_public(short_key).unwrap_err();
|
|
|
|
deserialize_ed25519_public(invalid_banner).unwrap_err();
|
|
|
|
deserialize_ed25519_public(invalid_pem).unwrap_err();
|
|
}
|
|
|
|
#[test]
|
|
// Ensure that trifid-pki produces the *exact same* bit-for-bit representation as nebula does
|
|
// to ensure signature interoperability
|
|
// We use an e3team production certificate for this as it is a known-good certificate.
|
|
fn test_serialization_interoperability() {
|
|
let known_good_cert = hex::decode("0a650a08636f72652d74777212098184c4508080f8ff0f28ae9fbf9c06309485c4ab063a20304e6279d8722f0fd8d966faa70adaeec08c4649d486457bb038be243753a7474a2056860ded3d14b7f3b77ca2a062ee5d683dd06b35f87446d8d6a7e923b6a7783d1240eb5d6b688eda0d36e925219c098ff2799e42207a093f7d9b7d875823ca05b2e0d3a749dd2fc9cec811ed9a2865d71c4d53cfdfd1bf7e6d5058bd9ecd388ddf0d").unwrap();
|
|
|
|
let cert = deserialize_nebula_certificate(&known_good_cert).unwrap();
|
|
let reserialized_cert_pem = cert.serialize().unwrap();
|
|
assert_eq!(known_good_cert, reserialized_cert_pem);
|
|
}
|
|
|
|
#[test]
|
|
fn test_deserialize_x25519_public() {
|
|
let priv_key = b"-----BEGIN NEBULA X25519 PUBLIC KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
|
-----END NEBULA X25519 PUBLIC KEY-----";
|
|
let short_key = b"-----BEGIN NEBULA X25519 PUBLIC KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
|
-----END NEBULA X25519 PUBLIC KEY-----";
|
|
let invalid_banner = b"-----BEGIN NOT A NEBULA PUBLIC KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
|
-----END NOT A NEBULA PUBLIC KEY-----";
|
|
let invalid_pem = b"-BEGIN NEBULA X25519 PUBLIC KEY-----
|
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
|
-END NEBULA X25519 PUBLIC KEY-----";
|
|
|
|
deserialize_x25519_public(priv_key).unwrap();
|
|
|
|
deserialize_x25519_public(short_key).unwrap_err();
|
|
|
|
deserialize_x25519_public(invalid_banner).unwrap_err();
|
|
|
|
deserialize_x25519_public(invalid_pem).unwrap_err();
|
|
}
|
|
|
|
#[test]
|
|
fn ca_pool_add_non_ca() {
|
|
let mut ca_pool = NebulaCAPool::new();
|
|
|
|
let (ca, ca_key, _) = test_ca_cert(
|
|
SystemTime::now(),
|
|
SystemTime::now() + Duration::from_secs(3600),
|
|
vec![],
|
|
vec![],
|
|
vec![],
|
|
);
|
|
let (cert, _, _) = test_cert(
|
|
&ca,
|
|
&ca_key,
|
|
SystemTime::now(),
|
|
SystemTime::now(),
|
|
vec![],
|
|
vec![],
|
|
vec![],
|
|
);
|
|
|
|
ca_pool
|
|
.add_ca_certificate(&cert.serialize_to_pem().unwrap())
|
|
.unwrap_err();
|
|
}
|
|
|
|
fn test_cert(
|
|
ca: &NebulaCertificate,
|
|
key: &SigningKey,
|
|
before: SystemTime,
|
|
after: SystemTime,
|
|
ips: Vec<Ipv4Net>,
|
|
subnets: Vec<Ipv4Net>,
|
|
groups: Vec<String>,
|
|
) -> (NebulaCertificate, SigningKey, VerifyingKey) {
|
|
let issuer = ca.sha256sum().unwrap();
|
|
|
|
let real_groups = if groups.is_empty() {
|
|
vec![
|
|
"test-group1".to_string(),
|
|
"test-group2".to_string(),
|
|
"test-group3".to_string(),
|
|
]
|
|
} else {
|
|
groups
|
|
};
|
|
|
|
let real_ips = if ips.is_empty() {
|
|
vec![
|
|
netmask!("10.1.1.1", "255.255.255.0"),
|
|
netmask!("10.1.1.2", "255.255.0.0"),
|
|
netmask!("10.1.1.3", "255.0.0.0"),
|
|
]
|
|
} else {
|
|
ips
|
|
};
|
|
|
|
let real_subnets = if subnets.is_empty() {
|
|
vec![
|
|
netmask!("9.1.1.1", "255.255.255.128"),
|
|
netmask!("9.1.1.2", "255.255.255.0"),
|
|
netmask!("9.1.1.3", "255.255.0.0"),
|
|
]
|
|
} else {
|
|
subnets
|
|
};
|
|
|
|
let mut csprng = OsRng;
|
|
let cert_key = SigningKey::generate(&mut csprng);
|
|
let pub_key = cert_key.verifying_key();
|
|
|
|
let mut cert = NebulaCertificate {
|
|
details: NebulaCertificateDetails {
|
|
name: "TEST_CA".to_string(),
|
|
ips: real_ips,
|
|
subnets: real_subnets,
|
|
groups: real_groups,
|
|
not_before: before,
|
|
not_after: after,
|
|
public_key: pub_key.to_bytes(),
|
|
is_ca: false,
|
|
issuer,
|
|
},
|
|
signature: vec![],
|
|
};
|
|
cert.sign(key).unwrap();
|
|
(cert, cert_key, pub_key)
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
fn in_a_minute() -> SystemTime {
|
|
round_systime_to_secs(SystemTime::now().add(Duration::from_secs(60))).unwrap()
|
|
}
|
|
#[allow(dead_code)]
|
|
fn a_minute_ago() -> SystemTime {
|
|
round_systime_to_secs(SystemTime::now().sub(Duration::from_secs(60))).unwrap()
|
|
}
|