1use std::fmt::Debug;
2use std::ops::{Deref, DerefMut, Index, IndexMut, Range, RangeFrom, RangeFull, RangeTo};
3
4use crate::platform::{mlock, munlock};
5
6pub struct CryptoVec {
9 p: *mut u8, size: usize,
11 capacity: usize,
12}
13
14impl Debug for CryptoVec {
15 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16 if self.size == 0 {
17 return f.write_str("<empty>");
18 }
19 write!(f, "<{:?}>", self.size)
20 }
21}
22
23impl Unpin for CryptoVec {}
24unsafe impl Send for CryptoVec {}
25unsafe impl Sync for CryptoVec {}
26
27impl AsRef<[u8]> for CryptoVec {
29 fn as_ref(&self) -> &[u8] {
30 self.deref()
31 }
32}
33
34impl AsMut<[u8]> for CryptoVec {
35 fn as_mut(&mut self) -> &mut [u8] {
36 self.deref_mut()
37 }
38}
39
40impl Deref for CryptoVec {
41 type Target = [u8];
42 fn deref(&self) -> &[u8] {
43 unsafe { std::slice::from_raw_parts(self.p, self.size) }
44 }
45}
46
47impl DerefMut for CryptoVec {
48 fn deref_mut(&mut self) -> &mut [u8] {
49 unsafe { std::slice::from_raw_parts_mut(self.p, self.size) }
50 }
51}
52
53impl From<String> for CryptoVec {
54 fn from(e: String) -> Self {
55 CryptoVec::from(e.into_bytes())
56 }
57}
58
59impl From<&str> for CryptoVec {
60 fn from(e: &str) -> Self {
61 CryptoVec::from(e.as_bytes())
62 }
63}
64
65impl From<&[u8]> for CryptoVec {
66 fn from(e: &[u8]) -> Self {
67 CryptoVec::from_slice(e)
68 }
69}
70
71impl From<Vec<u8>> for CryptoVec {
72 fn from(e: Vec<u8>) -> Self {
73 let mut c = CryptoVec::new_zeroed(e.len());
74 c.clone_from_slice(&e[..]);
75 c
76 }
77}
78
79impl Index<RangeFrom<usize>> for CryptoVec {
81 type Output = [u8];
82 fn index(&self, index: RangeFrom<usize>) -> &[u8] {
83 self.deref().index(index)
84 }
85}
86impl Index<RangeTo<usize>> for CryptoVec {
87 type Output = [u8];
88 fn index(&self, index: RangeTo<usize>) -> &[u8] {
89 self.deref().index(index)
90 }
91}
92impl Index<Range<usize>> for CryptoVec {
93 type Output = [u8];
94 fn index(&self, index: Range<usize>) -> &[u8] {
95 self.deref().index(index)
96 }
97}
98impl Index<RangeFull> for CryptoVec {
99 type Output = [u8];
100 fn index(&self, _: RangeFull) -> &[u8] {
101 self.deref()
102 }
103}
104
105impl IndexMut<RangeFull> for CryptoVec {
106 fn index_mut(&mut self, _: RangeFull) -> &mut [u8] {
107 self.deref_mut()
108 }
109}
110impl IndexMut<RangeFrom<usize>> for CryptoVec {
111 fn index_mut(&mut self, index: RangeFrom<usize>) -> &mut [u8] {
112 self.deref_mut().index_mut(index)
113 }
114}
115impl IndexMut<RangeTo<usize>> for CryptoVec {
116 fn index_mut(&mut self, index: RangeTo<usize>) -> &mut [u8] {
117 self.deref_mut().index_mut(index)
118 }
119}
120impl IndexMut<Range<usize>> for CryptoVec {
121 fn index_mut(&mut self, index: Range<usize>) -> &mut [u8] {
122 self.deref_mut().index_mut(index)
123 }
124}
125
126impl Index<usize> for CryptoVec {
127 type Output = u8;
128 fn index(&self, index: usize) -> &u8 {
129 self.deref().index(index)
130 }
131}
132
133impl std::io::Write for CryptoVec {
135 fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
136 self.extend(buf);
137 Ok(buf.len())
138 }
139
140 fn flush(&mut self) -> Result<(), std::io::Error> {
141 Ok(())
142 }
143}
144
145impl Default for CryptoVec {
147 fn default() -> Self {
148 CryptoVec {
149 p: std::ptr::NonNull::dangling().as_ptr(),
150 size: 0,
151 capacity: 0,
152 }
153 }
154}
155
156const MAX_CAPACITY: usize = 1usize << (usize::BITS - 2);
157
158#[cold]
159#[inline(never)]
160#[allow(clippy::panic)]
161fn capacity_overflow(len: usize) -> ! {
162 panic!("CryptoVec capacity overflow: {len}")
163}
164
165#[cold]
166#[inline(never)]
167#[allow(clippy::panic)]
168fn length_overflow(lhs: usize, rhs: usize) -> ! {
169 panic!("CryptoVec length overflow: {lhs} + {rhs}")
170}
171
172#[cold]
173#[inline(never)]
174fn alloc_failed(layout: std::alloc::Layout) -> ! {
175 std::alloc::handle_alloc_error(layout)
176}
177
178#[inline]
179fn checked_capacity(len: usize) -> usize {
180 if len > MAX_CAPACITY {
181 capacity_overflow(len);
182 }
183 len.next_power_of_two()
184}
185
186#[inline]
187unsafe fn alloc_zeroed(capacity: usize) -> *mut u8 {
188 debug_assert!(capacity > 0);
189 let layout = unsafe { std::alloc::Layout::from_size_align_unchecked(capacity, 1) };
190 let p = unsafe { std::alloc::alloc_zeroed(layout) };
191 if p.is_null() {
192 alloc_failed(layout);
193 }
194 let _ = mlock(p, capacity);
195 p
196}
197
198#[inline]
199fn checked_len_sum(lhs: usize, rhs: usize) -> usize {
200 let sum = lhs.wrapping_add(rhs);
201 if sum < lhs {
202 length_overflow(lhs, rhs);
203 }
204 sum
205}
206
207impl CryptoVec {
208 pub fn new() -> CryptoVec {
210 CryptoVec::default()
211 }
212
213 pub fn new_zeroed(size: usize) -> CryptoVec {
215 if size == 0 {
216 return CryptoVec::default();
217 }
218
219 let capacity = checked_capacity(size);
220 let p = unsafe { alloc_zeroed(capacity) };
221 CryptoVec { p, capacity, size }
222 }
223
224 pub fn with_capacity(capacity: usize) -> CryptoVec {
226 if capacity == 0 {
227 return CryptoVec::default();
228 }
229
230 let capacity = checked_capacity(capacity);
231 let p = unsafe { alloc_zeroed(capacity) };
232 CryptoVec {
233 p,
234 capacity,
235 size: 0,
236 }
237 }
238
239 pub fn len(&self) -> usize {
245 self.size
246 }
247
248 pub fn is_empty(&self) -> bool {
254 self.len() == 0
255 }
256
257 pub fn resize(&mut self, size: usize) {
261 if size <= self.capacity && size > self.size {
262 self.size = size
264 } else if size <= self.size {
265 unsafe {
268 zeroize(self.p.add(size), self.size - size);
269 }
270 self.size = size;
271 } else {
272 unsafe {
274 let next_capacity = checked_capacity(size);
275 let old_ptr = self.p;
276 let next_ptr = alloc_zeroed(next_capacity);
277
278 if self.capacity > 0 {
279 std::ptr::copy_nonoverlapping(old_ptr, next_ptr, self.size);
280 zeroize(old_ptr, self.size);
281 let _ = munlock(old_ptr, self.capacity);
282 let layout = std::alloc::Layout::from_size_align_unchecked(self.capacity, 1);
283 std::alloc::dealloc(old_ptr, layout);
284 }
285
286 self.p = next_ptr;
287 self.capacity = next_capacity;
288 self.size = size;
289 }
290 }
291 }
292
293 pub fn clear(&mut self) {
302 self.resize(0);
303 }
304
305 pub fn push(&mut self, s: u8) {
307 let size = self.size;
308 self.resize(checked_len_sum(size, 1));
309 unsafe { *self.p.add(size) = s }
310 }
311
312 pub fn read<R: std::io::Read>(
315 &mut self,
316 n_bytes: usize,
317 mut r: R,
318 ) -> Result<usize, std::io::Error> {
319 let cur_size = self.size;
320 let target_size = checked_len_sum(cur_size, n_bytes);
321 self.resize(target_size);
322 let s = unsafe { std::slice::from_raw_parts_mut(self.p.add(cur_size), n_bytes) };
323 match r.read(s) {
325 Ok(n) => {
326 self.resize(cur_size + n);
327 Ok(n)
328 }
329 Err(e) => {
330 self.resize(cur_size);
331 Err(e)
332 }
333 }
334 }
335
336 pub fn write_all_from<W: std::io::Write>(
346 &self,
347 offset: usize,
348 mut w: W,
349 ) -> Result<usize, std::io::Error> {
350 assert!(offset < self.size);
351 unsafe {
353 let s = std::slice::from_raw_parts(self.p.add(offset), self.size - offset);
354 w.write(s)
355 }
356 }
357
358 pub fn resize_mut(&mut self, n: usize) -> &mut [u8] {
365 let size = self.size;
366 self.resize(checked_len_sum(size, n));
367 unsafe { std::slice::from_raw_parts_mut(self.p.add(size), n) }
368 }
369
370 pub fn extend(&mut self, s: &[u8]) {
377 let size = self.size;
378 let added = s.len();
379 self.resize(checked_len_sum(size, added));
380 unsafe {
381 std::ptr::copy_nonoverlapping(s.as_ptr(), self.p.add(size), s.len());
382 }
383 }
384
385 pub fn from_slice(s: &[u8]) -> CryptoVec {
391 let mut v = CryptoVec::new();
392 v.resize(s.len());
393 unsafe {
394 std::ptr::copy_nonoverlapping(s.as_ptr(), v.p, s.len());
395 }
396 v
397 }
398}
399
400impl Clone for CryptoVec {
401 fn clone(&self) -> Self {
402 let mut v = Self::new();
403 v.extend(self);
404 v
405 }
406}
407
408impl Drop for CryptoVec {
410 fn drop(&mut self) {
411 if self.capacity > 0 {
412 unsafe {
413 zeroize(self.p, self.size);
414 let _ = munlock(self.p, self.capacity);
415 let layout = std::alloc::Layout::from_size_align_unchecked(self.capacity, 1);
416 std::alloc::dealloc(self.p, layout);
417 }
418 }
419 }
420}
421
422unsafe fn zeroize(dst: *mut u8, size: usize) {
423 unsafe {
424 std::ptr::write_bytes(dst, 0, size);
425 }
426 optimization_barrier(dst, size);
427}
428
429fn optimization_barrier(dst: *mut u8, size: usize) {
431 #[cfg(all(
432 not(miri),
433 any(
434 target_arch = "aarch64",
435 target_arch = "arm",
436 target_arch = "arm64ec",
437 target_arch = "loongarch64",
438 target_arch = "riscv32",
439 target_arch = "riscv64",
440 target_arch = "s390x",
441 target_arch = "x86",
442 target_arch = "x86_64",
443 )
444 ))]
445 {
446 let _ = size;
447 unsafe {
448 core::arch::asm!(
449 "# {}",
450 in(reg) dst,
451 options(readonly, preserves_flags, nostack),
452 );
453 }
454 }
455 #[cfg(not(all(
456 not(miri),
457 any(
458 target_arch = "aarch64",
459 target_arch = "arm",
460 target_arch = "arm64ec",
461 target_arch = "loongarch64",
462 target_arch = "riscv32",
463 target_arch = "riscv64",
464 target_arch = "s390x",
465 target_arch = "x86",
466 target_arch = "x86_64",
467 )
468 )))]
469 {
470 #[inline(never)]
473 fn custom_black_box(p: *const u8) {
474 let _ = unsafe { core::ptr::read_volatile(p) };
475 }
476
477 core::hint::black_box(dst);
478 if size > 0 {
479 custom_black_box(dst);
480 }
481 }
482}
483
484#[cfg(test)]
485mod test {
486 use super::{CryptoVec, checked_capacity};
487
488 #[test]
489 fn test_new() {
490 let crypto_vec = CryptoVec::new();
491 assert_eq!(crypto_vec.size, 0);
492 assert_eq!(crypto_vec.capacity, 0);
493 }
494
495 #[test]
496 fn test_resize_expand() {
497 let mut crypto_vec = CryptoVec::new_zeroed(5);
498 crypto_vec.resize(10);
499 assert_eq!(crypto_vec.size, 10);
500 assert!(crypto_vec.capacity >= 10);
501 assert!(crypto_vec.iter().skip(5).all(|&x| x == 0)); }
503
504 #[test]
505 fn test_resize_shrink() {
506 let mut crypto_vec = CryptoVec::new_zeroed(10);
507 crypto_vec.resize(5);
508 assert_eq!(crypto_vec.size, 5);
509 assert_eq!(crypto_vec.len(), 5);
511 }
512
513 #[test]
514 fn test_resize_zero() {
515 let mut crypto_vec = CryptoVec::new();
516 crypto_vec.resize(0);
517 assert_eq!(crypto_vec.size, 0);
518 assert_eq!(crypto_vec.len(), 0);
519 }
520
521 #[test]
522 fn test_push() {
523 let mut crypto_vec = CryptoVec::new();
524 crypto_vec.push(1);
525 crypto_vec.push(2);
526 assert_eq!(crypto_vec.size, 2);
527 assert_eq!(crypto_vec[0], 1);
528 assert_eq!(crypto_vec[1], 2);
529 }
530
531 #[test]
532 fn test_write_trait() {
533 use std::io::Write;
534
535 let mut crypto_vec = CryptoVec::new();
536 let bytes_written = crypto_vec.write(&[1, 2, 3]).unwrap();
537 assert_eq!(bytes_written, 3);
538 assert_eq!(crypto_vec.size, 3);
539 assert_eq!(crypto_vec.as_ref(), &[1, 2, 3]);
540 }
541
542 #[test]
543 fn test_as_ref_as_mut() {
544 let mut crypto_vec = CryptoVec::new_zeroed(5);
545 let slice_ref: &[u8] = crypto_vec.as_ref();
546 assert_eq!(slice_ref.len(), 5);
547 let slice_mut: &mut [u8] = crypto_vec.as_mut();
548 slice_mut[0] = 1;
549 assert_eq!(crypto_vec[0], 1);
550 }
551
552 #[test]
553 fn test_from_string() {
554 let input = String::from("hello");
555 let crypto_vec: CryptoVec = input.into();
556 assert_eq!(crypto_vec.as_ref(), b"hello");
557 }
558
559 #[test]
560 fn test_from_str() {
561 let input = "hello";
562 let crypto_vec: CryptoVec = input.into();
563 assert_eq!(crypto_vec.as_ref(), b"hello");
564 }
565
566 #[test]
567 fn test_from_byte_slice() {
568 let input = b"hello".as_slice();
569 let crypto_vec: CryptoVec = input.into();
570 assert_eq!(crypto_vec.as_ref(), b"hello");
571 }
572
573 #[test]
574 fn test_from_vec() {
575 let input = vec![1, 2, 3, 4];
576 let crypto_vec: CryptoVec = input.into();
577 assert_eq!(crypto_vec.as_ref(), &[1, 2, 3, 4]);
578 }
579
580 #[test]
581 fn test_index() {
582 let crypto_vec = CryptoVec::from(vec![1, 2, 3, 4, 5]);
583 assert_eq!(crypto_vec[0], 1);
584 assert_eq!(crypto_vec[4], 5);
585 assert_eq!(&crypto_vec[1..3], &[2, 3]);
586 }
587
588 #[test]
589 fn test_drop() {
590 let mut crypto_vec = CryptoVec::new_zeroed(10);
591 crypto_vec.extend(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
593 drop(crypto_vec);
594
595 }
600
601 #[test]
602 fn test_new_zeroed() {
603 let crypto_vec = CryptoVec::new_zeroed(10);
604 assert_eq!(crypto_vec.size, 10);
605 assert!(crypto_vec.capacity >= 10);
606 assert!(crypto_vec.iter().all(|&x| x == 0)); }
608
609 #[test]
610 fn test_clear() {
611 let mut crypto_vec = CryptoVec::new();
612 crypto_vec.extend(b"blabla");
613 crypto_vec.clear();
614 assert!(crypto_vec.is_empty());
615 }
616
617 #[test]
618 fn test_with_capacity_zero() {
619 let crypto_vec = CryptoVec::with_capacity(0);
620 assert_eq!(crypto_vec.size, 0);
621 assert_eq!(crypto_vec.capacity, 0);
622 }
623
624 #[test]
625 fn test_new_zeroed_zero() {
626 let crypto_vec = CryptoVec::new_zeroed(0);
627 assert_eq!(crypto_vec.size, 0);
628 assert_eq!(crypto_vec.capacity, 0);
629 }
630
631 #[test]
632 fn test_extend() {
633 let mut crypto_vec = CryptoVec::new();
634 crypto_vec.extend(b"test");
635 assert_eq!(crypto_vec.as_ref(), b"test");
636 }
637
638 #[test]
639 #[should_panic(expected = "CryptoVec capacity overflow")]
640 fn test_checked_capacity_overflow_panics() {
641 let _ = checked_capacity(usize::MAX);
642 }
643
644 #[test]
645 #[should_panic(expected = "CryptoVec capacity overflow")]
646 fn test_checked_capacity_rejects_values_above_max_capacity() {
647 let _ = checked_capacity(super::MAX_CAPACITY + 1);
648 }
649
650 #[test]
651 fn test_write_all_from() {
652 let mut crypto_vec = CryptoVec::new();
653 crypto_vec.extend(b"blabla");
654
655 let mut output: Vec<u8> = Vec::new();
656 let written_size = crypto_vec.write_all_from(0, &mut output).unwrap();
657 assert_eq!(written_size, 6); assert_eq!(output, b"blabla");
659 }
660
661 #[test]
662 fn test_resize_mut() {
663 let mut crypto_vec = CryptoVec::new();
664 crypto_vec.resize_mut(4).clone_from_slice(b"test");
665 assert_eq!(crypto_vec.as_ref(), b"test");
666 }
667
668 #[cfg(target_arch = "wasm32")]
670 mod wasm32 {
671 use wasm_bindgen_test::wasm_bindgen_test;
672
673 use super::*;
674
675 wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
676
677 #[wasm_bindgen_test]
678 fn test_push_u32_be() {
679 let mut crypto_vec = CryptoVec::new();
680 let value = 43554u32;
681 crypto_vec.push_u32_be(value);
682 assert_eq!(crypto_vec.len(), 4); assert_eq!(crypto_vec.read_u32_be(0), value);
684 }
685
686 #[wasm_bindgen_test]
687 fn test_read_u32_be() {
688 let mut crypto_vec = CryptoVec::new();
689 let value = 99485710u32;
690 crypto_vec.push_u32_be(value);
691 assert_eq!(crypto_vec.read_u32_be(0), value);
692 }
693 }
694}