mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-25 16:36:54 +02:00
Fix a weird little bug (actually incompatibility with the old code) in x25519 handling in identity, and more cleanup.
This commit is contained in:
parent
0397440022
commit
b1f67b13f9
6 changed files with 169 additions and 104 deletions
|
@ -1,4 +1,4 @@
|
|||
max_width = 300
|
||||
max_width = 256
|
||||
use_small_heuristics = "Max"
|
||||
tab_spaces = 4
|
||||
newline_style = "Unix"
|
||||
|
|
|
@ -22,23 +22,39 @@ pub const ED25519_SECRET_KEY_SIZE: usize = 32;
|
|||
pub const ED25519_SIGNATURE_SIZE: usize = 64;
|
||||
|
||||
/// Curve25519 key pair for ECDH key agreement.
|
||||
pub struct C25519KeyPair(x25519_dalek::StaticSecret, x25519_dalek::PublicKey);
|
||||
pub struct C25519KeyPair(x25519_dalek::StaticSecret, Secret<32>, x25519_dalek::PublicKey);
|
||||
|
||||
impl C25519KeyPair {
|
||||
#[inline(always)]
|
||||
pub fn generate() -> C25519KeyPair {
|
||||
let sk = x25519_dalek::StaticSecret::new(SecureRandom::get());
|
||||
let sk2 = Secret(sk.to_bytes());
|
||||
let pk = x25519_dalek::PublicKey::from(&sk);
|
||||
C25519KeyPair(sk, pk)
|
||||
C25519KeyPair(sk, sk2, pk)
|
||||
}
|
||||
|
||||
pub fn from_bytes(public_key: &[u8], secret_key: &[u8]) -> Option<C25519KeyPair> {
|
||||
if public_key.len() == 32 && secret_key.len() == 32 {
|
||||
// NOTE: we keep the original secret separately from x25519_dalek's StaticSecret
|
||||
// due to how "clamping" is done in the old C++ code vs x25519_dalek. Clamping
|
||||
// is explained here:
|
||||
//
|
||||
// https://www.jcraige.com/an-explainer-on-ed25519-clamping
|
||||
//
|
||||
// In the old C++ code clamping is done when the secret key is actually used.
|
||||
// In x25519_dalek it's done when the key is loaded into one of the secret
|
||||
// containers. Unfortunately this means that identities' secret keys won't look
|
||||
// the same in the actual identity structure vs. what you would get from the C++
|
||||
// v0 ZeroTier implementation. The cryptographic results are identical but we
|
||||
// still need to have our identity spit out identical bits when exported.
|
||||
//
|
||||
// Newly generated keys will be clamped at generation time, which will also yield
|
||||
// identical results in both cases.
|
||||
let pk: [u8; 32] = public_key.try_into().unwrap();
|
||||
let sk: [u8; 32] = secret_key.try_into().unwrap();
|
||||
let sk_orig: Secret<32> = Secret(secret_key.try_into().unwrap());
|
||||
let pk = x25519_dalek::PublicKey::from(pk);
|
||||
let sk = x25519_dalek::StaticSecret::from(sk);
|
||||
Some(C25519KeyPair(sk, pk))
|
||||
let sk = x25519_dalek::StaticSecret::from(sk_orig.0.clone());
|
||||
Some(C25519KeyPair(sk, sk_orig, pk))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -46,12 +62,12 @@ impl C25519KeyPair {
|
|||
|
||||
#[inline(always)]
|
||||
pub fn public_bytes(&self) -> [u8; C25519_PUBLIC_KEY_SIZE] {
|
||||
self.1.to_bytes()
|
||||
self.2.to_bytes()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn secret_bytes(&self) -> Secret<{ C25519_SECRET_KEY_SIZE }> {
|
||||
Secret(self.0.to_bytes())
|
||||
pub fn secret_bytes(&self) -> &Secret<32> {
|
||||
&self.1
|
||||
}
|
||||
|
||||
/// Execute ECDH agreement and return a raw (un-hashed) shared secret key.
|
||||
|
@ -65,18 +81,20 @@ impl C25519KeyPair {
|
|||
|
||||
impl Clone for C25519KeyPair {
|
||||
fn clone(&self) -> Self {
|
||||
Self(x25519_dalek::StaticSecret::from(self.0.to_bytes()), x25519_dalek::PublicKey::from(self.1.to_bytes()))
|
||||
Self(x25519_dalek::StaticSecret::from(self.0.to_bytes()), self.1.clone(), x25519_dalek::PublicKey::from(self.1 .0.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Ed25519 key pair for EDDSA signatures.
|
||||
pub struct Ed25519KeyPair(ed25519_dalek::Keypair);
|
||||
pub struct Ed25519KeyPair(ed25519_dalek::Keypair, Secret<32>);
|
||||
|
||||
impl Ed25519KeyPair {
|
||||
#[inline(always)]
|
||||
pub fn generate() -> Ed25519KeyPair {
|
||||
let mut rng = SecureRandom::get();
|
||||
Ed25519KeyPair(ed25519_dalek::Keypair::generate(&mut rng))
|
||||
let kp = ed25519_dalek::Keypair::generate(&mut rng);
|
||||
let sk2 = Secret(kp.secret.to_bytes());
|
||||
Ed25519KeyPair(kp, sk2)
|
||||
}
|
||||
|
||||
pub fn from_bytes(public_bytes: &[u8], secret_bytes: &[u8]) -> Option<Ed25519KeyPair> {
|
||||
|
@ -84,7 +102,11 @@ impl Ed25519KeyPair {
|
|||
let pk = ed25519_dalek::PublicKey::from_bytes(public_bytes);
|
||||
let sk = ed25519_dalek::SecretKey::from_bytes(secret_bytes);
|
||||
if pk.is_ok() && sk.is_ok() {
|
||||
Some(Ed25519KeyPair(ed25519_dalek::Keypair { public: pk.unwrap(), secret: sk.unwrap() }))
|
||||
// See comment in from_bytes() in C25519KeyPair for an explanation of the copy of the secret here.
|
||||
let pk = pk.unwrap();
|
||||
let sk = sk.unwrap();
|
||||
let sk2 = Secret(sk.to_bytes());
|
||||
Some(Ed25519KeyPair(ed25519_dalek::Keypair { public: pk, secret: sk }, sk2))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -99,8 +121,8 @@ impl Ed25519KeyPair {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn secret_bytes(&self) -> Secret<{ ED25519_SECRET_KEY_SIZE }> {
|
||||
Secret(self.0.secret.to_bytes())
|
||||
pub fn secret_bytes(&self) -> &Secret<32> {
|
||||
&self.1
|
||||
}
|
||||
|
||||
pub fn sign(&self, msg: &[u8]) -> [u8; ED25519_SIGNATURE_SIZE] {
|
||||
|
@ -126,7 +148,7 @@ impl Ed25519KeyPair {
|
|||
|
||||
impl Clone for Ed25519KeyPair {
|
||||
fn clone(&self) -> Self {
|
||||
Self(ed25519_dalek::Keypair::from_bytes(&self.0.to_bytes()).unwrap())
|
||||
Self(ed25519_dalek::Keypair::from_bytes(&self.0.to_bytes()).unwrap(), self.1.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ impl<const L: usize> Secret<L> {
|
|||
|
||||
#[inline(always)]
|
||||
pub fn as_bytes(&self) -> &[u8; L] {
|
||||
return &self.0;
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Get a clone of the first N bytes of this secret.
|
||||
|
|
|
@ -61,14 +61,7 @@ impl<const FREQ: i64> AtomicIntervalGate<FREQ> {
|
|||
if (time - prev_time) < FREQ {
|
||||
false
|
||||
} else {
|
||||
loop {
|
||||
let pt = self.0.swap(time, Ordering::AcqRel);
|
||||
if pt <= time {
|
||||
break;
|
||||
} else {
|
||||
time = pt;
|
||||
}
|
||||
}
|
||||
self.0.store(time, Ordering::Release);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,11 +23,11 @@ pub(crate) fn byte_array_range<const A: usize, const START: usize, const LEN: us
|
|||
}
|
||||
|
||||
/// Obtain a reference to a sub-array within an existing byte array.
|
||||
#[inline(always)]
|
||||
pub(crate) fn byte_array_range_mut<const A: usize, const START: usize, const LEN: usize>(a: &mut [u8; A]) -> &mut [u8; LEN] {
|
||||
assert!((START + LEN) <= A);
|
||||
unsafe { &mut *a.as_mut_ptr().add(START).cast::<[u8; LEN]>() }
|
||||
}
|
||||
//#[inline(always)]
|
||||
//pub(crate) fn byte_array_range_mut<const A: usize, const START: usize, const LEN: usize>(a: &mut [u8; A]) -> &mut [u8; LEN] {
|
||||
// assert!((START + LEN) <= A);
|
||||
// unsafe { &mut *a.as_mut_ptr().add(START).cast::<[u8; LEN]>() }
|
||||
//}
|
||||
|
||||
/// Non-cryptographic 64-bit bit mixer for things like local hashing.
|
||||
#[inline(always)]
|
||||
|
|
|
@ -41,9 +41,20 @@ pub const IDENTITY_ALGORITHM_EC_NIST_P384: u8 = 0x02;
|
|||
/// Bit mask to include all algorithms.
|
||||
pub const IDENTITY_ALGORITHM_ALL: u8 = 0xff;
|
||||
|
||||
/// Current sanity limit for the size of a marshaled Identity (can be increased if needed).
|
||||
pub const MAX_MARSHAL_SIZE: usize =
|
||||
ADDRESS_SIZE + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE + 16;
|
||||
/// Current sanity limit for the size of a marshaled Identity
|
||||
/// This is padded just a little up to 512 and can be increased if new key types are ever added.
|
||||
pub const MAX_MARSHAL_SIZE: usize = 25
|
||||
+ ADDRESS_SIZE
|
||||
+ C25519_PUBLIC_KEY_SIZE
|
||||
+ ED25519_PUBLIC_KEY_SIZE
|
||||
+ C25519_SECRET_KEY_SIZE
|
||||
+ ED25519_SECRET_KEY_SIZE
|
||||
+ P384_PUBLIC_KEY_SIZE
|
||||
+ P384_PUBLIC_KEY_SIZE
|
||||
+ P384_SECRET_KEY_SIZE
|
||||
+ P384_SECRET_KEY_SIZE
|
||||
+ P384_ECDSA_SIGNATURE_SIZE
|
||||
+ ED25519_SIGNATURE_SIZE;
|
||||
|
||||
/// Secret keys associated with NIST P-384 public keys.
|
||||
#[derive(Clone)]
|
||||
|
@ -110,6 +121,7 @@ fn concat_arrays_4<const A: usize, const B: usize, const C: usize, const D: usiz
|
|||
impl Identity {
|
||||
/// Generate a new identity.
|
||||
pub fn generate() -> Self {
|
||||
// First generate an identity with just x25519 keys.
|
||||
let mut sha = SHA512::new();
|
||||
let ed25519 = Ed25519KeyPair::generate();
|
||||
let ed25519_pub = ed25519.public_bytes();
|
||||
|
@ -137,7 +149,6 @@ impl Identity {
|
|||
sha.reset();
|
||||
}
|
||||
drop(genmem_pool_obj);
|
||||
|
||||
let mut id = Self {
|
||||
address,
|
||||
c25519: c25519_pub,
|
||||
|
@ -146,8 +157,11 @@ impl Identity {
|
|||
secret: Some(IdentitySecret { c25519, ed25519, p384: None }),
|
||||
fingerprint: [0_u8; 64], // replaced in upgrade()
|
||||
};
|
||||
|
||||
// Then "upgrade" to add NIST P-384 keys and compute fingerprint.
|
||||
assert!(id.upgrade().is_ok());
|
||||
assert!(id.p384.is_some() && id.secret.as_ref().unwrap().p384.is_some());
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
|
@ -194,6 +208,7 @@ impl Identity {
|
|||
return Ok(false);
|
||||
}
|
||||
|
||||
/// Get a bit mask of algorithms present in this identity.
|
||||
#[inline(always)]
|
||||
pub fn algorithms(&self) -> u8 {
|
||||
if self.p384.is_some() {
|
||||
|
@ -248,8 +263,7 @@ impl Identity {
|
|||
/// NIST P-384 and the result is HMAC(Curve25519 secret, NIST P-384 secret).
|
||||
///
|
||||
/// Nothing actually uses a 512-bit secret directly, but if the base secret is 512 bits then
|
||||
/// no entropy is lost when deriving secrets with a KDF. Ciphers like AES use the first 256 bits
|
||||
/// of these keys.
|
||||
/// no entropy is lost when deriving smaller secrets with a KDF.
|
||||
pub fn agree(&self, other: &Identity) -> Option<Secret<64>> {
|
||||
self.secret.as_ref().and_then(|secret| {
|
||||
let c25519_secret = Secret(SHA512::hash(&secret.c25519.agree(&other.c25519).0));
|
||||
|
@ -267,27 +281,33 @@ impl Identity {
|
|||
|
||||
/// Sign a message with this identity.
|
||||
///
|
||||
/// If legacy_compatibility is true this generates only an ed25519 signature. Otherwise it
|
||||
/// will generate a signature using both the ed25519 key and the P-384 key if the latter
|
||||
/// is present in the identity.
|
||||
/// If legacy_compatibility is true this generates only an ed25519 signature and uses the old
|
||||
/// format that also includes part of the plaintext hash at the end. The include_algorithms mask
|
||||
/// will be ignored. Otherwise it will generate a signature for every algorithm with a secret
|
||||
/// in this identity and that is specified in the include_algorithms bit mask.
|
||||
///
|
||||
/// A return of None happens if we don't have our secret key(s) or some other error occurs.
|
||||
pub fn sign(&self, msg: &[u8], legacy_compatibility: bool) -> Option<Vec<u8>> {
|
||||
pub fn sign(&self, msg: &[u8], include_algorithms: u8, legacy_compatibility: bool) -> Option<Vec<u8>> {
|
||||
if self.secret.is_some() {
|
||||
let secret = self.secret.as_ref().unwrap();
|
||||
if legacy_compatibility {
|
||||
Some(secret.ed25519.sign_zt(msg).to_vec())
|
||||
} else if secret.p384.is_some() {
|
||||
let mut tmp: Vec<u8> = Vec::with_capacity(1 + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE);
|
||||
tmp.push(IDENTITY_ALGORITHM_X25519 | IDENTITY_ALGORITHM_EC_NIST_P384);
|
||||
let _ = tmp.write_all(&secret.p384.as_ref().unwrap().ecdsa.sign(msg));
|
||||
let _ = tmp.write_all(&secret.ed25519.sign(msg));
|
||||
Some(tmp)
|
||||
} else {
|
||||
let mut tmp: Vec<u8> = Vec::with_capacity(1 + ED25519_SIGNATURE_SIZE);
|
||||
tmp.push(IDENTITY_ALGORITHM_X25519);
|
||||
let _ = tmp.write_all(&secret.ed25519.sign(msg));
|
||||
Some(tmp)
|
||||
let mut tmp: Vec<u8> = Vec::with_capacity(1 + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE);
|
||||
tmp.push(0);
|
||||
if secret.p384.is_some() && (include_algorithms & IDENTITY_ALGORITHM_EC_NIST_P384) != 0 {
|
||||
*tmp.first_mut().unwrap() |= IDENTITY_ALGORITHM_EC_NIST_P384;
|
||||
let _ = tmp.write_all(&secret.p384.as_ref().unwrap().ecdsa.sign(msg));
|
||||
}
|
||||
if (include_algorithms & IDENTITY_ALGORITHM_X25519) != 0 {
|
||||
*tmp.first_mut().unwrap() |= IDENTITY_ALGORITHM_X25519;
|
||||
let _ = tmp.write_all(&secret.ed25519.sign(msg));
|
||||
}
|
||||
if tmp.len() > 1 {
|
||||
Some(tmp)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
|
@ -297,33 +317,42 @@ impl Identity {
|
|||
/// Verify a signature against this identity.
|
||||
pub fn verify(&self, msg: &[u8], mut signature: &[u8]) -> bool {
|
||||
if signature.len() == 96 {
|
||||
// legacy ed25519-only signature with hash included
|
||||
ed25519_verify(&self.ed25519, signature, msg)
|
||||
// legacy ed25519-only signature with hash included detected by their unique size.
|
||||
return ed25519_verify(&self.ed25519, signature, msg);
|
||||
} else if signature.len() > 1 {
|
||||
// Otherwise we support compound signatures. Note that it's possible for there to be
|
||||
// unknown algorithms here if we ever add e.g. a PQ signature scheme and older nodes
|
||||
// don't support it, and therefore it's valid if all algorithms that are present and
|
||||
// understood pass signature check. The 'passed' variable makes sure we can't pass without
|
||||
// verifying at least one signature. If any present and understood algorithm fails the
|
||||
// whole check fails, so you can't have one good and one bad signature.
|
||||
let algorithms = signature[0];
|
||||
signature = &signature[1..];
|
||||
let mut ok = true;
|
||||
let mut checked = false;
|
||||
if ok && (algorithms & IDENTITY_ALGORITHM_EC_NIST_P384) != 0 && signature.len() >= P384_ECDSA_SIGNATURE_SIZE && self.p384.is_some() {
|
||||
ok = self.p384.as_ref().unwrap().ecdsa.verify(msg, &signature[..P384_ECDSA_SIGNATURE_SIZE]);
|
||||
let mut passed = false; // makes sure we can't pass with an empty signature!
|
||||
if (algorithms & IDENTITY_ALGORITHM_EC_NIST_P384) != 0 && signature.len() >= P384_ECDSA_SIGNATURE_SIZE && self.p384.is_some() {
|
||||
if !self.p384.as_ref().unwrap().ecdsa.verify(msg, &signature[..P384_ECDSA_SIGNATURE_SIZE]) {
|
||||
return false;
|
||||
}
|
||||
signature = &signature[P384_ECDSA_SIGNATURE_SIZE..];
|
||||
checked = true;
|
||||
passed = true;
|
||||
}
|
||||
if ok && (algorithms & IDENTITY_ALGORITHM_X25519) != 0 && signature.len() >= ED25519_SIGNATURE_SIZE {
|
||||
ok = ed25519_verify(&self.ed25519, &signature[..ED25519_SIGNATURE_SIZE], msg);
|
||||
signature = &signature[ED25519_SIGNATURE_SIZE..];
|
||||
checked = true;
|
||||
if (algorithms & IDENTITY_ALGORITHM_X25519) != 0 && signature.len() >= ED25519_SIGNATURE_SIZE {
|
||||
if !ed25519_verify(&self.ed25519, &signature[..ED25519_SIGNATURE_SIZE], msg) {
|
||||
return false;
|
||||
}
|
||||
//signature = &signature[ED25519_SIGNATURE_SIZE..];
|
||||
passed = true;
|
||||
}
|
||||
checked && ok
|
||||
return passed;
|
||||
} else {
|
||||
false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn to_bytes(&self, include_algorithms: u8, include_private: bool) -> Buffer<MAX_MARSHAL_SIZE> {
|
||||
let mut b: Buffer<MAX_MARSHAL_SIZE> = Buffer::new();
|
||||
self.marshal(&mut b, include_algorithms, include_private).expect("internal error marshaling Identity");
|
||||
assert!(self.marshal(&mut b, include_algorithms, include_private).is_ok());
|
||||
b
|
||||
}
|
||||
|
||||
|
@ -335,16 +364,19 @@ impl Identity {
|
|||
let secret = self.secret.as_ref();
|
||||
|
||||
buf.append_bytes_fixed(&self.address.to_bytes())?;
|
||||
buf.append_u8(0x00)?; // LEGACY: 0x00 here for backward compatibility
|
||||
buf.append_bytes_fixed(&self.c25519)?;
|
||||
buf.append_bytes_fixed(&self.ed25519)?;
|
||||
if include_private && secret.is_some() {
|
||||
let secret = secret.unwrap();
|
||||
buf.append_u8((C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8)?;
|
||||
buf.append_bytes_fixed(&secret.c25519.secret_bytes().0)?;
|
||||
buf.append_bytes_fixed(&secret.ed25519.secret_bytes().0)?;
|
||||
} else {
|
||||
buf.append_u8(0)?;
|
||||
|
||||
if (algorithms & IDENTITY_ALGORITHM_X25519) != 0 {
|
||||
buf.append_u8(0x00)?; // 0x00 is used for X25519 for backward compatibility with v0 identities
|
||||
buf.append_bytes_fixed(&self.c25519)?;
|
||||
buf.append_bytes_fixed(&self.ed25519)?;
|
||||
if include_private && secret.is_some() {
|
||||
let secret = secret.unwrap();
|
||||
buf.append_u8((C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE) as u8)?;
|
||||
buf.append_bytes_fixed(&secret.c25519.secret_bytes().0)?;
|
||||
buf.append_bytes_fixed(&secret.ed25519.secret_bytes().0)?;
|
||||
} else {
|
||||
buf.append_u8(0)?;
|
||||
}
|
||||
}
|
||||
|
||||
if (algorithms & IDENTITY_ALGORITHM_EC_NIST_P384) != 0 && self.p384.is_some() {
|
||||
|
@ -529,37 +561,48 @@ impl Identity {
|
|||
})
|
||||
}
|
||||
|
||||
/// Marshal this identity as a string with options to control which ciphers are included and whether private keys are included.
|
||||
/// Marshal this identity as a string.
|
||||
///
|
||||
/// The include_algorithms bitmap controls which algorithms will be included, provided we have them.
|
||||
/// If include_private is true private keys will be included, again if we have them.
|
||||
pub fn to_string_with_options(&self, include_algorithms: u8, include_private: bool) -> String {
|
||||
if include_private && self.secret.is_some() {
|
||||
let secret = self.secret.as_ref().unwrap();
|
||||
if (include_algorithms & IDENTITY_ALGORITHM_EC_NIST_P384) == IDENTITY_ALGORITHM_EC_NIST_P384 && secret.p384.is_some() && self.p384.is_some() {
|
||||
let p384_secret = secret.p384.as_ref().unwrap();
|
||||
let p384 = self.p384.as_ref().unwrap();
|
||||
let p384_secret_joined: [u8; P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE] = concat_arrays_2(p384_secret.ecdh.secret_key_bytes().as_bytes(), p384_secret.ecdsa.secret_key_bytes().as_bytes());
|
||||
let p384_joined: [u8; P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE] = concat_arrays_4(p384.ecdh.as_bytes(), p384.ecdsa.as_bytes(), &p384.ecdsa_self_signature, &p384.ed25519_self_signature);
|
||||
format!(
|
||||
"{}:0:{}{}:{}{}:2:{}:{}",
|
||||
self.address.to_string(),
|
||||
hex::to_string(&self.c25519),
|
||||
hex::to_string(&self.ed25519),
|
||||
hex::to_string(&secret.c25519.secret_bytes().0),
|
||||
hex::to_string(&secret.ed25519.secret_bytes().0),
|
||||
base64::encode_config(p384_joined, base64::URL_SAFE_NO_PAD),
|
||||
base64::encode_config(p384_secret_joined, base64::URL_SAFE_NO_PAD)
|
||||
)
|
||||
} else {
|
||||
format!("{}:0:{}{}:{}{}", self.address.to_string(), hex::to_string(&self.c25519), hex::to_string(&self.ed25519), hex::to_string(&secret.c25519.secret_bytes().0), hex::to_string(&secret.ed25519.secret_bytes().0))
|
||||
let include_p384 = self.p384.is_some() && ((include_algorithms & IDENTITY_ALGORITHM_EC_NIST_P384) != 0);
|
||||
|
||||
let mut s = String::with_capacity(MAX_MARSHAL_SIZE * 2);
|
||||
s.push_str(self.address.to_string().as_str());
|
||||
|
||||
if (include_algorithms & IDENTITY_ALGORITHM_X25519) != 0 {
|
||||
s.push_str(":0:"); // 0 used for x25519 for legacy reasons just like in marshal()
|
||||
s.push_str(hex::to_string(&self.c25519).as_str());
|
||||
s.push_str(hex::to_string(&self.ed25519).as_str());
|
||||
if self.secret.is_some() && include_private {
|
||||
let secret = self.secret.as_ref().unwrap();
|
||||
s.push(':');
|
||||
s.push_str(hex::to_string(secret.c25519.secret_bytes().as_bytes()).as_str());
|
||||
s.push_str(hex::to_string(secret.ed25519.secret_bytes().as_bytes()).as_str());
|
||||
} else if include_p384 {
|
||||
s.push(':');
|
||||
}
|
||||
} else {
|
||||
self.p384.as_ref().map_or_else(
|
||||
|| format!("{}:0:{}{}", self.address.to_string(), hex::to_string(&self.c25519), hex::to_string(&self.ed25519)),
|
||||
|p384| {
|
||||
let p384_joined: [u8; P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE] = concat_arrays_4(p384.ecdh.as_bytes(), p384.ecdsa.as_bytes(), &p384.ecdsa_self_signature, &p384.ed25519_self_signature);
|
||||
format!("{}:0:{}{}::2:{}", self.address.to_string(), hex::to_string(&self.c25519), hex::to_string(&self.ed25519), base64::encode_config(p384_joined, base64::URL_SAFE_NO_PAD))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if include_p384 {
|
||||
let p384 = self.p384.as_ref().unwrap();
|
||||
|
||||
s.push_str(":2:"); // 2 == IDENTITY_ALGORITHM_EC_NIST_P384
|
||||
let p384_joined: [u8; P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE] = concat_arrays_4(p384.ecdh.as_bytes(), p384.ecdsa.as_bytes(), &p384.ecdsa_self_signature, &p384.ed25519_self_signature);
|
||||
s.push_str(base64::encode_config(p384_joined, base64::URL_SAFE_NO_PAD).as_str());
|
||||
if self.secret.is_some() && include_private {
|
||||
let secret = self.secret.as_ref().unwrap();
|
||||
if secret.p384.is_some() {
|
||||
let p384_secret = secret.p384.as_ref().unwrap();
|
||||
let p384_secret_joined: [u8; P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE] = concat_arrays_2(p384_secret.ecdh.secret_key_bytes().as_bytes(), p384_secret.ecdsa.secret_key_bytes().as_bytes());
|
||||
s.push(':');
|
||||
s.push_str(base64::encode_config(p384_secret_joined, base64::URL_SAFE_NO_PAD).as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
/// Get this identity in string form with all ciphers and with secrets (if present)
|
||||
|
@ -618,8 +661,12 @@ impl FromStr for Identity {
|
|||
ptr += 1;
|
||||
}
|
||||
|
||||
let keys =
|
||||
[hex::from_string(keys[0].unwrap_or("")), hex::from_string(keys[1].unwrap_or("")), base64::decode_config(keys[2].unwrap_or(""), base64::URL_SAFE_NO_PAD).unwrap_or_else(|_| Vec::new()), base64::decode_config(keys[3].unwrap_or(""), base64::URL_SAFE_NO_PAD).unwrap_or_else(|_| Vec::new())];
|
||||
let keys = [
|
||||
hex::from_string(keys[0].unwrap_or("")),
|
||||
hex::from_string(keys[1].unwrap_or("")),
|
||||
base64::decode_config(keys[2].unwrap_or(""), base64::URL_SAFE_NO_PAD).unwrap_or_else(|_| Vec::new()),
|
||||
base64::decode_config(keys[3].unwrap_or(""), base64::URL_SAFE_NO_PAD).unwrap_or_else(|_| Vec::new()),
|
||||
];
|
||||
if keys[0].len() != C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE {
|
||||
return Err(InvalidFormatError);
|
||||
}
|
||||
|
@ -868,6 +915,7 @@ lazy_static! {
|
|||
/// Purge the memory pool used to verify identities. This can be called periodically
|
||||
/// from the maintenance function to prevent memory buildup from bursts of identity
|
||||
/// verification.
|
||||
#[allow(unused)]
|
||||
#[inline(always)]
|
||||
pub(crate) fn purge_verification_memory_pool() {
|
||||
ADDRESS_DERVIATION_MEMORY_POOL.purge();
|
||||
|
@ -926,6 +974,7 @@ mod tests {
|
|||
|
||||
for id_str in GOOD_V0_IDENTITIES {
|
||||
let mut id = Identity::from_str(id_str).unwrap();
|
||||
assert_eq!(id.to_string_with_options(IDENTITY_ALGORITHM_ALL, true).as_str(), id_str);
|
||||
|
||||
assert!(id.validate_identity());
|
||||
assert!(id.p384.is_none());
|
||||
|
@ -956,6 +1005,7 @@ mod tests {
|
|||
}
|
||||
for id_str in GOOD_V1_IDENTITIES {
|
||||
let id = Identity::from_str(id_str).unwrap();
|
||||
assert_eq!(id.to_string_with_options(IDENTITY_ALGORITHM_ALL, false).as_str(), id_str);
|
||||
|
||||
assert!(id.validate_identity());
|
||||
assert!(id.p384.is_some());
|
||||
|
|
Loading…
Add table
Reference in a new issue