mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
74 lines
2.3 KiB
Swift
74 lines
2.3 KiB
Swift
2 years ago
|
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||
|
//
|
||
|
// Note: This was taken from TensorFlow's Random:
|
||
|
// https://github.com/apple/swift/blob/bc8f9e61d333b8f7a625f74d48ef0b554726e349/stdlib/public/TensorFlow/Random.swift
|
||
|
//
|
||
|
// the complex approach is needed due to an issue with Swift's randomElement(using:)
|
||
|
// generation (see https://stackoverflow.com/a/64897775 for more info)
|
||
|
|
||
|
import Foundation
|
||
|
|
||
|
public struct ARC4RandomNumberGenerator: RandomNumberGenerator {
|
||
|
var state: [UInt8] = Array(0...255)
|
||
|
var iPos: UInt8 = 0
|
||
|
var jPos: UInt8 = 0
|
||
|
|
||
|
public init<T: BinaryInteger>(seed: T) {
|
||
|
self.init(
|
||
|
seed: (0..<(UInt64.bitWidth / UInt64.bitWidth)).map { index in
|
||
|
UInt8(truncatingIfNeeded: seed >> (UInt8.bitWidth * index))
|
||
|
}
|
||
|
)
|
||
|
}
|
||
|
|
||
|
public init(seed: [UInt8]) {
|
||
|
precondition(seed.count > 0, "Length of seed must be positive")
|
||
|
precondition(seed.count <= 256, "Length of seed must be at most 256")
|
||
|
|
||
|
// Note: Have to use a for loop instead of a 'forEach' otherwise
|
||
|
// it doesn't work properly (not sure why...)
|
||
|
var j: UInt8 = 0
|
||
|
for i: UInt8 in 0...255 {
|
||
|
j &+= S(i) &+ seed[Int(i) % seed.count]
|
||
|
swapAt(i, j)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Produce the next random UInt64 from the stream, and advance the internal state
|
||
|
public mutating func next() -> UInt64 {
|
||
|
// Note: Have to use a for loop instead of a 'forEach' otherwise
|
||
|
// it doesn't work properly (not sure why...)
|
||
|
var result: UInt64 = 0
|
||
|
for _ in 0..<UInt64.bitWidth / UInt8.bitWidth {
|
||
|
result <<= UInt8.bitWidth
|
||
|
result += UInt64(nextByte())
|
||
|
}
|
||
|
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
/// Helper to access the state
|
||
|
private func S(_ index: UInt8) -> UInt8 {
|
||
|
return state[Int(index)]
|
||
|
}
|
||
|
|
||
|
/// Helper to swap elements of the state
|
||
|
private mutating func swapAt(_ i: UInt8, _ j: UInt8) {
|
||
|
state.swapAt(Int(i), Int(j))
|
||
|
}
|
||
|
|
||
|
/// Generates the next byte in the keystream.
|
||
|
private mutating func nextByte() -> UInt8 {
|
||
|
iPos &+= 1
|
||
|
jPos &+= S(iPos)
|
||
|
swapAt(iPos, jPos)
|
||
|
return S(S(iPos) &+ S(jPos))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public extension ARC4RandomNumberGenerator {
|
||
|
mutating func nextBytes(count: Int) -> [UInt8] {
|
||
|
(0..<count).map { _ in nextByte() }
|
||
|
}
|
||
|
}
|