Skip to content

Commit

Permalink
Add ChaCha random generator
Browse files Browse the repository at this point in the history
  • Loading branch information
nvzqz committed Apr 10, 2017
1 parent 738d61f commit 9ca66ec
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 1 deletion.
10 changes: 10 additions & 0 deletions RandomKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
objects = {

/* Begin PBXBuildFile section */
520396751E9AFD8F0041FAA3 /* ChaCha.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520396741E9AFD8F0041FAA3 /* ChaCha.swift */; };
520396761E9AFD8F0041FAA3 /* ChaCha.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520396741E9AFD8F0041FAA3 /* ChaCha.swift */; };
520396771E9AFD8F0041FAA3 /* ChaCha.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520396741E9AFD8F0041FAA3 /* ChaCha.swift */; };
520396781E9AFD8F0041FAA3 /* ChaCha.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520396741E9AFD8F0041FAA3 /* ChaCha.swift */; };
522268EC1E4163EC00C8865A /* XorshiftStar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522268EB1E4163EC00C8865A /* XorshiftStar.swift */; };
522268ED1E4163EC00C8865A /* XorshiftStar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522268EB1E4163EC00C8865A /* XorshiftStar.swift */; };
522268EE1E4163EC00C8865A /* XorshiftStar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522268EB1E4163EC00C8865A /* XorshiftStar.swift */; };
Expand Down Expand Up @@ -284,6 +288,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
520396741E9AFD8F0041FAA3 /* ChaCha.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChaCha.swift; sourceTree = "<group>"; };
520DA1341DDF16FD00F265CE /* UnsafeRandom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnsafeRandom.swift; sourceTree = "<group>"; };
520DA1391DDF5FA800F265CE /* RandomEnum.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomEnum.swift; sourceTree = "<group>"; };
5217714E1DE6D5EF00DD4584 /* UniqueShuffleable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UniqueShuffleable.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -618,6 +623,7 @@
5290FC551E7C5AFA008CA2AA /* ReseedingRandomGenerator.swift */,
52D47D811E316B9A0050588E /* RandomBytesGenerator.swift */,
52D47D771E31654B0050588E /* DeviceRandom.swift */,
520396741E9AFD8F0041FAA3 /* ChaCha.swift */,
52D47D7C1E3165C50050588E /* MersenneTwister.swift */,
52D47D8B1E3180B60050588E /* Xoroshiro.swift */,
52CE98D01E4052ED00DA001B /* Xorshift.swift */,
Expand Down Expand Up @@ -1002,6 +1008,7 @@
52D47D8C1E3180B60050588E /* Xoroshiro.swift in Sources */,
524F1E4A1E31352B00BDE35F /* Bool+RandomKit.swift in Sources */,
52D47D491E315F7D0050588E /* Color+RandomKit.swift in Sources */,
520396751E9AFD8F0041FAA3 /* ChaCha.swift in Sources */,
52D47D7D1E3165C50050588E /* MersenneTwister.swift in Sources */,
52D47D731E3165360050588E /* RandomGenerator.swift in Sources */,
);
Expand Down Expand Up @@ -1062,6 +1069,7 @@
52D47D8D1E3180B60050588E /* Xoroshiro.swift in Sources */,
524F1E4B1E31352D00BDE35F /* Bool+RandomKit.swift in Sources */,
52D47D4A1E315F7E0050588E /* Color+RandomKit.swift in Sources */,
520396761E9AFD8F0041FAA3 /* ChaCha.swift in Sources */,
52D47D7E1E3165C50050588E /* MersenneTwister.swift in Sources */,
52D47D741E3165360050588E /* RandomGenerator.swift in Sources */,
);
Expand Down Expand Up @@ -1122,6 +1130,7 @@
52D47D8E1E3180B60050588E /* Xoroshiro.swift in Sources */,
524F1E4C1E31352D00BDE35F /* Bool+RandomKit.swift in Sources */,
52D47D4B1E315F7E0050588E /* Color+RandomKit.swift in Sources */,
520396771E9AFD8F0041FAA3 /* ChaCha.swift in Sources */,
52D47D7F1E3165C50050588E /* MersenneTwister.swift in Sources */,
52D47D751E3165360050588E /* RandomGenerator.swift in Sources */,
);
Expand Down Expand Up @@ -1206,6 +1215,7 @@
52D47D8F1E3180B60050588E /* Xoroshiro.swift in Sources */,
524F1E4D1E31352F00BDE35F /* Bool+RandomKit.swift in Sources */,
52D47D4C1E315F7F0050588E /* Color+RandomKit.swift in Sources */,
520396781E9AFD8F0041FAA3 /* ChaCha.swift in Sources */,
52D47D801E3165C50050588E /* MersenneTwister.swift in Sources */,
52D47D761E3165360050588E /* RandomGenerator.swift in Sources */,
);
Expand Down
8 changes: 8 additions & 0 deletions Sources/RandomKit/Internal/StackArray.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ internal typealias _Array312<T> = (
/// A stack-allocated array of 16 elements.
internal typealias _Array16<T> = (T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T)

/// A stack-allocated array of 8 elements.
internal typealias _Array8<T> = (T, T, T, T, T, T, T, T)

/// Returns a stack-allocated array of 312 zeros.
internal func _zero312<T: ExpressibleByIntegerLiteral>() -> _Array312<T> {
return (
Expand All @@ -67,6 +70,11 @@ internal func _zero16<T: ExpressibleByIntegerLiteral>() -> _Array16<T> {
return (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
}

/// Returns a stack-allocated array of 8 zeros.
internal func _zero8<T: ExpressibleByIntegerLiteral>() -> _Array8<T> {
return (0, 0, 0, 0, 0, 0, 0, 0)
}

/// Returns a mutable pointer to the contents of `array`.
internal func _contents<T>(of array: inout _Array312<T>) -> UnsafeMutablePointer<T> {
return _pointer(to: &array, as: T.self)
Expand Down
2 changes: 1 addition & 1 deletion Sources/RandomKit/Internal/Typecasting.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ internal func _unsafeValue<T>(of type: T.Type = T.self) -> T {
return _unsafeCast(Optional<T>.none)
}

internal func _pointer<T, U>(to value: inout T, as type: U.Type) -> UnsafeMutablePointer<U> {
internal func _pointer<T, U>(to value: inout T, as type: U.Type = U.self) -> UnsafeMutablePointer<U> {
return UnsafeMutableRawPointer(&value).assumingMemoryBound(to: type)
}
209 changes: 209 additions & 0 deletions Sources/RandomKit/Types/RandomGenerator/ChaCha.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
//
// ChaCha.swift
// RandomKit
//
// The MIT License (MIT)
//
// Copyright (c) 2015-2017 Nikolai Vazquez
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

/// A generator that uses a [ChaCha20][1] algorithm.
///
/// This implementation is taken from that of [ChaCha][2] in the Rust `rand` crate.
///
/// [1]: http://cr.yp.to/chacha.html
/// [2]: https://doc.rust-lang.org/rand/rand/chacha/struct.ChaChaRng.html
public struct ChaCha: RandomBytesGenerator, SeedableFromOtherRandomGenerator {

private typealias _State = _Array16<UInt32>

private static let _stateCount = 16

private static let _keyCount = 8

private static let _rounds = 20

private static var _empty: ChaCha {
return ChaCha(_buffer: _zero16(), _state: _zero16(), _index: _stateCount)
}

/// A default global instance seeded with `DeviceRandom.default`.
public static var `default` = seeded

/// A default global instance that reseeds itself with `DeviceRandom.default`.
public static var defaultReseeding = reseeding

/// Returns an unseeded instance.
public static var unseeded: ChaCha {
var result = _empty
result._reset()
return result
}

private var _buffer: _State

private var _state: _State

private var _index: Int

private var _bufferPointer: UnsafeMutablePointer<UInt32> {
mutating get { return _pointer(to: &_buffer) }
}

private var _statePointer: UnsafeMutablePointer<UInt32> {
mutating get { return _pointer(to: &_state) }
}

private init(_buffer: _State, _state: _State, _index: Int) {
self._buffer = _buffer
self._state = _state
self._index = _index
}

/// Creates an instance from `seed`.
public init(seed: UnsafeBufferPointer<UInt32>) {
self = ._empty
reseed(with: seed)
}

/// Creates an instance from `seed`.
public init<S: Sequence>(seed: S) where S.Iterator.Element == UInt32 {
self = ._empty
reseed(with: seed)
}

/// Creates an instance seeded with `randomGenerator`.
public init<R: RandomGenerator>(seededWith randomGenerator: inout R) {
var seed: _Array8<UInt32> = randomGenerator.randomUnsafeValue()
let pointer = _pointer(to: &seed, as: UInt32.self)
let buffer = UnsafeBufferPointer(start: pointer, count: ChaCha._keyCount)
self.init(seed: buffer)
}

private mutating func _reset() {
_initialize(from: _zero8())
}

private mutating func _initialize(from key: _Array8<UInt32>) {
_state.0 = 0x61707865
_state.1 = 0x3320646E
_state.2 = 0x79622D32
_state.3 = 0x6B206574
_state.4 = key.0
_state.5 = key.1
_state.6 = key.2
_state.7 = key.3
_state.8 = key.4
_state.9 = key.5
_state.10 = key.6
_state.11 = key.7
_state.12 = 0
_state.13 = 0
_state.14 = 0
_state.15 = 0
}

private mutating func _update() {
let bufferPtr = _bufferPointer
let statePtr = _statePointer
_buffer = _state
_index = 0
for _ in 0 ..< ChaCha._rounds / 2 {
_doubleRound(bufferPtr)
}
for i in 0 ..< ChaCha._stateCount {
bufferPtr[i] = bufferPtr[i] &+ statePtr[i]
}
for i in 12 ..< 16 {
statePtr[i] = statePtr[i] &+ 1
guard statePtr[i] == 0 else {
return
}
}
}

private mutating func _reseed<S: Sequence>(with seed: S) where S.Iterator.Element == UInt32 {
reseed(with: seed)
}

/// Sets the internal 128-bit counter.
public mutating func setCounter(low: UInt64, high: UInt64) {
_state.12 = UInt32(truncatingBitPattern: low)
_state.13 = UInt32(truncatingBitPattern: low >> 32)
_state.14 = UInt32(truncatingBitPattern: high)
_state.15 = UInt32(truncatingBitPattern: high >> 32)
_index = ChaCha._stateCount
}

/// Reseeds `self` with `seed`.
public mutating func reseed(with seed: UnsafeBufferPointer<UInt32>) {
// Required to specify method with same name.
_reseed(with: seed)
}

/// Reseeds `self` with `seed`.
public mutating func reseed<S: Sequence>(with seed: S) where S.Iterator.Element == UInt32 {
_reset()
let keyPointer = _statePointer
for (i, s) in zip(4 ..< ChaCha._keyCount + 4, seed) {
keyPointer[i] = s
}
}

/// Returns random `Bytes`.
public mutating func randomBytes() -> UInt32 {
if _index == ChaCha._stateCount {
_update()
}
defer { _index += 1 }
return _bufferPointer[_index % ChaCha._stateCount]
}

}

extension UInt32 {
@inline(__always)
fileprivate func _rotateLeft(_ n: UInt32) -> UInt32 {
return (self << n) | (self >> (32 &- n))
}
}

@inline(__always)
private func _quarterRound(_ a: inout UInt32, _ b: inout UInt32, _ c: inout UInt32, _ d: inout UInt32) {
a = a &+ b; d ^= a; d = d._rotateLeft(16)
c = c &+ d; b ^= c; b = b._rotateLeft(12)
a = a &+ b; d ^= a; d = d._rotateLeft(8)
c = c &+ d; b ^= c; b = b._rotateLeft(7)
}

@inline(__always)
private func _doubleRound(_ x: UnsafeMutablePointer<UInt32>) {
// Column round
_quarterRound(&x[0], &x[4], &x[8], &x[12])
_quarterRound(&x[1], &x[5], &x[9], &x[13])
_quarterRound(&x[2], &x[6], &x[10], &x[14])
_quarterRound(&x[3], &x[7], &x[11], &x[15])
// Diagonal round
_quarterRound(&x[0], &x[5], &x[10], &x[15])
_quarterRound(&x[1], &x[6], &x[11], &x[12])
_quarterRound(&x[2], &x[7], &x[8], &x[13])
_quarterRound(&x[3], &x[4], &x[9], &x[14])
}
1 change: 1 addition & 0 deletions Sources/benchmark/Help.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ func printHelpAndExit() -> Never {
message += " " + sf("--xoroshiro") + " Use xoroshiro generator\n"
message += " " + sf("--xorshift") + " Use xorshift generator\n"
message += " " + sf("--xorshift-star") + " Use xorshift1024* generator\n"
message += " " + sf("--chacha") + " Use ChaCha20 generator\n"
message += " " + sf("--mersenne-twister") + " Use mersenne twister generator\n"
message += " " + sf("--arc4random") + " Use arc4random generator\n"
message += " " + sf("--dev") + " Use both /dev/random and /dev/urandom generators\n"
Expand Down
4 changes: 4 additions & 0 deletions Sources/benchmark/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ if bench("--xorshift-star") {
runBenchmarks(using: &XorshiftStar.default)
}

if bench("--chacha") {
runBenchmarks(using: &ChaCha.default)
}

if bench("--mersenne-twister") {
runBenchmarks(using: &MersenneTwister.default)
}
Expand Down

0 comments on commit 9ca66ec

Please sign in to comment.