package cipher

import "crypto/cipher"

cipher包实现了多个标准的用于包装底层块加密算法的加密算法实现。

参见http://csrc.nist.gov/groups/ST/toolkit/BCM/current_modes.html和NIST Special Publication 800-38A。

Index

返回首页


  • type Block
  • type BlockMode
  • type Stream
  • type StreamReader
  • type StreamWriter
  • type AEAD
  • Examples

    返回首页


  • NewCBCDecrypter
  • NewCBCEncrypter
  • NewCFBDecrypter
  • NewCFBEncrypter
  • NewCTR
  • NewOFB
  • StreamReader
  • StreamWriter
  • type Block

    type Block interface {
        // 返回加密字节块的大小
        BlockSize() int
        // 加密src的第一块数据并写入dst,src和dst可指向同一内存地址
        Encrypt(dst, src []byte)
        // 解密src的第一块数据并写入dst,src和dst可指向同一内存地址
        Decrypt(dst, src []byte)
    }

    Block接口代表一个使用特定密钥的底层块加/解密器。它提供了加密和解密独立数据块的能力。

    type BlockMode

    type BlockMode interface {
        // 返回加密字节块的大小
        BlockSize() int
        // 加密或解密连续的数据块,src的尺寸必须是块大小的整数倍,src和dst可指向同一内存地址
        CryptBlocks(dst, src []byte)
    }

    BlockMode接口代表一个工作在块模式(如CBC、ECB等)的加/解密器。

    func NewCBCEncrypter

    func NewCBCEncrypter(b Block, iv []byte) BlockMode

    返回一个密码分组链接模式的、底层用b加密的BlockMode接口,初始向量iv的长度必须等于b的块尺寸。

    Example
    key := []byte("example key 1234")
    plaintext := []byte("exampleplaintext")
    // CBC mode works on blocks so plaintexts may need to be padded to the
    // next whole block. For an example of such padding, see
    // https://tools.ietf.org/html/rfc5246#section-6.2.3.2. Here we'll
    // assume that the plaintext is already of the correct length.
    if len(plaintext)%aes.BlockSize != 0 {
        panic("plaintext is not a multiple of the block size")
    }
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }
    // The IV needs to be unique, but not secure. Therefore it's common to
    // include it at the beginning of the ciphertext.
    ciphertext := make([]byte, aes.BlockSize+len(plaintext))
    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        panic(err)
    }
    mode := cipher.NewCBCEncrypter(block, iv)
    mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
    // It's important to remember that ciphertexts must be authenticated
    // (i.e. by using crypto/hmac) as well as being encrypted in order to
    // be secure.
    fmt.Printf("%x\n", ciphertext)
    

    func NewCBCDecrypter

    func NewCBCDecrypter(b Block, iv []byte) BlockMode

    返回一个密码分组链接模式的、底层用b解密的BlockMode接口,初始向量iv必须和加密时使用的iv相同。

    Example
    key := []byte("example key 1234")
    ciphertext, _ := hex.DecodeString("f363f3ccdcb12bb883abf484ba77d9cd7d32b5baecb3d4b1b3e0e4beffdb3ded")
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }
    // The IV needs to be unique, but not secure. Therefore it's common to
    // include it at the beginning of the ciphertext.
    if len(ciphertext) < aes.BlockSize {
        panic("ciphertext too short")
    }
    iv := ciphertext[:aes.BlockSize]
    ciphertext = ciphertext[aes.BlockSize:]
    // CBC mode always works in whole blocks.
    if len(ciphertext)%aes.BlockSize != 0 {
        panic("ciphertext is not a multiple of the block size")
    }
    mode := cipher.NewCBCDecrypter(block, iv)
    // CryptBlocks can work in-place if the two arguments are the same.
    mode.CryptBlocks(ciphertext, ciphertext)
    // If the original plaintext lengths are not a multiple of the block
    // size, padding would have to be added when encrypting, which would be
    // removed at this point. For an example, see
    // https://tools.ietf.org/html/rfc5246#section-6.2.3.2. However, it's
    // critical to note that ciphertexts must be authenticated (i.e. by
    // using crypto/hmac) before being decrypted in order to avoid creating
    // a padding oracle.
    fmt.Printf("%s\n", ciphertext)

    Output:

    exampleplaintext
    

    type Stream

    type Stream interface {
        // 从加密器的key流和src中依次取出字节二者xor后写入dst,src和dst可指向同一内存地址
        XORKeyStream(dst, src []byte)
    }

    Stream接口代表一个流模式的加/解密器。

    func NewCFBEncrypter

    func NewCFBEncrypter(block Block, iv []byte) Stream

    返回一个密码反馈模式的、底层用block加密的Stream接口,初始向量iv的长度必须等于block的块尺寸。

    Example
    key := []byte("example key 1234")
    plaintext := []byte("some plaintext")
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }
    // The IV needs to be unique, but not secure. Therefore it's common to
    // include it at the beginning of the ciphertext.
    ciphertext := make([]byte, aes.BlockSize+len(plaintext))
    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        panic(err)
    }
    stream := cipher.NewCFBEncrypter(block, iv)
    stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
    // It's important to remember that ciphertexts must be authenticated
    // (i.e. by using crypto/hmac) as well as being encrypted in order to
    // be secure.
    

    func NewCFBDecrypter

    func NewCFBDecrypter(block Block, iv []byte) Stream

    返回一个密码反馈模式的、底层用block解密的Stream接口,初始向量iv必须和加密时使用的iv相同。

    Example
    key := []byte("example key 1234")
    ciphertext, _ := hex.DecodeString("22277966616d9bc47177bd02603d08c9a67d5380d0fe8cf3b44438dff7b9")
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }
    // The IV needs to be unique, but not secure. Therefore it's common to
    // include it at the beginning of the ciphertext.
    if len(ciphertext) < aes.BlockSize {
        panic("ciphertext too short")
    }
    iv := ciphertext[:aes.BlockSize]
    ciphertext = ciphertext[aes.BlockSize:]
    stream := cipher.NewCFBDecrypter(block, iv)
    // XORKeyStream can work in-place if the two arguments are the same.
    stream.XORKeyStream(ciphertext, ciphertext)
    fmt.Printf("%s", ciphertext)

    Output:

    some plaintext
    

    func NewOFB

    func NewOFB(b Block, iv []byte) Stream

    返回一个输出反馈模式的、底层采用b生成key流的Stream接口,初始向量iv的长度必须等于b的块尺寸。

    Example
    key := []byte("example key 1234")
    plaintext := []byte("some plaintext")
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }
    // The IV needs to be unique, but not secure. Therefore it's common to
    // include it at the beginning of the ciphertext.
    ciphertext := make([]byte, aes.BlockSize+len(plaintext))
    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        panic(err)
    }
    stream := cipher.NewOFB(block, iv)
    stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
    // It's important to remember that ciphertexts must be authenticated
    // (i.e. by using crypto/hmac) as well as being encrypted in order to
    // be secure.
    // OFB mode is the same for both encryption and decryption, so we can
    // also decrypt that ciphertext with NewOFB.
    plaintext2 := make([]byte, len(plaintext))
    stream = cipher.NewOFB(block, iv)
    stream.XORKeyStream(plaintext2, ciphertext[aes.BlockSize:])
    fmt.Printf("%s\n", plaintext2)

    Output:

    some plaintext
    

    func NewCTR

    func NewCTR(block Block, iv []byte) Stream

    返回一个计数器模式的、底层采用block生成key流的Stream接口,初始向量iv的长度必须等于block的块尺寸。

    Example
    key := []byte("example key 1234")
    plaintext := []byte("some plaintext")
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }
    // The IV needs to be unique, but not secure. Therefore it's common to
    // include it at the beginning of the ciphertext.
    ciphertext := make([]byte, aes.BlockSize+len(plaintext))
    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        panic(err)
    }
    stream := cipher.NewCTR(block, iv)
    stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
    // It's important to remember that ciphertexts must be authenticated
    // (i.e. by using crypto/hmac) as well as being encrypted in order to
    // be secure.
    // CTR mode is the same for both encryption and decryption, so we can
    // also decrypt that ciphertext with NewCTR.
    plaintext2 := make([]byte, len(plaintext))
    stream = cipher.NewCTR(block, iv)
    stream.XORKeyStream(plaintext2, ciphertext[aes.BlockSize:])
    fmt.Printf("%s\n", plaintext2)

    Output:

    some plaintext
    

    type StreamReader

    type StreamReader struct {
        S   Stream
        R   io.Reader
    }

    将一个Stream与一个io.Reader接口关联起来,Read方法会调用XORKeyStream方法来处理获取的所有切片。

    Example
    key := []byte("example key 1234")
    inFile, err := os.Open("encrypted-file")
    if err != nil {
        panic(err)
    }
    defer inFile.Close()
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }
    // If the key is unique for each ciphertext, then it's ok to use a zero
    // IV.
    var iv [aes.BlockSize]byte
    stream := cipher.NewOFB(block, iv[:])
    outFile, err := os.OpenFile("decrypted-file", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
    if err != nil {
        panic(err)
    }
    defer outFile.Close()
    reader := &cipher.StreamReader{S: stream, R: inFile}
    // Copy the input file to the output file, decrypting as we go.
    if _, err := io.Copy(outFile, reader); err != nil {
        panic(err)
    }
    // Note that this example is simplistic in that it omits any
    // authentication of the encrypted data. It you were actually to use
    // StreamReader in this manner, an attacker could flip arbitrary bits in
    // the output.
    

    func (StreamReader) Read

    func (r StreamReader) Read(dst []byte) (n int, err error)

    type StreamWriter

    type StreamWriter struct {
        S   Stream
        W   io.Writer
        Err error // unused
    }

    将一个Stream与一个io.Writer接口关联起来,Write方法会调用XORKeyStream方法来处理提供的所有切片。如果Write方法返回的n小于提供的切片的长度,则表示StreamWriter不同步,必须丢弃。StreamWriter没有内建的缓存,不需要调用Close方法去清空缓存。

    Example
    key := []byte("example key 1234")
    inFile, err := os.Open("plaintext-file")
    if err != nil {
        panic(err)
    }
    defer inFile.Close()
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }
    // If the key is unique for each ciphertext, then it's ok to use a zero
    // IV.
    var iv [aes.BlockSize]byte
    stream := cipher.NewOFB(block, iv[:])
    outFile, err := os.OpenFile("encrypted-file", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
    if err != nil {
        panic(err)
    }
    defer outFile.Close()
    writer := &cipher.StreamWriter{S: stream, W: outFile}
    // Copy the input file to the output file, encrypting as we go.
    if _, err := io.Copy(writer, inFile); err != nil {
        panic(err)
    }
    // Note that this example is simplistic in that it omits any
    // authentication of the encrypted data. It you were actually to use
    // StreamReader in this manner, an attacker could flip arbitrary bits in
    // the decrypted result.
    

    func (StreamWriter) Write

    func (w StreamWriter) Write(src []byte) (n int, err error)

    func (StreamWriter) Close

    func (w StreamWriter) Close() error

    如果w.W字段实现了io.Closer接口,本方法会调用其Close方法并返回该方法的返回值;否则不做操作返回nil。

    type AEAD

    type AEAD interface {
        // 返回提供给Seal和Open方法的随机数nonce的字节长度
        NonceSize() int
        // 返回原始文本和加密文本的最大长度差异
        Overhead() int
        // 加密并认证明文,认证附加的data,将结果添加到dst,返回更新后的切片。
        // nonce的长度必须是NonceSize()字节,且对给定的key和时间都是独一无二的。
        // plaintext和dst可以是同一个切片,也可以不同。
        Seal(dst, nonce, plaintext, data []byte) []byte
        // 解密密文并认证,认证附加的data,如果认证成功,将明文添加到dst,返回更新后的切片。
        // nonce的长度必须是NonceSize()字节,nonce和data都必须和加密时使用的相同。
        // ciphertext和dst可以是同一个切片,也可以不同。
        Open(dst, nonce, ciphertext, data []byte) ([]byte, error)
    }

    AEAD接口是一种提供了使用关联数据进行认证加密的功能的加密模式。

    func NewGCM

    func NewGCM(cipher Block) (AEAD, error)

    函数用迦洛瓦计数器模式包装提供的128位Block接口,并返回AEAD接口。