Skip to content

Commit 9ca66ec

Browse files
committed
Add ChaCha random generator
1 parent 738d61f commit 9ca66ec

File tree

6 files changed

+233
-1
lines changed

6 files changed

+233
-1
lines changed

RandomKit.xcodeproj/project.pbxproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
520396751E9AFD8F0041FAA3 /* ChaCha.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520396741E9AFD8F0041FAA3 /* ChaCha.swift */; };
11+
520396761E9AFD8F0041FAA3 /* ChaCha.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520396741E9AFD8F0041FAA3 /* ChaCha.swift */; };
12+
520396771E9AFD8F0041FAA3 /* ChaCha.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520396741E9AFD8F0041FAA3 /* ChaCha.swift */; };
13+
520396781E9AFD8F0041FAA3 /* ChaCha.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520396741E9AFD8F0041FAA3 /* ChaCha.swift */; };
1014
522268EC1E4163EC00C8865A /* XorshiftStar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522268EB1E4163EC00C8865A /* XorshiftStar.swift */; };
1115
522268ED1E4163EC00C8865A /* XorshiftStar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522268EB1E4163EC00C8865A /* XorshiftStar.swift */; };
1216
522268EE1E4163EC00C8865A /* XorshiftStar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522268EB1E4163EC00C8865A /* XorshiftStar.swift */; };
@@ -284,6 +288,7 @@
284288
/* End PBXContainerItemProxy section */
285289

286290
/* Begin PBXFileReference section */
291+
520396741E9AFD8F0041FAA3 /* ChaCha.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChaCha.swift; sourceTree = "<group>"; };
287292
520DA1341DDF16FD00F265CE /* UnsafeRandom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnsafeRandom.swift; sourceTree = "<group>"; };
288293
520DA1391DDF5FA800F265CE /* RandomEnum.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomEnum.swift; sourceTree = "<group>"; };
289294
5217714E1DE6D5EF00DD4584 /* UniqueShuffleable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UniqueShuffleable.swift; sourceTree = "<group>"; };
@@ -618,6 +623,7 @@
618623
5290FC551E7C5AFA008CA2AA /* ReseedingRandomGenerator.swift */,
619624
52D47D811E316B9A0050588E /* RandomBytesGenerator.swift */,
620625
52D47D771E31654B0050588E /* DeviceRandom.swift */,
626+
520396741E9AFD8F0041FAA3 /* ChaCha.swift */,
621627
52D47D7C1E3165C50050588E /* MersenneTwister.swift */,
622628
52D47D8B1E3180B60050588E /* Xoroshiro.swift */,
623629
52CE98D01E4052ED00DA001B /* Xorshift.swift */,
@@ -1002,6 +1008,7 @@
10021008
52D47D8C1E3180B60050588E /* Xoroshiro.swift in Sources */,
10031009
524F1E4A1E31352B00BDE35F /* Bool+RandomKit.swift in Sources */,
10041010
52D47D491E315F7D0050588E /* Color+RandomKit.swift in Sources */,
1011+
520396751E9AFD8F0041FAA3 /* ChaCha.swift in Sources */,
10051012
52D47D7D1E3165C50050588E /* MersenneTwister.swift in Sources */,
10061013
52D47D731E3165360050588E /* RandomGenerator.swift in Sources */,
10071014
);
@@ -1062,6 +1069,7 @@
10621069
52D47D8D1E3180B60050588E /* Xoroshiro.swift in Sources */,
10631070
524F1E4B1E31352D00BDE35F /* Bool+RandomKit.swift in Sources */,
10641071
52D47D4A1E315F7E0050588E /* Color+RandomKit.swift in Sources */,
1072+
520396761E9AFD8F0041FAA3 /* ChaCha.swift in Sources */,
10651073
52D47D7E1E3165C50050588E /* MersenneTwister.swift in Sources */,
10661074
52D47D741E3165360050588E /* RandomGenerator.swift in Sources */,
10671075
);
@@ -1122,6 +1130,7 @@
11221130
52D47D8E1E3180B60050588E /* Xoroshiro.swift in Sources */,
11231131
524F1E4C1E31352D00BDE35F /* Bool+RandomKit.swift in Sources */,
11241132
52D47D4B1E315F7E0050588E /* Color+RandomKit.swift in Sources */,
1133+
520396771E9AFD8F0041FAA3 /* ChaCha.swift in Sources */,
11251134
52D47D7F1E3165C50050588E /* MersenneTwister.swift in Sources */,
11261135
52D47D751E3165360050588E /* RandomGenerator.swift in Sources */,
11271136
);
@@ -1206,6 +1215,7 @@
12061215
52D47D8F1E3180B60050588E /* Xoroshiro.swift in Sources */,
12071216
524F1E4D1E31352F00BDE35F /* Bool+RandomKit.swift in Sources */,
12081217
52D47D4C1E315F7F0050588E /* Color+RandomKit.swift in Sources */,
1218+
520396781E9AFD8F0041FAA3 /* ChaCha.swift in Sources */,
12091219
52D47D801E3165C50050588E /* MersenneTwister.swift in Sources */,
12101220
52D47D761E3165360050588E /* RandomGenerator.swift in Sources */,
12111221
);

