mainline/common/
mutable.rs1use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
4use serde::{Deserialize, Serialize};
5use sha1_smol::Sha1;
6use std::convert::TryFrom;
7
8use crate::Id;
9
10use super::PutMutableRequestArguments;
11
12#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
13pub struct MutableItem {
15 target: Id,
17 key: [u8; 32],
19 pub(crate) seq: i64,
21 pub(crate) value: Box<[u8]>,
23 #[serde(with = "serde_bytes")]
25 signature: [u8; 64],
26 salt: Option<Box<[u8]>>,
28}
29
30impl MutableItem {
31 pub fn new(signer: SigningKey, value: &[u8], seq: i64, salt: Option<&[u8]>) -> Self {
33 let signable = encode_signable(seq, value, salt);
34 let signature = signer.sign(&signable);
35
36 Self::new_signed_unchecked(
37 signer.verifying_key().to_bytes(),
38 signature.into(),
39 value,
40 seq,
41 salt,
42 )
43 }
44
45 pub fn target_from_key(public_key: &[u8; 32], salt: Option<&[u8]>) -> Id {
47 let mut encoded = vec![];
48
49 encoded.extend(public_key);
50
51 if let Some(salt) = salt {
52 encoded.extend(salt);
53 }
54
55 let mut hasher = Sha1::new();
56 hasher.update(&encoded);
57 let bytes = hasher.digest().bytes();
58
59 bytes.into()
60 }
61
62 pub fn new_signed_unchecked(
64 key: [u8; 32],
65 signature: [u8; 64],
66 value: &[u8],
67 seq: i64,
68 salt: Option<&[u8]>,
69 ) -> Self {
70 Self {
71 target: MutableItem::target_from_key(&key, salt),
72 key,
73 value: value.into(),
74 seq,
75 signature,
76 salt: salt.map(|s| s.into()),
77 }
78 }
79
80 pub(crate) fn from_dht_message(
81 target: Id,
82 key: &[u8],
83 v: Box<[u8]>,
84 seq: i64,
85 signature: &[u8],
86 salt: Option<Box<[u8]>>,
87 ) -> Result<Self, MutableError> {
88 let key = VerifyingKey::try_from(key).map_err(|_| MutableError::InvalidMutablePublicKey)?;
89
90 let signature =
91 Signature::from_slice(signature).map_err(|_| MutableError::InvalidMutableSignature)?;
92
93 key.verify(&encode_signable(seq, &v, salt.as_deref()), &signature)
94 .map_err(|_| MutableError::InvalidMutableSignature)?;
95
96 Ok(Self {
97 target,
98 key: key.to_bytes(),
99 value: v,
100 seq,
101 signature: signature.to_bytes(),
102 salt,
103 })
104 }
105
106 pub fn target(&self) -> &Id {
110 &self.target
111 }
112
113 pub fn key(&self) -> &[u8; 32] {
115 &self.key
116 }
117
118 pub fn value(&self) -> &[u8] {
120 &self.value
121 }
122
123 pub fn seq(&self) -> i64 {
125 self.seq
126 }
127
128 pub fn signature(&self) -> &[u8; 64] {
130 &self.signature
131 }
132
133 pub fn salt(&self) -> Option<&[u8]> {
136 self.salt.as_deref()
137 }
138}
139
140pub fn encode_signable(seq: i64, value: &[u8], salt: Option<&[u8]>) -> Box<[u8]> {
141 let mut signable = vec![];
142
143 if let Some(salt) = salt {
144 signable.extend(format!("4:salt{}:", salt.len()).into_bytes());
145 signable.extend(salt);
146 }
147
148 signable.extend(format!("3:seqi{}e1:v{}:", seq, value.len()).into_bytes());
149 signable.extend(value);
150
151 signable.into()
152}
153
154#[derive(thiserror::Error, Debug)]
155pub enum MutableError {
157 #[error("Invalid mutable item signature")]
158 InvalidMutableSignature,
160
161 #[error("Invalid mutable item public key")]
162 InvalidMutablePublicKey,
164}
165
166impl PutMutableRequestArguments {
167 pub fn from(item: MutableItem, cas: Option<i64>) -> Self {
171 Self {
172 target: item.target,
173 v: item.value,
174 k: item.key,
175 seq: item.seq,
176 sig: item.signature,
177 salt: item.salt,
178 cas,
179 }
180 }
181}
182
183impl From<PutMutableRequestArguments> for MutableItem {
184 fn from(request: PutMutableRequestArguments) -> Self {
185 Self {
186 target: request.target,
187 value: request.v,
188 key: request.k,
189 seq: request.seq,
190 signature: request.sig,
191 salt: request.salt,
192 }
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn signable_without_salt() {
202 let signable = encode_signable(4, b"Hello world!", None);
203
204 assert_eq!(&*signable, b"3:seqi4e1:v12:Hello world!");
205 }
206 #[test]
207 fn signable_with_salt() {
208 let signable = encode_signable(4, b"Hello world!", Some(b"foobar"));
209
210 assert_eq!(&*signable, b"4:salt6:foobar3:seqi4e1:v12:Hello world!");
211 }
212}