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