1 use crate::errors::*;
2
3 use libsodium_sys::*;
4 use siphasher::sip::SipHasher13;
5 use std::ffi::CStr;
6 use std::hash::Hasher;
7 use std::ptr;
8
9 #[derive(Derivative)]
10 #[derivative(Default)]
11 pub struct Signature(
12 #[derivative(Default(value = "[0u8; crypto_sign_BYTES as usize]"))]
13 [u8; crypto_sign_BYTES as usize],
14 );
15
16 impl Signature {
as_bytes(&self) -> &[u8; crypto_sign_BYTES as usize]17 pub fn as_bytes(&self) -> &[u8; crypto_sign_BYTES as usize] {
18 &self.0
19 }
20
from_bytes(bytes: [u8; crypto_sign_BYTES as usize]) -> Self21 pub fn from_bytes(bytes: [u8; crypto_sign_BYTES as usize]) -> Self {
22 Signature(bytes)
23 }
24 }
25
26 big_array! { BigArray; crypto_sign_SECRETKEYBYTES as usize }
27
28 #[derive(Serialize, Deserialize, Derivative, Clone)]
29 #[derivative(Default)]
30 pub struct SignSK(
31 #[serde(with = "BigArray")]
32 #[derivative(Default(value = "[0u8; crypto_sign_SECRETKEYBYTES as usize]"))]
33 [u8; crypto_sign_SECRETKEYBYTES as usize],
34 );
35
36 impl SignSK {
as_bytes(&self) -> &[u8; crypto_sign_SECRETKEYBYTES as usize]37 pub fn as_bytes(&self) -> &[u8; crypto_sign_SECRETKEYBYTES as usize] {
38 &self.0
39 }
40
from_bytes(bytes: [u8; crypto_sign_SECRETKEYBYTES as usize]) -> Self41 pub fn from_bytes(bytes: [u8; crypto_sign_SECRETKEYBYTES as usize]) -> Self {
42 SignSK(bytes)
43 }
44
sign(&self, bytes: &[u8]) -> Signature45 pub fn sign(&self, bytes: &[u8]) -> Signature {
46 let mut signature = Signature::default();
47 let ret = unsafe {
48 crypto_sign_detached(
49 signature.0.as_mut_ptr(),
50 ptr::null_mut(),
51 bytes.as_ptr(),
52 bytes.len() as _,
53 self.as_bytes().as_ptr(),
54 )
55 };
56 assert_eq!(ret, 0);
57 signature
58 }
59 }
60
61 #[derive(Debug, Serialize, Deserialize, Default, Clone)]
62 pub struct SignPK([u8; crypto_sign_PUBLICKEYBYTES as usize]);
63
64 impl SignPK {
as_bytes(&self) -> &[u8; crypto_sign_PUBLICKEYBYTES as usize]65 pub fn as_bytes(&self) -> &[u8; crypto_sign_PUBLICKEYBYTES as usize] {
66 &self.0
67 }
68
from_bytes(bytes: [u8; crypto_sign_PUBLICKEYBYTES as usize]) -> Self69 pub fn from_bytes(bytes: [u8; crypto_sign_PUBLICKEYBYTES as usize]) -> Self {
70 SignPK(bytes)
71 }
72
as_string(&self) -> String73 pub fn as_string(&self) -> String {
74 bin2hex(self.as_bytes())
75 }
76 }
77
78 #[derive(Derivative, Serialize, Deserialize, Clone)]
79 #[derivative(Debug, Default)]
80 pub struct SignKeyPair {
81 #[derivative(Debug = "ignore")]
82 pub sk: SignSK,
83 pub pk: SignPK,
84 }
85
86 impl SignKeyPair {
new() -> Self87 pub fn new() -> Self {
88 let mut kp = SignKeyPair::default();
89 unsafe { crypto_sign_keypair(kp.pk.0.as_mut_ptr(), kp.sk.0.as_mut_ptr()) };
90 kp
91 }
92 }
93
94 #[derive(Debug, Default, Clone, Serialize, Deserialize)]
95 pub struct CryptSK([u8; crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES as usize]);
96
97 impl CryptSK {
as_bytes( &self, ) -> &[u8; crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES as usize]98 pub fn as_bytes(
99 &self,
100 ) -> &[u8; crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES as usize] {
101 &self.0
102 }
103
from_bytes( bytes: [u8; crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES as usize], ) -> Self104 pub fn from_bytes(
105 bytes: [u8; crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES as usize],
106 ) -> Self {
107 CryptSK(bytes)
108 }
109 }
110
111 #[derive(Debug, Default, Clone, Serialize, Deserialize)]
112 pub struct CryptPK([u8; crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES as usize]);
113
114 impl CryptPK {
as_bytes( &self, ) -> &[u8; crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES as usize]115 pub fn as_bytes(
116 &self,
117 ) -> &[u8; crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES as usize] {
118 &self.0
119 }
120
from_bytes( bytes: [u8; crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES as usize], ) -> Self121 pub fn from_bytes(
122 bytes: [u8; crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES as usize],
123 ) -> Self {
124 CryptPK(bytes)
125 }
126 }
127
128 #[derive(Debug, Default, Clone, Serialize, Deserialize)]
129 pub struct CryptKeyPair {
130 pub sk: CryptSK,
131 pub pk: CryptPK,
132 }
133
134 impl CryptKeyPair {
from_seed( seed: [u8; crypto_box_curve25519xchacha20poly1305_SEEDBYTES as usize], ) -> Self135 pub fn from_seed(
136 seed: [u8; crypto_box_curve25519xchacha20poly1305_SEEDBYTES as usize],
137 ) -> Self {
138 let mut kp = CryptKeyPair::default();
139 unsafe {
140 crypto_box_curve25519xchacha20poly1305_seed_keypair(
141 kp.pk.0.as_mut_ptr(),
142 kp.sk.0.as_mut_ptr(),
143 seed.as_ptr(),
144 )
145 };
146 kp
147 }
148
compute_shared_key(&self, pk: &[u8]) -> Result<SharedKey, Error>149 pub fn compute_shared_key(&self, pk: &[u8]) -> Result<SharedKey, Error> {
150 let mut shared_key = SharedKey::default();
151 let res = unsafe {
152 crypto_box_curve25519xchacha20poly1305_beforenm(
153 shared_key.0.as_mut_ptr(),
154 pk.as_ptr(),
155 self.sk.0.as_ptr(),
156 )
157 };
158 ensure!(res == 0, "Weak public key");
159 Ok(shared_key)
160 }
161 }
162
163 #[derive(Debug, Clone, Default)]
164 pub struct SharedKey([u8; crypto_box_curve25519xchacha20poly1305_BEFORENMBYTES as usize]);
165
166 impl SharedKey {
decrypt(&self, nonce: &[u8], encrypted: &[u8]) -> Result<Vec<u8>, Error>167 pub fn decrypt(&self, nonce: &[u8], encrypted: &[u8]) -> Result<Vec<u8>, Error> {
168 let encrypted_len = encrypted.len();
169 let mut decrypted =
170 vec![0u8; encrypted_len - crypto_box_curve25519xchacha20poly1305_MACBYTES as usize];
171 let res = unsafe {
172 libsodium_sys::crypto_box_curve25519xchacha20poly1305_open_easy_afternm(
173 decrypted.as_mut_ptr(),
174 encrypted.as_ptr(),
175 encrypted_len as _,
176 nonce.as_ptr(),
177 self.0.as_ptr(),
178 )
179 };
180 ensure!(res == 0, "Unable to decrypt");
181 let idx = decrypted
182 .iter()
183 .rposition(|x| *x != 0x00)
184 .ok_or_else(|| anyhow!("Padding error"))?;
185 ensure!(decrypted[idx] == 0x80, "Padding error");
186 decrypted.truncate(idx);
187 Ok(decrypted)
188 }
189
encrypt_into( &self, target: &mut Vec<u8>, nonce: &[u8], client_nonce: &[u8], plaintext: Vec<u8>, max_target_size: usize, ) -> Result<(), Error>190 pub fn encrypt_into(
191 &self,
192 target: &mut Vec<u8>,
193 nonce: &[u8],
194 client_nonce: &[u8],
195 plaintext: Vec<u8>,
196 max_target_size: usize,
197 ) -> Result<(), Error> {
198 ensure!(
199 max_target_size >= crypto_box_curve25519xchacha20poly1305_MACBYTES as usize,
200 "Max target size too small"
201 );
202 let plaintext_len = plaintext.len();
203 let max_padded_plaintext_len =
204 max_target_size - crypto_box_curve25519xchacha20poly1305_MACBYTES as usize;
205 let mut hasher = SipHasher13::new();
206 hasher.write(&self.0);
207 hasher.write(client_nonce);
208 let pad_size: usize = 1 + (hasher.finish() as usize & 0xff);
209 let mut padded_plaintext_len = (plaintext_len + pad_size) & !63;
210 if padded_plaintext_len < plaintext_len {
211 padded_plaintext_len += 256;
212 }
213 if padded_plaintext_len > max_padded_plaintext_len {
214 padded_plaintext_len = max_padded_plaintext_len;
215 }
216 ensure!(padded_plaintext_len > plaintext_len, "No room for padding");
217 let mut padded_plaintext = plaintext;
218 padded_plaintext.push(0x80);
219 while padded_plaintext.len() != padded_plaintext_len {
220 padded_plaintext.push(0x00);
221 }
222 let padded_plaintext_len = padded_plaintext.len();
223 let target_header_len = target.len();
224 target.resize(
225 target_header_len
226 + padded_plaintext_len
227 + crypto_box_curve25519xchacha20poly1305_MACBYTES as usize,
228 0,
229 );
230 let encrypted = &mut target[target_header_len..];
231 let res = unsafe {
232 libsodium_sys::crypto_box_curve25519xchacha20poly1305_easy_afternm(
233 encrypted.as_mut_ptr(),
234 padded_plaintext.as_ptr(),
235 padded_plaintext_len as _,
236 nonce.as_ptr(),
237 self.0.as_ptr(),
238 )
239 };
240 ensure!(res == 0, "Unable to encrypt");
241 Ok(())
242 }
243 }
244
bin2hex(bin: &[u8]) -> String245 pub fn bin2hex(bin: &[u8]) -> String {
246 let bin_len = bin.len();
247 let hex_len = bin_len * 2 + 1;
248 let mut hex = vec![0u8; hex_len];
249 unsafe {
250 sodium_bin2hex(hex.as_mut_ptr() as *mut _, hex_len, bin.as_ptr(), bin_len);
251 }
252 CStr::from_bytes_with_nul(&hex)
253 .unwrap()
254 .to_str()
255 .unwrap()
256 .to_string()
257 }
258
init() -> Result<(), Error>259 pub fn init() -> Result<(), Error> {
260 let res = unsafe { sodium_init() };
261 ensure!(res >= 0, "Unable to initialize libsodium");
262 Ok(())
263 }
264