dragon_9000 2014-10-26 21:01
浏览 303
已采纳

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

The typical usage of crypto/rand goes something like this:

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

Which fills the byte slice I have labeled "salt" here with a sequence of random bytes.

Under what circumstances might the random number generator fail? Would it be insecure to fall back to a math/rand equivalent in the event that err is not nil?

Since the length of the byte slice is already known, n also seems useless to me, is there any reason I wouldn't just use _,err in its place?

  • 写回答

1条回答 默认 最新

  • dongtangjie0495 2014-10-26 21:50
    关注

    To be safe your code should look more like this:

    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)
    }
    

    Output:

    16 [191 235 81 37 175 238 93 202 230 158 41 199 202 85 67 209]
    

    n may be less than len(salt) if insufficient entropy is available. You should always check for errors.

    For example, one of the many ways to obtain a sequence of random numbers is the getrandom system call on Linux or the CryptGenRandom API call on Windows.

    References:

    random: introduce getrandom(2) system call

    CryptGenRandom function

    ADDENDUM:

    The crypto/rand package is a cryptographically secure pseudorandom number generator. Package math/rand is not cryptographically secure.

    There are too many paths in even a simple program to test them all. Therefore, the only way to write programs with zero defects and zero bugs is to write readable, maintainable code that is provably correct. Systematic Programming by Niklaus Wirth is a good primer. It's worthwhile to spend time on constructing a robust general form, which can easily be adapted to each special case and that is easily maintainable as requirements change.

    For example, for the io.Reader interface, typical usage is a looping pattern.

    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 is the interface that wraps the basic Read method.

    Read reads up to len(p) bytes into p. It returns the number of bytes read (0 <= n <= len(p)) and any error encountered. Even if Read returns n < len(p), it may use all of p as scratch space during the call. If some data is available but not len(p) bytes, Read conventionally returns what is available instead of waiting for more.

    When Read encounters an error or end-of-file condition after successfully reading n > 0 bytes, it returns the number of bytes read. It may return the (non-nil) error from the same call or return the error (and n == 0) from a subsequent call. An instance of this general case is that a Reader returning a non-zero number of bytes at the end of the input stream may return either err == EOF or err == nil. The next Read should return 0, EOF regardless.

    Callers should always process the n > 0 bytes returned before considering the error err. Doing so correctly handles I/O errors that happen after reading some bytes and also both of the allowed EOF behaviors.

    Implementations of Read are discouraged from returning a zero byte count with a nil error, and callers should treat that situation as a no-op.

    We only want to allocate the buffer once, before we start the Read loop. However, we want the compiler and runtime to detect if we stray outside the valid buffer length n in the Read loop, so we write buf = buf[:n]. However, when we loop to the next Read we explicitly want the full buffer: buf[:cap(buf).

    It's never wrong to write Read(buf[:cap(buf)]). Even though you may not have a Read loop now, you may add one later, and you may forget to reset the buffer length. There may be special case for a particular Read implementation, like an underlying ReadFull. Now you have to read and monitor the underlying code to prove that your code is correct. Documentation is not always reliable. And you can't safely switch to another io.Reader Read implementation.

    When you access the salt slice, salt[:len(salt)], you are using len(salt) not n. If they differ, you have a bug.

    "implementations should follow a general principle of robustness: be conservative in what you do, be liberal in what you accept from others." Jon Postel

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 file converter 转换格式失败 报错 Error marking filters as finished,如何解决?
  • ¥15 ubuntu系统下挂载磁盘上执行./提示权限不够
  • ¥15 Arcgis相交分析无法绘制一个或多个图形
  • ¥15 关于#r语言#的问题:差异分析前数据准备,报错Error in data[, sampleName1] : subscript out of bounds请问怎么解决呀以下是全部代码:
  • ¥15 seatunnel-web使用SQL组件时候后台报错,无法找到表格
  • ¥15 fpga自动售货机数码管(相关搜索:数字时钟)
  • ¥15 用前端向数据库插入数据,通过debug发现数据能走到后端,但是放行之后就会提示错误
  • ¥30 3天&7天&&15天&销量如何统计同一行
  • ¥30 帮我写一段可以读取LD2450数据并计算距离的Arduino代码
  • ¥15 飞机曲面部件如机翼,壁板等具体的孔位模型