1 回答
TA贡献1876条经验 获得超5个赞
为了安全起见,您的代码应该更像这样:
package main
import (
"crypto/rand"
"fmt"
)
func main() {
saltLength := 16
salt := make([]byte, saltLength)
n, err := rand.Read(salt[:cap(salt)])
if err != nil {
// handle error
}
salt = salt[:n]
if len(salt) != saltLength {
// handle error
}
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 {
bufLen := 256
buf := make([]byte, bufLen)
for {
n, err := rdr.Read(buf[:cap(buf)])
if n == 0 {
if err == nil {
continue
}
if err == io.EOF {
break
}
return err
}
buf = buf[:n]
// process read buffer
if err != nil && err != io.EOF {
return err
}
}
return nil
}
类型阅读器
type Reader interface {
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。如果它们不同,则说明存在错误。
“实施应遵循稳健的一般原则:在所做的事情上保持保守,从他人那里接受的事情上保持自由。” 乔恩·波斯特尔
- 1 回答
- 0 关注
- 285 浏览
添加回答
举报