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

Random 在什么情况下,crypto/rand read()的两个返回值会有用?,random,hash,go,Random,Hash,Go,crypto/rand的典型用法如下: salt := make([]byte, saltLength) n,err := rand.Read(salt) package main import ( "crypto/rand" "fmt" ) func main() { saltLength := 16 salt := make([]byte, saltLength) n, err := rand.Read(salt[:cap(salt)])

crypto/rand的典型用法如下:

salt := make([]byte, saltLength)
n,err := rand.Read(salt)
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)
}
它用随机字节序列填充了我在这里标记为salt的字节片

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


既然字节片的长度已经知道了,n对我来说似乎也没什么用处,有什么理由我不直接使用u,err代替它吗?

为了安全起见,您的代码应该更像这样:

salt := make([]byte, saltLength)
n,err := rand.Read(salt)
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可能小于lensalt。你应该经常检查错误

例如,获取随机数序列的许多方法之一是Linux上的getrandom系统调用或Windows上的CryptGenRandom API调用

参考资料:

增编:

crypto/rand软件包是一个加密安全的伪随机数生成器。包math/rand不是加密安全的

即使在一个简单的程序中,也有太多的路径来测试它们。因此,编写零缺陷和零bug程序的唯一方法是编写可读的、可维护的、可证明正确的代码。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
}
Reader是封装基本读取方法的接口

Read最多可将lenp字节读入p。它返回字节数 读取之前返回的0字节 考虑到这个错误。这样做可以正确地处理 在读取一些字节以及两个允许的EOF后发生 行为

不鼓励Read的实现返回零字节 计数时出现零错误,调用方应将该情况视为错误 禁止操作

在开始读循环之前,我们只想分配一次缓冲区。但是,我们希望编译器和运行时检测读取循环中是否超出了有效缓冲区长度n,因此我们写入buf=buf[:n]。然而,当我们循环到下一次读取时,我们明确地想要完整的缓冲区:buf[:capbuf

写Readbuf从来没有错。即使您现在可能没有读循环,您也可以稍后添加一个,并且您可能会忘记重置缓冲区长度。对于特定的读实现,例如底层ReadFull,可能会有特殊情况。现在您必须读取并监视底层代码,以证明您的代码是正确的。文档并不总是可靠的。您可以't无法安全地切换到另一个io.Reader读取实现

当您访问salt切片salt[:lensalt]时,您使用的是lensalt而不是n。如果它们不同,则您有一个bug

实现应该遵循健壮性的一般原则:be 在你所做的事情上要保守,在你所接受的事情上要自由 其他的


如果没有足够的熵来生成一个随机数,不管这意味着什么。太好了!谢谢!那么我假设如果加密/随机失败,使用math/rand作为回退是不安全的吗?你能解释一下为什么要使用salt=salt[:n]为什么不将saltLength与n进行比较?@jcmiller11:请参阅我的答案的附录。@4of4:作为一般规则,对于任何API,如果返回错误,除非明确定义,否则假定其他返回值未定义。这意味着我们通常先检查err。然而,对于io.Reader.Read,n定义良好;它是返回的字节数在出现任何错误之前将ad放入buf。除n==0外,buf[:n]将被传递到//进程读取缓冲区,您可以在其中添加长度测试。在//进程读取缓冲区之后,我们检查错误。例如,假设n,err:=rdr。Readbuf[:capbuf]返回n==saltLength,其中saltLength>0,err==io.ErrNoProgress。