1use super::{Error, vec::SecureVec};
2use core::ops::Range;
3use zeroize::Zeroize;
4
5#[derive(Clone)]
48pub struct SecureString {
49 vec: SecureVec<u8>,
50}
51
52impl SecureString {
53 pub fn new() -> Result<Self, Error> {
54 let vec = SecureVec::new()?;
55 Ok(SecureString { vec })
56 }
57
58 pub fn new_with_capacity(capacity: usize) -> Result<Self, Error> {
59 let vec = SecureVec::new_with_capacity(capacity)?;
60 Ok(SecureString { vec })
61 }
62
63 pub unsafe fn from_utf8_unchecked(vec: SecureVec<u8>) -> SecureString {
70 SecureString { vec }
71 }
72
73 pub fn erase(&mut self) {
74 self.vec.erase();
75 }
76
77 pub fn byte_len(&self) -> usize {
81 self.vec.len()
82 }
83
84 pub fn is_empty(&self) -> bool {
85 self.vec.is_empty()
86 }
87
88 pub fn drain(&mut self, range: Range<usize>) {
93 self.unlock_str(|s| {
94 assert!(
95 s.is_char_boundary(range.start) && s.is_char_boundary(range.end),
96 "SecureString::drain: range {:?} does not lie on UTF-8 char boundaries",
97 range
98 );
99 });
100 let _d = self.vec.drain(range);
101 }
102
103 pub fn char_len(&self) -> usize {
108 self.unlock_str(|s| s.chars().count())
109 }
110
111 pub fn char_len_unchecked(&self) -> usize {
116 self.unlock_str_unchecked(|s| s.chars().count())
117 }
118
119 pub fn push_str(&mut self, string: &str) {
121 let slice = string.as_bytes();
122 for s in slice.iter() {
123 self.vec.push(*s);
124 }
125 }
126
127 pub fn unlock_str<F, R>(&self, f: F) -> R
132 where
133 F: FnOnce(&str) -> R,
134 {
135 self.vec.unlock_slice(|slice| {
136 let str = core::str::from_utf8(slice)
137 .expect("SecureString invariant violated: internal bytes are not valid UTF-8");
138 f(str)
139 })
140 }
141
142 pub fn unlock_str_unchecked<F, R>(&self, f: F) -> R
149 where
150 F: FnOnce(&str) -> R,
151 {
152 self.vec.unlock_slice(|slice| {
153 let str = unsafe { core::str::from_utf8_unchecked(slice) };
154 f(str)
155 })
156 }
157
158 pub fn unlock_mut<F, R>(&mut self, f: F) -> R
160 where
161 F: FnOnce(&mut SecureString) -> R,
162 {
163 f(self)
164 }
165
166 pub fn insert_text_at_char_idx(&mut self, char_idx: usize, text_to_insert: &str) -> usize {
184 let chars_to_insert_count = text_to_insert.chars().count();
185 if chars_to_insert_count == 0 {
186 return 0;
187 }
188
189 let bytes_to_insert = text_to_insert.as_bytes();
190 let insert_len = bytes_to_insert.len();
191
192 let byte_idx = self
194 .vec
195 .unlock_slice(|current_bytes| char_to_byte_idx(current_bytes, char_idx));
196
197 self.vec.reserve(insert_len);
198
199 let old_byte_len = self.vec.len();
200
201 self.vec.unlock_memory();
203 unsafe {
204 let ptr = self.vec.as_mut_ptr();
205
206 if byte_idx < old_byte_len {
209 core::ptr::copy(
210 ptr.add(byte_idx),
211 ptr.add(byte_idx + insert_len),
212 old_byte_len - byte_idx,
213 );
214 }
215
216 core::ptr::copy_nonoverlapping(
218 bytes_to_insert.as_ptr(),
219 ptr.add(byte_idx),
220 insert_len,
221 );
222
223 self.vec.len += insert_len;
224 }
225
226 self.vec.lock_memory();
227
228 chars_to_insert_count
229 }
230
231 pub fn delete_text_char_range(&mut self, char_range: core::ops::Range<usize>) {
245 if char_range.start >= char_range.end {
246 return;
247 }
248
249 let (byte_start, byte_end) = self.unlock_str_unchecked(|str| {
250 let byte_start = char_to_byte_idx(str.as_bytes(), char_range.start);
251 let byte_end = char_to_byte_idx(str.as_bytes(), char_range.end);
252 (byte_start, byte_end)
253 });
254
255 let new_len = self.vec.unlock_slice_mut(|current_bytes| {
256 if byte_start >= byte_end || byte_end > current_bytes.len() {
257 return 0;
258 }
259
260 let remove_len = byte_end - byte_start;
261 let old_total_len = current_bytes.len();
262
263 current_bytes.copy_within(byte_end..old_total_len, byte_start);
265
266 let new_len = old_total_len - remove_len;
267 for i in new_len..old_total_len {
269 current_bytes[i].zeroize();
270 }
271 new_len
272 });
273 self.vec.len = new_len;
274 }
275}
276
277#[cfg(feature = "use_os")]
278impl From<String> for SecureString {
279 fn from(s: String) -> SecureString {
283 let vec = SecureVec::from_vec(s.into_bytes()).unwrap();
284 SecureString { vec }
285 }
286}
287
288impl From<&str> for SecureString {
289 fn from(s: &str) -> SecureString {
293 let bytes = s.as_bytes();
294 let mut new_vec = SecureVec::new_with_capacity(bytes.len()).unwrap();
296 new_vec.init_from_clone(bytes);
297 SecureString { vec: new_vec }
298 }
299}
300
301impl TryFrom<SecureVec<u8>> for SecureString {
302 type Error = Error;
303
304 fn try_from(vec: SecureVec<u8>) -> Result<Self, Self::Error> {
309 let valid = vec.unlock_slice(|slice| core::str::from_utf8(slice).is_ok());
310 if valid {
311 Ok(SecureString { vec })
312 } else {
313 Err(Error::InvalidUtf8)
314 }
315 }
316}
317
318#[cfg(feature = "serde")]
319impl serde::Serialize for SecureString {
320 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
321 where
322 S: serde::Serializer,
323 {
324 let res = self.unlock_str(|str| serializer.serialize_str(str));
325 res
326 }
327}
328
329#[cfg(feature = "serde")]
330impl<'de> serde::Deserialize<'de> for SecureString {
331 fn deserialize<D>(deserializer: D) -> Result<SecureString, D::Error>
332 where
333 D: serde::Deserializer<'de>,
334 {
335 struct SecureStringVisitor;
336 impl<'de> serde::de::Visitor<'de> for SecureStringVisitor {
337 type Value = SecureString;
338 fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
339 write!(formatter, "an utf-8 encoded string")
340 }
341 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
342 where
343 E: serde::de::Error,
344 {
345 Ok(SecureString::from(v))
346 }
347 }
348 deserializer.deserialize_string(SecureStringVisitor)
349 }
350}
351
352fn char_to_byte_idx(s_bytes: &[u8], char_idx: usize) -> usize {
353 core::str::from_utf8(s_bytes)
354 .ok()
355 .and_then(|s| s.char_indices().nth(char_idx).map(|(idx, _)| idx))
356 .unwrap_or(s_bytes.len()) }
358
359#[cfg(all(test, feature = "use_os"))]
360mod tests {
361 use super::*;
362
363 #[test]
364 fn test_creation() {
365 let hello_world = "Hello, world!";
366 let secure = SecureString::from(hello_world);
367
368 secure.unlock_str(|str| {
369 assert_eq!(str, hello_world);
370 });
371 }
372
373 #[test]
374 fn test_from_string() {
375 let hello_world = String::from("Hello, world!");
376 let string = SecureString::from(hello_world);
377
378 string.unlock_str(|str| {
379 assert_eq!(str, "Hello, world!");
380 });
381 }
382
383 #[test]
384 fn test_try_from_secure_vec() {
385 let hello_world = "Hello, world!".to_string();
386 let vec: SecureVec<u8> = SecureVec::from_slice(hello_world.as_bytes()).unwrap();
387
388 let string = SecureString::from(hello_world);
389 let string2 = SecureString::try_from(vec).unwrap();
390
391 string.unlock_str(|str| {
392 string2.unlock_str(|str2| {
393 assert_eq!(str, str2);
394 });
395 });
396
397 string.unlock_str_unchecked(|str| {
398 string2.unlock_str_unchecked(|str2| {
399 assert_eq!(str, str2);
400 });
401 });
402 }
403
404 #[test]
405 fn test_clone() {
406 let hello_world = "Hello, world!".to_string();
407 let secure1 = SecureString::from(hello_world.clone());
408 let secure2 = secure1.clone();
409
410 secure2.unlock_str(|str| {
411 assert_eq!(str, hello_world);
412 });
413
414 secure2.unlock_str_unchecked(|str| {
415 assert_eq!(str, hello_world);
416 });
417 }
418
419 #[test]
420 fn test_insert_text_at_char_idx() {
421 let hello_world = "My name is ";
422 let mut secure = SecureString::from(hello_world);
423 secure.insert_text_at_char_idx(12, "Mike");
424 secure.unlock_str(|str| {
425 assert_eq!(str, "My name is Mike");
426 });
427
428 secure.unlock_str_unchecked(|str| {
429 assert_eq!(str, "My name is Mike");
430 });
431 }
432
433 #[test]
434 fn test_delete_text_char_range() {
435 let hello_world = "My name is Mike";
436 let mut secure = SecureString::from(hello_world);
437 secure.delete_text_char_range(10..17);
438 secure.unlock_str(|str| {
439 assert_eq!(str, "My name is");
440 });
441
442 secure.unlock_str_unchecked(|str| {
443 assert_eq!(str, "My name is");
444 });
445 }
446
447 #[test]
448 fn test_drain() {
449 let hello_world = "Hello, world!";
450 let mut secure = SecureString::from(hello_world);
451 secure.drain(0..7);
452 secure.unlock_str(|str| {
453 assert_eq!(str, "world!");
454 });
455
456 secure.unlock_str_unchecked(|str| {
457 assert_eq!(str, "world!");
458 });
459 }
460
461 #[cfg(feature = "serde")]
462 #[test]
463 fn test_serde() {
464 let hello_world = "Hello, world!";
465 let secure = SecureString::from(hello_world);
466
467 let json_string = serde_json::to_string(&secure).expect("Serialization failed");
468 let json_bytes = serde_json::to_vec(&secure).expect("Serialization failed");
469
470 let deserialized_string: SecureString =
471 serde_json::from_str(&json_string).expect("Deserialization failed");
472
473 let deserialized_bytes: SecureString =
474 serde_json::from_slice(&json_bytes).expect("Deserialization failed");
475
476 deserialized_string.unlock_str(|str| {
477 assert_eq!(str, hello_world);
478 });
479
480 deserialized_string.unlock_str_unchecked(|str| {
481 assert_eq!(str, hello_world);
482 });
483
484 deserialized_bytes.unlock_str(|str| {
485 assert_eq!(str, hello_world);
486 });
487
488 deserialized_bytes.unlock_str_unchecked(|str| {
489 assert_eq!(str, hello_world);
490 });
491 }
492
493 #[test]
494 fn test_unlock_str() {
495 let hello_word = "Hello, world!";
496 let string = SecureString::from(hello_word);
497 let _exposed_string = string.unlock_str(|str| {
498 assert_eq!(str, hello_word);
499 String::from(str)
500 });
501
502 let _exposed_string = string.unlock_str_unchecked(|str| {
503 assert_eq!(str, hello_word);
504 String::from(str)
505 });
506 }
507
508 #[test]
509 fn test_push_str() {
510 let hello_world = "Hello, world!";
511
512 let mut string = SecureString::new().unwrap();
513 string.push_str(hello_world);
514 string.unlock_str(|str| {
515 assert_eq!(str, hello_world);
516 });
517
518 string.unlock_str_unchecked(|str| {
519 assert_eq!(str, hello_world);
520 });
521 }
522
523 #[test]
524 fn test_unlock_mut() {
525 let hello_world = "Hello, world!";
526 let mut string = SecureString::from("Hello, ");
527 string.unlock_mut(|string| {
528 string.push_str("world!");
529 });
530
531 string.unlock_str(|str| {
532 assert_eq!(str, hello_world);
533 });
534
535 string.unlock_str_unchecked(|str| {
536 assert_eq!(str, hello_world);
537 });
538 }
539}