128 lines
3.5 KiB
Rust
128 lines
3.5 KiB
Rust
/// `logtail` is a collection of tools that enables users to manage the
|
|
/// cryptographic keypairs involved in the [logtail](https://github.com/tailscale/tailscale/blob/main/logtail/api.md)
|
|
/// protocol. This is a port of [github.com/tailscale/tailscale/logtail](https://github.com/tailscale/tailscale/tree/main/logtail)'s
|
|
/// `id.go`.
|
|
use sha2::{Digest, Sha256};
|
|
use std::fmt;
|
|
|
|
/// This represents an instance that write logs.
|
|
/// Private IDs are only shared with the server when writing logs.
|
|
#[derive(Clone, PartialEq, Eq)]
|
|
pub struct PrivateID([u8; SHA256_SIZE]);
|
|
|
|
mod serde;
|
|
|
|
impl PrivateID {
|
|
/// Safely generate a new PrivateId for use in Config objects.
|
|
/// You should persist this across runs of an instance of your app, so that
|
|
/// it can append to the same log file on each run.
|
|
pub fn new() -> Self {
|
|
let mut id: [u8; 32] = rand::random();
|
|
|
|
// Clamping, for future use.
|
|
id[0] &= 248;
|
|
id[31] = (id[31] & 127) | 64;
|
|
|
|
Self(id)
|
|
}
|
|
|
|
/// Parse a PrivateID from its base 16 representation.
|
|
pub fn from_hex(data: String) -> Result<Self, hex::FromHexError> {
|
|
let mut id: [u8; 32] = [0; 32];
|
|
hex::decode_to_slice(data, &mut id)?;
|
|
Ok(Self(id))
|
|
}
|
|
|
|
/// Show a private ID as its base 16 representation.
|
|
pub fn as_hex(&self) -> String {
|
|
hex::encode(self.0)
|
|
}
|
|
|
|
/// Converts this private ID into its public equivalent.
|
|
pub fn as_public(&self) -> PublicID {
|
|
let mut hasher = Sha256::new();
|
|
hasher.update(&self.0);
|
|
PublicID(hasher.finalize().into())
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for PrivateID {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
write!(f, "{}", self.as_hex())
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for PrivateID {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
write!(f, "PrivateID({})", self.as_hex())
|
|
}
|
|
}
|
|
|
|
const SHA256_SIZE: usize = 32;
|
|
|
|
/// The public component to a logtail ID, derived from the SHA256 hash
|
|
/// of the private ID.
|
|
#[derive(Clone, PartialEq, Eq)]
|
|
pub struct PublicID([u8; SHA256_SIZE]);
|
|
|
|
impl PublicID {
|
|
/// Parse a PublicID from its base 16 representation.
|
|
pub fn from_hex(data: String) -> Result<Self, hex::FromHexError> {
|
|
let mut id: [u8; 32] = [0; 32];
|
|
hex::decode_to_slice(data, &mut id)?;
|
|
Ok(Self(id))
|
|
}
|
|
|
|
/// Show a public ID as its base 16 representation.
|
|
pub fn as_hex(&self) -> String {
|
|
hex::encode(self.0)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for PublicID {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
write!(f, "{}", self.as_hex())
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for PublicID {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
write!(f, "PublicID({})", self.as_hex())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn id() {
|
|
let id = PrivateID::new();
|
|
assert_ne!(id.as_hex(), "".to_string());
|
|
}
|
|
|
|
#[test]
|
|
fn encode_decode() {
|
|
let id = PrivateID::new();
|
|
let as_hex = id.as_hex();
|
|
let other_id = PrivateID::from_hex(as_hex).unwrap();
|
|
assert_eq!(id, other_id);
|
|
}
|
|
|
|
#[test]
|
|
fn to_public() {
|
|
let id = PrivateID::from_hex(
|
|
"6451f1641112c13e80daa7237d62391b028e638fef9cf91fd7ea347a5290a643".to_string(),
|
|
)
|
|
.unwrap();
|
|
let pub_id = id.as_public();
|
|
assert_eq!(
|
|
pub_id,
|
|
PublicID::from_hex(
|
|
"db3a35df70555653e5fcff5bbc06e1573ad67e00744b56691c15cde72d17c449".to_string()
|
|
)
|
|
.unwrap()
|
|
);
|
|
}
|
|
}
|