use super::bencode::{Dict, Value, bytes, entry};
use super::krpc::NodeId;
use ring::signature::{ED25519, Ed25519KeyPair, KeyPair, UnparsedPublicKey};
pub const MAX_VALUE: usize = 1000;
pub struct Keypair(Ed25519KeyPair);
impl Keypair {
pub fn generate() -> Result<Keypair, String> {
let mut seed = [0u8; 32];
super::random(&mut seed)?;
Keypair::from_seed(seed)
}
pub fn from_seed(seed: [u8; 32]) -> Result<Keypair, String> {
Ed25519KeyPair::from_seed_unchecked(&seed)
.map(Keypair)
.map_err(|e| format!("ed25519 seed refused: {e}"))
}
pub fn public(&self) -> [u8; 32] {
<[u8; 32]>::try_from(self.0.public_key().as_ref()).unwrap_or([0u8; 32])
}
pub fn sign(&self, salt: Vec<u8>, seq: i64, value: Vec<u8>) -> Result<Mutable, String> {
let encoded = bytes(&value).encode().len();
if encoded > MAX_VALUE {
return Err(format!(
"value bencodes to {encoded} bytes; BEP 44 allows {MAX_VALUE}"
));
}
let sig = self.0.sign(&signed_bytes(&salt, seq, &value));
let sig = <[u8; 64]>::try_from(sig.as_ref()).unwrap_or([0u8; 64]);
Ok(Mutable {
key: self.public(),
salt,
seq,
value,
sig,
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Mutable {
pub key: [u8; 32],
pub salt: Vec<u8>,
pub seq: i64,
pub value: Vec<u8>,
pub sig: [u8; 64],
}
impl Mutable {
pub fn target(&self) -> NodeId {
target_of(&self.key, &self.salt)
}
pub fn verify(&self) -> bool {
UnparsedPublicKey::new(&ED25519, self.key)
.verify(&signed_bytes(&self.salt, self.seq, &self.value), &self.sig)
.is_ok()
}
pub(crate) fn from_reply(r: &Dict, key: [u8; 32], salt: &[u8]) -> Option<Mutable> {
let sig = <[u8; 64]>::try_from(r.get(b"sig".as_slice())?.as_bytes()?).ok()?;
let item = Mutable {
key,
salt: salt.to_vec(),
seq: r.get(b"seq".as_slice())?.as_int()?,
value: r.get(b"v".as_slice())?.as_bytes()?.to_vec(),
sig,
};
item.verify().then_some(item)
}
pub(crate) fn put_args(&self, token: &[u8]) -> Dict {
let mut args = Dict::from([
entry("k", bytes(&self.key)),
entry("seq", Value::Int(self.seq)),
entry("sig", bytes(&self.sig)),
entry("token", bytes(token)),
entry("v", bytes(&self.value)),
]);
if !self.salt.is_empty() {
args.insert(b"salt".to_vec(), bytes(&self.salt));
}
args
}
}
pub fn target_of(key: &[u8; 32], salt: &[u8]) -> NodeId {
let mut input = key.to_vec();
input.extend_from_slice(salt);
let digest = ring::digest::digest(&ring::digest::SHA1_FOR_LEGACY_USE_ONLY, &input);
NodeId::parse(digest.as_ref()).unwrap_or(NodeId([0u8; 20]))
}
pub(crate) fn signed_bytes(salt: &[u8], seq: i64, value: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
if !salt.is_empty() {
out.extend_from_slice(b"4:salt");
out.extend_from_slice(&bytes(salt).encode());
}
out.extend_from_slice(b"3:seq");
out.extend_from_slice(&Value::Int(seq).encode());
out.extend_from_slice(b"1:v");
out.extend_from_slice(&bytes(value).encode());
out
}
#[cfg(test)]
mod tests;