在Swift中使用SecRandomCopyBytes

27

我希望在Swift 3.0中使用SecRandomCopyBytes生成随机字节。以下是我在Swift 2.2中的做法。

private static func generateRandomBytes() -> String? {
    let data = NSMutableData(length: Int(32))

    let result = SecRandomCopyBytes(kSecRandomDefault, 32, UnsafeMutablePointer<UInt8>(data!.mutableBytes))
    if result == errSecSuccess {
        return data!.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
    } else {
        print("Problem generating random bytes")
        return nil
    }
}
在Swift 3中,我尝试像这样做,因为我知道unsafemutablebytes的概念现在已经不同了,但是它不允许我返回。如果我注释掉返回部分,它仍然会显示“无法推断出泛型参数ResultType”。
fileprivate static func generateRandomBytes() -> String? {
    var keyData = Data(count: 32)
    _ = keyData.withUnsafeMutableBytes {mutableBytes in
        let result = SecRandomCopyBytes(kSecRandomDefault, keyData.count, mutableBytes)
        if result == errSecSuccess {
            return keyData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
        } else {
            print("Problem generating random bytes")
            return nil
        }
    }
    return nil
}

有人知道如何修复这个问题吗?

谢谢

3个回答

54
你离正确很近了,但是闭包中的return语句只能从闭包中返回而不是外部函数。 因此,只应在闭包中调用SecRandomCopyBytes()函数,并将结果传回。
func generateRandomBytes() -> String? {

    var keyData = Data(count: 32)
    let result = keyData.withUnsafeMutableBytes {
        (mutableBytes: UnsafeMutablePointer<UInt8>) -> Int32 in
        SecRandomCopyBytes(kSecRandomDefault, 32, mutableBytes)
    }
    if result == errSecSuccess {
        return keyData.base64EncodedString()
    } else {
        print("Problem generating random bytes")
        return nil
    }
}

"单表达式闭包"的闭包类型可以自动推断,因此可以缩短为

func generateRandomBytes() -> String? {

    var keyData = Data(count: 32)
    let result = keyData.withUnsafeMutableBytes {
        SecRandomCopyBytes(kSecRandomDefault, 32, $0)
    }
    if result == errSecSuccess {
        return keyData.base64EncodedString()
    } else {
        print("Problem generating random bytes")
        return nil
    }
}

Swift 5更新:

func generateRandomBytes() -> String? {

    var keyData = Data(count: 32)
    let result = keyData.withUnsafeMutableBytes {
        SecRandomCopyBytes(kSecRandomDefault, 32, $0.baseAddress!)
    }
    if result == errSecSuccess {
        return keyData.base64EncodedString()
    } else {
        print("Problem generating random bytes")
        return nil
    }
}

1
@CodeOverRide:感谢您解决了“重叠访问”问题。我进行了一些简化,不需要复制数据。只需在闭包内部不使用 keyData.count 即可。 - Martin R
@AnkitJayaswal:没有开箱即用的功能,但实现起来并不太难(类似于 https://dev59.com/YV8d5IYBdhLWcg3wsD1W#26550169)。但请注意,从Swift 4.2开始,Swift标准库提供了一个随机数生成器,根据https://developer.apple.com/documentation/swift/systemrandomnumbergenerator的说法,它是具有密码学安全性的。 - Martin R
@MartinR - 快速问题,在Swift 5版本中,您使用$0.baseAddress来指定指向keyData变量内存位置开头的指针(我想是这样的)?---这个语法如何工作,我认为$0是闭包的第一个“参数”的快捷方式,但在这里对我来说没有意义,因为闭包没有参数。 - Woodstock
@MartinR - 非常感谢您。好的,这很有道理,但是keyData又是如何填充随机字节的呢?它应该只有闭包级别的作用域,而不是bufPtr,或者说bufPtr逃逸了闭包吗?我的意思是我们返回的是keyData而不是bufPtr$0 - Woodstock
@Woodstock:Closure 被调用时,bufPtr 被设置为 keyData 的元素存储地址(和长度)。因此,SecRandomCopyBytes 被调用时,使用指向该元素存储的指针,并填充 keyData 的字节。 - Martin R
显示剩余6条评论

13

这是使用Swift 5实现函数的最简单和“最快”的方法:

func generateRandomBytes() -> String? {
    var bytes = [UInt8](repeating: 0, count: 32)
    let result = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)

    guard result == errSecSuccess else {
        print("Problem generating random bytes")
        return nil
    }

    return Data(bytes).base64EncodedString()
}

通常在Swift中,当函数的控制流取决于表达式的成功或失败,或者存在非空值时,最好使用guard语句而不是if/else语句。


7
根据 苹果文档,它看起来类似于这样:
public func randomData(ofLength length: Int) throws -> Data {
    var bytes = [UInt8](repeating: 0, count: length)
    let status = SecRandomCopyBytes(kSecRandomDefault, length, &bytes)
    if status == errSecSuccess {
        return Data(bytes: bytes)
    }
    // throw an error
}

或者作为附加的初始化器:

public extension Data {
    public init(randomOfLength length: Int) throws {
        var bytes = [UInt8](repeating: 0, count: length)
        let status = SecRandomCopyBytes(kSecRandomDefault, length, &bytes)
        if status == errSecSuccess {
            self.init(bytes: bytes)
        } else {
            // throw an error
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接