为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
慕田峪7331174 2021-08-23 17:50:24
crypto/rand 的典型用法是这样的:salt := make([]byte, saltLength)n,err := rand.Read(salt)它用随机字节序列填充了我在此处标记为“盐”的字节切片。在什么情况下随机数生成器可能会失败?如果 err 不为零,返回到数学/兰特等价物是否不安全?由于字节切片的长度已经知道,n 对我来说似乎也没用,有什么理由我不只是使用 _,err 代替它?
查看完整描述

1 回答

?
慕运维8079593

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。如果它们不同,则说明存在错误。


“实施应遵循稳健的一般原则:在所做的事情上保持保守,从他人那里接受的事情上保持自由。” 乔恩·波斯特尔


查看完整回答
反对 回复 2021-08-23
  • 1 回答
  • 0 关注
  • 285 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信