pub struct Output { /* private fields */ }
Expand description

Output from password hashing functions, i.e. the “hash” or “digest” as raw bytes.

The Output type implements the RECOMMENDED best practices described in the PHC string format specification, namely:

The hash output, for a verification, must be long enough to make preimage attacks at least as hard as password guessing. To promote wide acceptance, a default output size of 256 bits (32 bytes, encoded as 43 characters) is recommended. Function implementations SHOULD NOT allow outputs of less than 80 bits to be used for password verification.

Per the description above, the recommended default length for an Output of a password hashing function is 32-bytes (256-bits).

Constraints

The above guidelines are interpreted into the following constraints:

  • Minimum length: 10-bytes (80-bits)
  • Maximum length: 64-bytes (512-bits)

The specific recommendation of a 64-byte maximum length is taken as a best practice from the hash output guidelines for Argon2 Encoding given in the same document:

The hash output…length shall be between 12 and 64 bytes (16 and 86 characters, respectively). The default output length is 32 bytes (43 characters).

Based on this guidance, this type enforces an upper bound of 64-bytes as a reasonable maximum, and recommends using 32-bytes.

Constant-time comparisons

The Output type impls the ConstantTimeEq trait from the subtle crate and uses it to perform constant-time comparisons.

Additionally the PartialEq and Eq trait impls for Output use ConstantTimeEq when performing comparisons.

Attacks on non-constant-time password hash comparisons

Comparing password hashes in constant-time is known to mitigate at least one poorly understood attack involving an adversary with the following knowledge/capabilities:

  • full knowledge of what password hashing algorithm is being used including any relevant configurable parameters
  • knowledge of the salt for a particular victim
  • ability to accurately measure a timing side-channel on comparisons of the password hash over the network

An attacker with the above is able to perform an offline computation of the hash for any chosen password in such a way that it will match the hash computed by the server.

As noted above, they also measure timing variability in the server’s comparison of the hash it computes for a given password and a target hash the attacker is trying to learn.

When the attacker observes a hash comparison that takes longer than their previous attempts, they learn that they guessed another byte in the password hash correctly. They can leverage repeated measurements and observations with different candidate passwords to learn the password hash a byte-at-a-time in a manner similar to other such timing side-channel attacks.

The attack may seem somewhat counterintuitive since learning prefixes of a password hash does not reveal any additional information about the password itself. However, the above can be combined with an offline dictionary attack where the attacker is able to determine candidate passwords to send to the server by performing a brute force search offline and selecting candidate passwords whose hashes match the portion of the prefix they have learned so far.

As the attacker learns a longer and longer prefix of the password hash, they are able to more effectively eliminate candidate passwords offline as part of a dictionary attack, until they eventually guess the correct password or exhaust their set of candidate passwords.

Mitigations

While we have taken care to ensure password hashes are compared in constant time, we would also suggest preventing such attacks by using randomly generated salts and keeping those salts secret.

The SaltString::generate function can be used to generate random high-entropy salt values.

Implementations§

§

impl Output

pub const MIN_LENGTH: usize = 10usize

Minimum length of a Output string: 10-bytes.

pub const MAX_LENGTH: usize = 64usize

Maximum length of Output string: 64-bytes.

See type-level documentation about Output for more information.

pub const B64_MAX_LENGTH: usize = 86usize

Maximum length of Output when encoded as B64 string: 86-bytes (i.e. 86 ASCII characters)

pub fn new(input: &[u8]) -> Result<Output, Error>

Create a Output from the given byte slice, validating it according to Output::MIN_LENGTH and Output::MAX_LENGTH restrictions.

pub fn new_with_encoding( input: &[u8], encoding: Encoding ) -> Result<Output, Error>

Create a Output from the given byte slice and Encoding, validating it according to Output::MIN_LENGTH and Output::MAX_LENGTH restrictions.

pub fn init_with<F>(output_size: usize, f: F) -> Result<Output, Error>
where F: FnOnce(&mut [u8]) -> Result<(), Error>,

Initialize an Output using the provided method, which is given a mutable byte slice into which it should write the output.

The output_size (in bytes) must be known in advance, as well as at least Output::MIN_LENGTH bytes and at most Output::MAX_LENGTH bytes.

pub fn as_bytes(&self) -> &[u8]

Borrow the output value as a byte slice.

pub fn encoding(&self) -> Encoding

Get the Encoding that this Output is serialized with.

pub fn len(&self) -> usize

Get the length of the output value as a byte slice.

pub fn b64_decode(input: &str) -> Result<Output, Error>

Parse B64-encoded Output, i.e. using the PHC string specification’s restricted interpretation of Base64.

pub fn b64_encode<'a>(&self, out: &'a mut [u8]) -> Result<&'a str, Error>

Write B64-encoded Output to the provided buffer, returning a sub-slice containing the encoded data.

Returns an error if the buffer is too short to contain the output.

pub fn decode(input: &str, encoding: Encoding) -> Result<Output, Error>

Decode the given input string using the specified Encoding.

pub fn encode<'a>( &self, out: &'a mut [u8], encoding: Encoding ) -> Result<&'a str, Error>

Encode this Output using the specified Encoding.

pub fn b64_len(&self) -> usize

Get the length of this Output when encoded as B64.

Trait Implementations§

§

impl AsRef<[u8]> for Output

§

fn as_ref(&self) -> &[u8]

Converts this type into a shared reference of the (usually inferred) input type.
§

impl Clone for Output

§

fn clone(&self) -> Output

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl ConstantTimeEq for Output

§

fn ct_eq(&self, other: &Output) -> Choice

Determine if two items are equal. Read more
source§

fn ct_ne(&self, other: &Self) -> Choice

Determine if two items are NOT equal. Read more
§

impl Debug for Output

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl Display for Output

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl FromStr for Output

§

type Err = Error

The associated error which can be returned from parsing.
§

fn from_str(s: &str) -> Result<Output, Error>

Parses a string s to return a value of this type. Read more
§

impl PartialEq for Output

§

fn eq(&self, other: &Output) -> bool

This method tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
§

impl TryFrom<&[u8]> for Output

§

type Error = Error

The type returned in the event of a conversion error.
§

fn try_from(input: &[u8]) -> Result<Output, Error>

Performs the conversion.
§

impl Copy for Output

§

impl Eq for Output

§

impl StructuralEq for Output

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<T> AsOut<T> for T
where T: Copy,

§

fn as_out(&mut self) -> Out<'_, T>

Returns an out reference to self.
§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

§

fn implicit( self, class: Class, constructed: bool, tag: u32 ) -> TaggedParser<'a, Implicit, Self, E>

source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T> ToHex for T
where T: AsRef<[u8]>,

source§

fn encode_hex<U>(&self) -> U
where U: FromIterator<char>,

Encode the hex strict representing self into the result. Lower case letters are used (e.g. f9b4ca)
source§

fn encode_hex_upper<U>(&self) -> U
where U: FromIterator<char>,

Encode the hex strict representing self into the result. Upper case letters are used (e.g. F9B4CA)
source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more