Coverage Report

Created: 2024-05-20 06:38

/rust/registry/src/index.crates.io-6f17d22bba15001f/openssl-0.10.62/src/base64.rs
Line
Count
Source (jump to first uncovered line)
1
//! Base64 encoding support.
2
use crate::error::ErrorStack;
3
use crate::{cvt_n, LenType};
4
use libc::c_int;
5
use openssl_macros::corresponds;
6
7
/// Encodes a slice of bytes to a base64 string.
8
///
9
/// # Panics
10
///
11
/// Panics if the input length or computed output length overflow a signed C integer.
12
#[corresponds(EVP_EncodeBlock)]
13
0
pub fn encode_block(src: &[u8]) -> String {
14
0
    assert!(src.len() <= c_int::max_value() as usize);
15
0
    let src_len = src.len() as LenType;
16
0
17
0
    let len = encoded_len(src_len).unwrap();
18
0
    let mut out = Vec::with_capacity(len as usize);
19
0
20
0
    // SAFETY: `encoded_len` ensures space for 4 output characters
21
0
    // for every 3 input bytes including padding and nul terminator.
22
0
    // `EVP_EncodeBlock` will write only single byte ASCII characters.
23
0
    // `EVP_EncodeBlock` will only write to not read from `out`.
24
0
    unsafe {
25
0
        let out_len = ffi::EVP_EncodeBlock(out.as_mut_ptr(), src.as_ptr(), src_len);
26
0
        out.set_len(out_len as usize);
27
0
        String::from_utf8_unchecked(out)
28
0
    }
29
0
}
30
31
/// Decodes a base64-encoded string to bytes.
32
///
33
/// # Panics
34
///
35
/// Panics if the input length or computed output length overflow a signed C integer.
36
#[corresponds(EVP_DecodeBlock)]
37
0
pub fn decode_block(src: &str) -> Result<Vec<u8>, ErrorStack> {
38
0
    let src = src.trim();
39
0
40
0
    // https://github.com/openssl/openssl/issues/12143
41
0
    if src.is_empty() {
42
0
        return Ok(vec![]);
43
0
    }
44
0
45
0
    assert!(src.len() <= c_int::max_value() as usize);
46
0
    let src_len = src.len() as LenType;
47
0
48
0
    let len = decoded_len(src_len).unwrap();
49
0
    let mut out = Vec::with_capacity(len as usize);
50
51
    // SAFETY: `decoded_len` ensures space for 3 output bytes
52
    // for every 4 input characters including padding.
53
    // `EVP_DecodeBlock` can write fewer bytes after stripping
54
    // leading and trailing whitespace, but never more.
55
    // `EVP_DecodeBlock` will only write to not read from `out`.
56
0
    unsafe {
57
0
        let out_len = cvt_n(ffi::EVP_DecodeBlock(
58
0
            out.as_mut_ptr(),
59
0
            src.as_ptr(),
60
0
            src_len,
61
0
        ))?;
62
0
        out.set_len(out_len as usize);
63
0
    }
64
0
65
0
    if src.ends_with('=') {
66
0
        out.pop();
67
0
        if src.ends_with("==") {
68
0
            out.pop();
69
0
        }
70
0
    }
71
72
0
    Ok(out)
73
0
}
74
75
0
fn encoded_len(src_len: LenType) -> Option<LenType> {
76
0
    let mut len = (src_len / 3).checked_mul(4)?;
77
78
0
    if src_len % 3 != 0 {
79
0
        len = len.checked_add(4)?;
80
0
    }
81
82
0
    len = len.checked_add(1)?;
83
84
0
    Some(len)
85
0
}
86
87
0
fn decoded_len(src_len: LenType) -> Option<LenType> {
88
0
    let mut len = (src_len / 4).checked_mul(3)?;
89
90
0
    if src_len % 4 != 0 {
91
0
        len = len.checked_add(3)?;
92
0
    }
93
94
0
    Some(len)
95
0
}
96
97
#[cfg(test)]
98
mod tests {
99
    use super::*;
100
101
    #[test]
102
    fn test_encode_block() {
103
        assert_eq!("".to_string(), encode_block(b""));
104
        assert_eq!("Zg==".to_string(), encode_block(b"f"));
105
        assert_eq!("Zm8=".to_string(), encode_block(b"fo"));
106
        assert_eq!("Zm9v".to_string(), encode_block(b"foo"));
107
        assert_eq!("Zm9vYg==".to_string(), encode_block(b"foob"));
108
        assert_eq!("Zm9vYmE=".to_string(), encode_block(b"fooba"));
109
        assert_eq!("Zm9vYmFy".to_string(), encode_block(b"foobar"));
110
    }
111
112
    #[test]
113
    fn test_decode_block() {
114
        assert_eq!(b"".to_vec(), decode_block("").unwrap());
115
        assert_eq!(b"f".to_vec(), decode_block("Zg==").unwrap());
116
        assert_eq!(b"fo".to_vec(), decode_block("Zm8=").unwrap());
117
        assert_eq!(b"foo".to_vec(), decode_block("Zm9v").unwrap());
118
        assert_eq!(b"foob".to_vec(), decode_block("Zm9vYg==").unwrap());
119
        assert_eq!(b"fooba".to_vec(), decode_block("Zm9vYmE=").unwrap());
120
        assert_eq!(b"foobar".to_vec(), decode_block("Zm9vYmFy").unwrap());
121
    }
122
123
    #[test]
124
    fn test_strip_whitespace() {
125
        assert_eq!(b"foobar".to_vec(), decode_block(" Zm9vYmFy\n").unwrap());
126
        assert_eq!(b"foob".to_vec(), decode_block(" Zm9vYg==\n").unwrap());
127
    }
128
}