在什么情况下,crypto/rand read() 的两个返回值会有用?

crypto/rand 的典型用法是这样的:


salt := make([]byte, saltLength)

n,err := rand.Read(salt)

它用随机字节序列填充了我在此处标记为“盐”的字节切片。


在什么情况下随机数生成器可能会失败?如果 err 不为零,返回到数学/兰特等价物是否不安全?


由于字节切片的长度已经知道,n 对我来说似乎也没用,有什么理由我不只是使用 _,err 代替它?


慕田峪7331174
浏览 286回答 1
1回答

慕运维8079593

为了安全起见,您的代码应该更像这样:package mainimport (&nbsp; &nbsp; "crypto/rand"&nbsp; &nbsp; "fmt")func main() {&nbsp; &nbsp; saltLength := 16&nbsp; &nbsp; salt := make([]byte, saltLength)&nbsp; &nbsp; n, err := rand.Read(salt[:cap(salt)])&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; // handle error&nbsp; &nbsp; }&nbsp; &nbsp; salt = salt[:n]&nbsp; &nbsp; if len(salt) != saltLength {&nbsp; &nbsp; &nbsp; &nbsp; // handle error&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println(len(salt), salt)}输出:16 [191 235 81 37 175 238 93 202 230 158 41 199 202 85 67 209]n可能小于len(salt)可用熵不足的情况。您应该始终检查错误。例如,获取随机数序列的众多方法之一是getrandomLinux 上的系统调用或CryptGenRandomWindows上的API 调用。附录:该crypto/rand软件包是一个加密安全的伪随机数生成器。包math/rand不是加密安全的。即使是一个简单的程序,也有太多的路径来测试它们。因此,编写零缺陷和零错误的程序的唯一方法是编写可证明正确的可读、可维护的代码。Niklaus Wirth 的 Systematic Programming 是一本很好的入门书。花时间构建一个健壮的通用表单是值得的,它可以很容易地适应每个特殊情况,并且随着需求的变化很容易维护。例如,对于io.Reader接口,典型的用法是循环模式。func Reader(rdr io.Reader) error {&nbsp; &nbsp; bufLen := 256&nbsp; &nbsp; buf := make([]byte, bufLen)&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; n, err := rdr.Read(buf[:cap(buf)])&nbsp; &nbsp; &nbsp; &nbsp; if n == 0 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err == nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err == io.EOF {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; buf = buf[:n]&nbsp; &nbsp; &nbsp; &nbsp; // process read buffer&nbsp; &nbsp; &nbsp; &nbsp; if err != nil && err != io.EOF {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; return nil}类型阅读器type Reader interface {&nbsp; &nbsp; &nbsp; &nbsp; Read(p []byte) (n int, err error)}Reader 是包装基本 Read 方法的接口。Read 将最多 len(p) 个字节读入 p。它返回读取的字节数 (0 <= n <= len(p)) 和遇到的任何错误。即使 Read 返回 n < len(p),它也可能在调用期间使用所有 p 作为暂存空间。如果某些数据可用但 len(p) 字节不可用,则 Read 通常会返回可用的数据,而不是等待更多数据。当 Read 在成功读取 n > 0 个字节后遇到错误或文件结束条件时,它返回读取的字节数。它可能会从同一个调用中返回(非零)错误或从后续调用中返回错误(和 n == 0)。这种一般情况的一个实例是,在输入流末尾返回非零字节数的 Reader 可能返回 err == EOF 或 err == nil。不管怎样,下一个 Read 应该返回 0,EOF。在考虑错误 err 之前,调用者应始终处理返回的 n > 0 个字节。这样做可以正确处理在读取一些字节后发生的 I/O 错误以及允许的 EOF 行为。不鼓励 Read 的实现返回带有 nil 错误的零字节计数,并且调用者应该将这种情况视为无操作。我们只想在开始Read循环之前分配缓冲区一次。但是,我们希望编译器和运行时,如果我们偏离了有效的缓冲区长度外检测n中Read循环,所以我们写buf = buf[:n]。然而,当我们循环到下一个Read我们要明确全缓冲:buf[:cap(buf)。写永远不会错Read(buf[:cap(buf)])。即使您Read现在可能没有循环,您也可以稍后添加循环,并且您可能会忘记重置缓冲区长度。特定Read实现可能有特殊情况,例如底层ReadFull. 现在您必须阅读并监控底层代码以证明您的代码是正确的。文档并不总是可靠的。而且您无法安全地切换到另一个io.Reader Read实现。当您访问salt切片时salt[:len(salt)],您使用的是len(salt)not n。如果它们不同,则说明存在错误。“实施应遵循稳健的一般原则:在所做的事情上保持保守,从他人那里接受的事情上保持自由。” 乔恩·波斯特尔
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go