std/crypto
import std/crypto to use it. Source: src/std/crypto.iyi at v0.18.0.
The hash is named by its type, SHA256 and the rest from std/digest:
import std/digest::{SHA256}
import std/crypto::{HMAC, HKDF, PBKDF2, Subtle} HMAC.hexdigest(SHA256, key, message)
HKDF.derive(SHA256, secret, salt, "context", 32)
PBKDF2.derive(SHA256, password, salt, 600_000, 32)
Subtle.constant_time_compare(sent, expected)A key, a message, a salt or an info is a String, whose bytes are taken, or an Array(UInt8); what is derived is an Array(UInt8). HMAC.new(SHA256, key) is the streaming form: update as the message arrives, final once, reset to start the next message under the same key. The keyed state is computed once and copied back on reset, so a loop that MACs many messages under one key, PBKDF2's, pays for the key once and allocates nothing per message.
Refused with a sentence: a second final without a reset, an HKDF output longer than 255 hashes, a PBKDF2 count or length below one.
What this is not: a cipher, a signature, or TLS. A digest compared with == stops at the first byte that differs, and how long it took says where; compare a MAC an attacker sent with Subtle.constant_time_compare.
HKDFstruct
HKDF, RFC 5869: extract concentrates input keying material into a pseudorandom key, expand stretches that key into as many bytes as are asked for, bound to an info that names their use; derive is both. An empty salt is the RFC's default, a hash's length of zeros, which is the same key to HMAC.
pub struct HKDF
def self.derive(algorithm : H.class, ikm : String | Array(UInt8), salt : String | Array(UInt8), info : String | Array(UInt8), length : Int32) : Array(UInt8) forall H
# At most 255 hashes' worth of bytes, which is what a one-byte block
# counter can number.
def self.expand(algorithm : H.class, prk : String | Array(UInt8), info : String | Array(UInt8), length : Int32) : Array(UInt8) forall H
def self.extract(algorithm : H.class, salt : String | Array(UInt8), ikm : String | Array(UInt8)) : Array(UInt8) forall H
def initialize
endHMACclass
pub class HMAC
def self.digest(algorithm : H.class, key : String | Array(UInt8), data : String | Array(UInt8)) : Array(UInt8) forall H
# Bytes in a MAC: the hash's digest size.
def digest_size : Int32
# The MAC, into the first `digest_size` bytes of *dst*, with nothing
# allocated.
def final(dst : Array(UInt8)) : Nil
def final : Array(UInt8)
def self.hexdigest(algorithm : H.class, key : String | Array(UInt8), data : String | Array(UInt8)) : String forall H
def hexfinal : String
def initialize(algorithm : H.class, key : String | Array(UInt8)) forall H
# Ready for the next message under the same key.
def reset : Nil
def update(data : Pointer(UInt8), count : Int32) : self
def update(data : String | Array(UInt8)) : self
endPBKDF2struct
PBKDF2, RFC 8018 §5.2, with HMAC as its pseudorandom function: a key from a password, made deliberately slow by iterations. OWASP's figure for HMAC-SHA256 is 600,000.
pub struct PBKDF2
def self.derive(algorithm : H.class, password : String | Array(UInt8), salt : String | Array(UInt8), iterations : Int32, length : Int32) : Array(UInt8) forall H
def initialize
endSubtlestruct
pub struct Subtle
# Whether *a* and *b* hold the same bytes, in a time that depends on
# their sizes and not on their contents: every byte is read, and the
# differences are gathered with `|` rather than tested one at a time.
# Byte strings of different sizes answer false at once, so the size is
# not a secret this keeps.
def self.constant_time_compare(a : String | Array(UInt8), b : String | Array(UInt8)) : Bool
def initialize
end
Keyed hashing and key derivation over std/digest, written in iyi: HMAC (RFC 2104), HKDF (RFC 5869), PBKDF2 (RFC 8018 §5.2), and a comparison whose time does not depend on where two byte strings differ.