加密/兰特的典型用法是这样的:在什么情况下,crypto/rand read()的返回值会有用吗?
salt := make([]byte, saltLength)
n,err := rand.Read(salt)
与随机字节序列这里填充字节片我都标有“盐”。
在什么情况下随机数生成器会失败?在err不是零的情况下,回到数学/兰德等价物会不安全吗?
由于字节片段的长度已经知道,n对我来说也似乎没用,是不是有什么理由我不会只用_,err代替它?
加密/兰特的典型用法是这样的:在什么情况下,crypto/rand read()的返回值会有用吗?
salt := make([]byte, saltLength)
n,err := rand.Read(salt)
与随机字节序列这里填充字节片我都标有“盐”。
在什么情况下随机数生成器会失败?在err不是零的情况下,回到数学/兰德等价物会不安全吗?
由于字节片段的长度已经知道,n对我来说也似乎没用,是不是有什么理由我不会只用_,err代替它?
为了安全起见你的代码看起来更像是这样的:
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)
如果熵不足是可用的。你应该经常检查错误。
例如,获得一系列随机数的众多方法之一是Linux上的getrandom
系统调用或Windows上的CryptGenRandom
API调用。
参考文献:
random: introduce getrandom(2) system call
附录:
的crypto/rand
包是密码安全伪随机数发生器。包math/rand
不是加密安全的。
即使是一个简单的程序中也有太多路径来测试它们。因此,编写零缺陷和零错误程序的唯一方法是编写可读,可维护的代码,这些代码是非常正确的。 Niklaus Wirth的系统编程是一个很好的入门书。花时间构建一个强大的通用表单非常值得,它可以很容易地适应每种特殊情况,并且随着需求的变化很容易维护。
例如,对于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方法的接口。
读取最多len(p)个字节到p。它返回读取的字节数 (0 < = n < = len(p))以及遇到的任何错误。即使读取 返回len(p),它可能会在调用 期间将所有p用作暂存空间。如果有些数据可用但不是len(p)字节,则通常返回可用的数据,而不是等待更多数据。
当 成功读取n> 0字节后,如果读取遇到错误或文件结束条件,它将返回读取的字节数。 它可能会从相同的调用中返回(非零)错误,或者返回后续调用中的 错误(和n == 0)。这种通用 情况的一个实例是,在输入流的末尾 处返回非零字节数的Reader可以返回err == EOF或err == nil。接下来的 应该返回0,EOF不管。
考虑到错误错误,调用者应始终处理在 之前返回的n> 0个字节。这样做可以正确处理在读取一些字节以及允许的EOF 行为之后发生的 发生的I/O错误。
不推荐使用Read的实现返回一个零字节 计数为零的错误,并且调用者应该将该情况视为 禁止操作。
我们只想分配一次缓冲区,然后再启动Read
循环。但是,我们希望编译器和运行时检测是否我们在Read
循环中超出有效缓冲区长度n
,因此我们编写buf = buf[:n]
。但是,当我们循环到下一个Read
时,我们明确需要完整缓冲区:buf[:cap(buf)
。
编写Read(buf[:cap(buf)])
绝对不会错。即使您现在可能没有Read
循环,您可能稍后再添加一个循环,但您可能会忘记重置缓冲区长度。对于特定的Read
实施可能有特殊情况,如底层ReadFull
。现在您必须阅读并监视底层代码,以证明您的代码是正确的。文件并不总是可靠的。并且您不能安全地切换到另一个io.Reader
Read
实施。
当您访问salt
切片时,salt[:len(salt)]
,您使用的是len(salt)
而不是n
。如果他们不同,你有一个错误。
“的实施应遵循稳健性的一般原则:是 保守你做什么,是你从 别人接受什么样的自由。” Jon Postel
@ 4of4:不正确。这是防弹代码。 – peterSO 2014-10-26 22:02:06
太棒了!谢谢!那么我是否假设使用数学/兰德作为后备,如果加密/兰德失败将不安全?你能解释一下你为什么要做'salt = salt [:n]'为什么不把saltLength和n比较? – jcmiller11 2014-10-27 13:17:28
@ jcmiller11:查看我答案的附录。 – peterSO 2014-10-27 15:29:26
如果没有足够的熵可用于生成“随机”(无论是什么意思)数量。 – Volker 2014-10-26 21:03:36