1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
/* Copyright (c) Fortanix, Inc. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! This crate provides an implementation of Base64 encoding/decoding that is //! designed to be resistant against software side-channel attacks (such as //! timing & cache attacks), see below for details. On certain platforms it //! also uses SIMD making it very fast. This makes it suitable for e.g. //! decoding cryptographic private keys in PEM format. //! //! The API is very similar to the base64 implementation in the old //! rustc-serialize crate, making it easy to use in existing projects. //! //! # Resistance against Software Side-Channel Attacks //! //! An indistinguishable-time (colloquially: constant-time) implementation of //! an algorithm has a runtime that's independent of the data being processed. //! This indistinguishability is usually based on the control flow of the //! program as well as its memory access pattern. In that case, //! indistinguishability may be achieved by making sure the control flow and //! memory access pattern don't depend on the data. Other factors, such as //! instruction cycle count may also be consequential. //! //! See the [BearSSL page on constant-time cryptography] for more information. //! //! The runtime of the implementations in this crate is intended to be //! dependent only on whitespace and the length of the valid data, not the data //! itself. //! //! [BearSSL page on constant-time cryptography]: https://bearssl.org/constanttime.html //! //! # Implementation //! //! Depending on the runtime CPU architecture, this crate uses different //! implementations with different security properties. //! //! * x86 with AVX2: All lookup tables are implemented with SIMD //! instructions. No secret-dependent memory accceses. //! * Other platforms: Lookups are limited to 64-byte aligned lookup tables. On //! platforms with 64-byte cache lines this may be sufficient to prevent //! certain cache side-channel attacks. However, it's known that this is [not //! sufficient for all platforms]. //! //! [not sufficient on some platforms]: https://ts.data61.csiro.au/projects/TS/cachebleed/ #![no_std] #![cfg_attr(all(test, feature = "nightly"), feature(test))] extern crate alloc; #[cfg(any(test, feature = "std"))] #[macro_use] extern crate std; #[cfg(all(test, feature = "nightly"))] extern crate test; #[cfg(test)] #[macro_use] mod test_support; #[macro_use] mod misc; mod avx2; mod lut_align64; mod decode; mod encode; use alloc::{string::String, vec::Vec}; pub use self::CharacterSet::*; /// Available encoding character sets #[derive(Clone, Copy, Debug)] pub enum CharacterSet { /// The standard character set (uses `+` and `/`) Standard, /// The URL safe character set (uses `-` and `_`) UrlSafe, } /// Available newline types #[derive(Clone, Copy, Debug)] pub enum Newline { /// A linefeed (i.e. Unix-style newline) LF, /// A carriage return and a linefeed (i.e. Windows-style newline) CRLF, } /// Contains configuration parameters for `to_base64`. #[derive(Clone, Copy, Debug)] pub struct Config { /// Character set to use pub char_set: CharacterSet, /// Newline to use pub newline: Newline, /// True to pad output with `=` characters pub pad: bool, /// `Some(len)` to wrap lines at `len`, `None` to disable line wrapping pub line_length: Option<usize>, } /// Configuration for RFC 4648 standard base64 encoding pub static STANDARD: Config = Config { char_set: Standard, newline: Newline::CRLF, pad: true, line_length: None, }; /// Configuration for RFC 4648 base64url encoding pub static URL_SAFE: Config = Config { char_set: UrlSafe, newline: Newline::CRLF, pad: false, line_length: None, }; /// Configuration for RFC 2045 MIME base64 encoding pub static MIME: Config = Config { char_set: Standard, newline: Newline::CRLF, pad: true, line_length: Some(76), }; /// A trait for converting a value to base64 encoding. pub trait ToBase64 { /// Converts the value of `self` to a base64 value following the specified /// format configuration, returning the owned string. fn to_base64(&self, config: Config) -> String; } impl ToBase64 for [u8] { /// Turn a vector of `u8` bytes into a base64 string. /// /// # Example /// /// ```rust /// use b64_ct::{ToBase64, STANDARD}; /// /// fn main () { /// let str = [52,32].to_base64(STANDARD); /// println!("base 64 output: {:?}", str); /// } /// ``` fn to_base64(&self, config: Config) -> String { encode::encode64_arch(self, config) } } impl<'a, T: ?Sized + ToBase64> ToBase64 for &'a T { fn to_base64(&self, config: Config) -> String { (**self).to_base64(config) } } #[doc(inline)] pub use decode::Error as FromBase64Error; /// A trait for converting from base64 encoded values. pub trait FromBase64 { /// Converts the value of `self`, interpreted as base64 encoded data, into /// an owned vector of bytes, returning the vector. fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error>; } impl FromBase64 for str { /// Convert any base64 encoded string (literal, `@`, `&`, or `~`) /// to the byte values it encodes. /// /// You can use the `String::from_utf8` function to turn a `Vec<u8>` into a /// string with characters corresponding to those values. /// /// # Example /// /// This converts a string literal to base64 and back. /// /// ```rust /// use b64_ct::{ToBase64, FromBase64, STANDARD}; /// /// fn main () { /// let hello_str = b"Hello, World".to_base64(STANDARD); /// println!("base64 output: {}", hello_str); /// let res = hello_str.from_base64(); /// if res.is_ok() { /// let opt_bytes = String::from_utf8(res.unwrap()); /// if opt_bytes.is_ok() { /// println!("decoded from base64: {:?}", opt_bytes.unwrap()); /// } /// } /// } /// ``` #[inline] fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error> { self.as_bytes().from_base64() } } impl FromBase64 for [u8] { fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error> { decode::decode64_arch(self) } } impl<'a, T: ?Sized + FromBase64> FromBase64 for &'a T { fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error> { (**self).from_base64() } }