Sources/RandomKit/Internal/StackArray.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ internal typealias _Array312<T> = (
4444
/// A stack-allocated array of 16 elements.
4545
internal typealias _Array16<T> = (T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T)
4646

47+
/// A stack-allocated array of 8 elements.
48+
internal typealias _Array8<T> = (T, T, T, T, T, T, T, T)
49+
4750
/// Returns a stack-allocated array of 312 zeros.
4851
internal func _zero312<T: ExpressibleByIntegerLiteral>() -> _Array312<T> {
4952
return (
@@ -67,6 +70,11 @@ internal func _zero16<T: ExpressibleByIntegerLiteral>() -> _Array16<T> {
6770
return (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
6871
}
6972

73+
/// Returns a stack-allocated array of 8 zeros.
74+
internal func _zero8<T: ExpressibleByIntegerLiteral>() -> _Array8<T> {
75+
return (0, 0, 0, 0, 0, 0, 0, 0)
76+
}
77+
7078
/// Returns a mutable pointer to the contents of `array`.
7179
internal func _contents<T>(of array: inout _Array312<T>) -> UnsafeMutablePointer<T> {
7280
return _pointer(to: &array, as: T.self)

Sources/RandomKit/Internal/Typecasting.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,6 @@ internal func _unsafeValue<T>(of type: T.Type = T.self) -> T {
4747
return _unsafeCast(Optional<T>.none)
4848
}
4949

50-
internal func _pointer<T, U>(to value: inout T, as type: U.Type) -> UnsafeMutablePointer<U> {
50+
internal func _pointer<T, U>(to value: inout T, as type: U.Type = U.self) -> UnsafeMutablePointer<U> {
5151
return UnsafeMutableRawPointer(&value).assumingMemoryBound(to: type)
5252
}
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
//
2+
// ChaCha.swift
3+
// RandomKit
4+
//
5+
// The MIT License (MIT)
6+
//
7+
// Copyright (c) 2015-2017 Nikolai Vazquez
8+
//
9+
// Permission is hereby granted, free of charge, to any person obtaining a copy
10+
// of this software and associated documentation files (the "Software"), to deal
11+
// in the Software without restriction, including without limitation the rights
12+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
// copies of the Software, and to permit persons to whom the Software is
14+
// furnished to do so, subject to the following conditions:
15+
//
16+
// The above copyright notice and this permission notice shall be included in
17+
// all copies or substantial portions of the Software.
18+
//
19+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
// THE SOFTWARE.
26+
//
27+
28+
/// A generator that uses a [ChaCha20][1] algorithm.
29+
///
30+
/// This implementation is taken from that of [ChaCha][2] in the Rust `rand` crate.
31+
///
32+
/// [1]: http://cr.yp.to/chacha.html
33+
/// [2]: https://doc.rust-lang.org/rand/rand/chacha/struct.ChaChaRng.html
34+
public struct ChaCha: RandomBytesGenerator, SeedableFromOtherRandomGenerator {
35+
36+
private typealias _State = _Array16<UInt32>
37+
38+
private static let _stateCount = 16
39+
40+
private static let _keyCount = 8
41+
42+
private static let _rounds = 20
43+
44+
private static var _empty: ChaCha {
45+
return ChaCha(_buffer: _zero16(), _state: _zero16(), _index: _stateCount)
46+
}
47+
48+
/// A default global instance seeded with `DeviceRandom.default`.
49+
public static var `default` = seeded
50+
51+
/// A default global instance that reseeds itself with `DeviceRandom.default`.
52+
public static var defaultReseeding = reseeding
53+
54+
/// Returns an unseeded instance.
55+
public static var unseeded: ChaCha {
56+
var result = _empty
57+
result._reset()
58+
return result
59+
}
60+
61+
private var _buffer: _State
62+
63+
private var _state: _State
64+
65+
private var _index: Int
66+
67+
private var _bufferPointer: UnsafeMutablePointer<UInt32> {
68+
mutating get { return _pointer(to: &_buffer) }
69+
}
70+
71+
private var _statePointer: UnsafeMutablePointer<UInt32> {
72+
mutating get { return _pointer(to: &_state) }
73+
}
74+
75+
private init(_buffer: _State, _state: _State, _index: Int) {
76+
self._buffer = _buffer
77+
self._state = _state
78+
self._index = _index
79+
}
80+
81+
/// Creates an instance from `seed`.
82+
public init(seed: UnsafeBufferPointer<UInt32>) {
83+
self = ._empty
84+
reseed(with: seed)
85+
}
86+
87+
/// Creates an instance from `seed`.
88+
public init<S: Sequence>(seed: S) where S.Iterator.Element == UInt32 {
89+
self = ._empty
90+
reseed(with: seed)
91+
}
92+
93+
/// Creates an instance seeded with `randomGenerator`.
94+
public init<R: RandomGenerator>(seededWith randomGenerator: inout R) {
95+
var seed: _Array8<UInt32> = randomGenerator.randomUnsafeValue()
96+
let pointer = _pointer(to: &seed, as: UInt32.self)
97+
let buffer = UnsafeBufferPointer(start: pointer, count: ChaCha._keyCount)
98+
self.init(seed: buffer)
99+
}
100+
101+
private mutating func _reset() {
102+
_initialize(from: _zero8())
103+
}
104+
105+
private mutating func _initialize(from key: _Array8<UInt32>) {
106+
_state.0 = 0x61707865
107+
_state.1 = 0x3320646E
108+
_state.2 = 0x79622D32
109+
_state.3 = 0x6B206574
110+
_state.4 = key.0
111+
_state.5 = key.1
112+
_state.6 = key.2
113+
_state.7 = key.3
114+
_state.8 = key.4
115+
_state.9 = key.5
116+
_state.10 = key.6
117+
_state.11 = key.7
118+
_state.12 = 0
119+
_state.13 = 0
120+
_state.14 = 0
121+
_state.15 = 0
122+
}
123+
124+
private mutating func _update() {
125+
let bufferPtr = _bufferPointer
126+
let statePtr = _statePointer
127+
_buffer = _state
128+
_index = 0
129+
for _ in 0 ..< ChaCha._rounds / 2 {
130+
_doubleRound(bufferPtr)
131+
}
132+
for i in 0 ..< ChaCha._stateCount {
133+
bufferPtr[i] = bufferPtr[i] &+ statePtr[i]
134+
}
135+
for i in 12 ..< 16 {
136+
statePtr[i] = statePtr[i] &+ 1
137+
guard statePtr[i] == 0 else {
138+
return
139+
}
140+
}
141+
}
142+
143+
private mutating func _reseed<S: Sequence>(with seed: S) where S.Iterator.Element == UInt32 {
144+
reseed(with: seed)
145+
}
146+
147+
/// Sets the internal 128-bit counter.
148+
public mutating func setCounter(low: UInt64, high: UInt64) {
149+
_state.12 = UInt32(truncatingBitPattern: low)
150+
_state.13 = UInt32(truncatingBitPattern: low >> 32)
151+
_state.14 = UInt32(truncatingBitPattern: high)
152+
_state.15 = UInt32(truncatingBitPattern: high >> 32)
153+
_index = ChaCha._stateCount
154+
}
155+
156+
/// Reseeds `self` with `seed`.
157+
public mutating func reseed(with seed: UnsafeBufferPointer<UInt32>) {
158+
// Required to specify method with same name.
159+
_reseed(with: seed)
160+
}
161+
162+
/// Reseeds `self` with `seed`.
163+
public mutating func reseed<S: Sequence>(with seed: S) where S.Iterator.Element == UInt32 {
164+
_reset()
165+
let keyPointer = _statePointer
166+
for (i, s) in zip(4 ..< ChaCha._keyCount + 4, seed) {
167+
keyPointer[i] = s
168+
}
169+
}
170+
171+
/// Returns random `Bytes`.
172+
public mutating func randomBytes() -> UInt32 {
173+
if _index == ChaCha._stateCount {
174+
_update()
175+
}
176+
defer { _index += 1 }
177+
return _bufferPointer[_index % ChaCha._stateCount]
178+
}
179+
180+
}
181+
182+
extension UInt32 {
183+
@inline(__always)
184+
fileprivate func _rotateLeft(_ n: UInt32) -> UInt32 {
185+
return (self << n) | (self >> (32 &- n))
186+
}
187+
}
188+
189+
@inline(__always)
190+
private func _quarterRound(_ a: inout UInt32, _ b: inout UInt32, _ c: inout UInt32, _ d: inout UInt32) {
191+
a = a &+ b; d ^= a; d = d._rotateLeft(16)
192+
c = c &+ d; b ^= c; b = b._rotateLeft(12)
193+
a = a &+ b; d ^= a; d = d._rotateLeft(8)
194+
c = c &+ d; b ^= c; b = b._rotateLeft(7)
195+
}
196+
197+
@inline(__always)
198+
private func _doubleRound(_ x: UnsafeMutablePointer<UInt32>) {
199+
// Column round
200+
_quarterRound(&x[0], &x[4], &x[8], &x[12])
201+
_quarterRound(&x[1], &x[5], &x[9], &x[13])
202+
_quarterRound(&x[2], &x[6], &x[10], &x[14])
203+
_quarterRound(&x[3], &x[7], &x[11], &x[15])
204+
// Diagonal round
205+
_quarterRound(&x[0], &x[5], &x[10], &x[15])
206+
_quarterRound(&x[1], &x[6], &x[11], &x[12])
207+
_quarterRound(&x[2], &x[7], &x[8], &x[13])
208+
_quarterRound(&x[3], &x[4], &x[9], &x[14])
209+
}

Sources/benchmark/Help.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ func printHelpAndExit() -> Never {
7575
message += " " + sf("--xoroshiro") + " Use xoroshiro generator\n"
7676
message += " " + sf("--xorshift") + " Use xorshift generator\n"
7777
message += " " + sf("--xorshift-star") + " Use xorshift1024* generator\n"
78+
message += " " + sf("--chacha") + " Use ChaCha20 generator\n"
7879
message += " " + sf("--mersenne-twister") + " Use mersenne twister generator\n"
7980
message += " " + sf("--arc4random") + " Use arc4random generator\n"
8081
message += " " + sf("--dev") + " Use both /dev/random and /dev/urandom generators\n"

Sources/benchmark/main.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ if bench("--xorshift-star") {
110110
runBenchmarks(using: &XorshiftStar.default)
111111
}
112112

113+
if bench("--chacha") {
114+
runBenchmarks(using: &ChaCha.default)
115+
}
116+
113117
if bench("--mersenne-twister") {
114118
runBenchmarks(using: &MersenneTwister.default)
115119
}

0 commit comments

Comments
 (0)