-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d213ae0
commit 9ea135f
Showing
7 changed files
with
302 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 64 additions & 0 deletions
64
Sources/MessageAuthentication/MessageAuthenticationHash.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/// A hash that can be used to generate a message authentication code (MAC). | ||
public | ||
protocol MessageAuthenticationHash:RandomAccessCollection where Index == Int, Element == UInt8 | ||
{ | ||
/// The natural block stride of this hash function. | ||
static | ||
var stride:Int { get } | ||
|
||
/// The output size, in bytes, of this hash function. | ||
static | ||
var count:Int { get } | ||
|
||
/// Computes an instance of this hash for the given message. | ||
init<Message>(hashing message:Message) | ||
where Message:Collection, Message.Element == UInt8 | ||
} | ||
extension MessageAuthenticationHash | ||
{ | ||
@inlinable public | ||
var endIndex:Int | ||
{ | ||
self.startIndex + Self.count | ||
} | ||
@inlinable public | ||
var count:Int | ||
{ | ||
Self.count | ||
} | ||
} | ||
extension MessageAuthenticationHash | ||
{ | ||
/// Computes a hash-based message authentication code (HMAC) for | ||
/// the given message using the given key. | ||
/// | ||
/// This initializer computes an instance of ``MessageAuthenticationKey`` | ||
/// and uses it to generate an instance of ``Self``. If you are reusing | ||
/// the same `key` multiple times, it is more efficient to compute the | ||
/// message authentication key externally and call its | ||
/// ``MessageAuthenticationKey.authenticate(_:)`` method instead. | ||
@inlinable public | ||
init<Message, Key>(authenticating message:Message, key:Key) | ||
where Message:Sequence, Message.Element == UInt8, | ||
Key:Collection, Key.Element == UInt8 | ||
{ | ||
let key:MessageAuthenticationKey<Self> = .init(key) | ||
self = key.authenticate(message) | ||
} | ||
} | ||
|
||
extension MessageAuthenticationHash | ||
{ | ||
/// Derives an encryption key from a password and salt. | ||
/// This is a password-based key derivation function | ||
/// ([PBKDR2](https://en.wikipedia.org/wiki/PBKDF2)). | ||
@inlinable public static | ||
func pbkdf2<Password, Salt>(password:Password, salt:Salt, iterations:Int, | ||
blocks:Int = 1) -> [UInt8] | ||
where Password:Collection, Password.Element == UInt8, | ||
Salt:Collection, Salt.Element == UInt8 | ||
{ | ||
let key:MessageAuthenticationKey<Self> = .init(password) | ||
return key.derive(salt: salt, iterations: iterations, blocks: blocks) | ||
} | ||
} |
80 changes: 80 additions & 0 deletions
80
Sources/MessageAuthentication/MessageAuthenticationKey.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/// A precomputed message authentication key, which can be used to compute | ||
/// hash-based message authentication codes ([HMACs](https://en.wikipedia.org/wiki/HMAC)). | ||
/// | ||
/// Using this type to generate authentication codes for many messages with | ||
/// the same base key is faster than repeatedly calling | ||
/// ``MessageAuthenticationHash.init(authenticating:key:)``. | ||
@frozen public | ||
struct MessageAuthenticationKey<Hash> where Hash:MessageAuthenticationHash | ||
{ | ||
public | ||
let inner:[UInt8] | ||
public | ||
let outer:[UInt8] | ||
|
||
/// Creates a message authentication key from the given base key. | ||
@inlinable public | ||
init<Key>(_ key:Key) where Key:Collection, Key.Element == UInt8 | ||
{ | ||
let normalized:[UInt8] | ||
let count:Int = key.count | ||
if count > Hash.stride | ||
{ | ||
let key:Hash = .init(hashing: key) | ||
normalized = [UInt8].init(key) + repeatElement(0, count: Hash.stride - Hash.count) | ||
} | ||
else if count < Hash.stride | ||
{ | ||
normalized = [UInt8].init(key) + repeatElement(0, count: Hash.stride - count) | ||
} | ||
else | ||
{ | ||
normalized = [UInt8].init(key) | ||
} | ||
|
||
self.inner = normalized.map { $0 ^ 0x36 } | ||
self.outer = normalized.map { $0 ^ 0x5c } | ||
} | ||
} | ||
extension MessageAuthenticationKey | ||
{ | ||
/// Computes a hash-based message authentication code | ||
/// ([HMAC](https://en.wikipedia.org/wiki/HMAC)) for the given message | ||
/// using this key. | ||
@inlinable public | ||
func authenticate<Message>(_ message:Message) -> Hash | ||
where Message:Sequence, Message.Element == UInt8 | ||
{ | ||
.init(hashing: outer + Hash.init(hashing: inner + message)) | ||
} | ||
/// Derives an encryption key using this message authentication key and | ||
/// the given salt. If this message authentication key was computed from | ||
/// a password, then this functions as a password-based key derivation | ||
/// function ([PBKDR2](https://en.wikipedia.org/wiki/PBKDF2)). | ||
@inlinable public | ||
func derive<Salt>(salt:Salt, iterations:Int, blocks:Int = 1) -> [UInt8] | ||
where Salt:Collection, Salt.Element == UInt8 | ||
{ | ||
var output:[UInt8] = [] | ||
output.reserveCapacity(blocks * Hash.count) | ||
for block:UInt32 in 1 ... UInt32.init(blocks) | ||
{ | ||
let salt:[UInt8] = withUnsafeBytes(of: block.bigEndian) { [UInt8].init(salt) + $0 } | ||
|
||
var hash:Hash = self.authenticate(salt) | ||
var block:[UInt8] = .init(hash) | ||
|
||
for _ in 1 ..< iterations | ||
{ | ||
hash = self.authenticate(hash) | ||
for (index, byte):(Int, UInt8) in zip(block.indices, hash) | ||
{ | ||
block[index] ^= byte | ||
} | ||
} | ||
|
||
output.append(contentsOf: block) | ||
} | ||
return output | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.