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

时间:2014-10-26 21:01:38

标签: random hash go

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

salt := make([]byte, saltLength)
n,err := rand.Read(salt)

填充我标记的字节切片" salt"这里有一系列随机字节。

在什么情况下随机数发生器会失败?如果错误不是零,那么回到数学/兰特等值是否不安全?

由于字节切片的长度已经知道,n对我来说似乎也没用,是否有任何理由我不会使用_,错误代替它?

1 个答案:

答案 0 :(得分:2)

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

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

CryptGenRandom function

附录:

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

type Reader interface {
        Read(p []byte) (n int, err error)
}
     

Reader是包装基本Read方法的接口。

     

读取读取最多len(p)个字节到p。它返回字节数   读取(0< = n< = len(p))并遇到任何错误。即使阅读   返回n< len(p),它可以使用所有p作为临时空间   呼叫。如果某些数据可用但不是len(p)字节,则读取   通常会返回可用的内容,而不是等待更多内容。

     

Read之后遇到错误或文件结束条件   成功阅读n> 0字节,它返回读取的字节数。   它可能会从同一个调用返回(非零)错误或返回   来自后续调用的错误(和n == 0)。这个将军的一个例子   case是一个Reader在结尾返回非零字节数   输入流可以返回err == EOF或err == nil。该   next Read应返回0,EOF无论如何。

     

来电者应始终处理n>之前返回0个字节   考虑到错误错误。这样做可以正确处理I / O错误   在读取一些字节以及两个允许的EOF之后发生   的行为。

     

不鼓励读取的实现返回零字节   以零错误计数,并且呼叫者应将该情况视为一个   无操作。

我们只想在启动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)而不是n。如果它们不同,那你就有一个错误。

  

“实现应遵循健壮性的一般原则:be   在你所做的事情上保守,在你接受的事上保持自由   其他人。“Jon Postel