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}