dkforest

A forum and chat platform (onion)
git clone https://git.dasho.dev/n0tr1v/dkforest.git
Log | Files | Refs | LICENSE

stream.go (3484B)


      1 package crypto
      2 
      3 import (
      4 	"crypto/aes"
      5 	"crypto/cipher"
      6 	"crypto/hmac"
      7 	"crypto/rand"
      8 	"crypto/sha256"
      9 	"errors"
     10 	"hash"
     11 	"io"
     12 )
     13 
     14 func NewCtrStram(encKey []byte) (cipher.Stream, cipher.Block, []byte, error) {
     15 	block, err := aes.NewCipher(encKey)
     16 	if err != nil {
     17 		return nil, nil, nil, err
     18 	}
     19 	iv := make([]byte, block.BlockSize())
     20 	_, err = rand.Read(iv)
     21 	if err != nil {
     22 		return nil, nil, nil, err
     23 	}
     24 	stream := cipher.NewCTR(block, iv)
     25 	return stream, block, iv, nil
     26 }
     27 
     28 // NewStreamEncrypter creates a new stream encrypter
     29 func NewStreamEncrypter(encKey, macKey []byte, plainText io.Reader) (*StreamEncrypter, error) {
     30 	stream, block, iv, err := NewCtrStram(encKey)
     31 	if err != nil {
     32 		return nil, err
     33 	}
     34 
     35 	mac := hmac.New(sha256.New, macKey)
     36 	return &StreamEncrypter{
     37 		Source: plainText,
     38 		Block:  block,
     39 		Stream: stream,
     40 		Mac:    mac,
     41 		IV:     iv,
     42 	}, nil
     43 }
     44 
     45 // NewStreamDecrypter creates a new stream decrypter
     46 func NewStreamDecrypter(encKey, macKey []byte, meta StreamMeta, cipherText io.Reader) (*StreamDecrypter, error) {
     47 	block, err := aes.NewCipher(encKey)
     48 	if err != nil {
     49 		return nil, err
     50 	}
     51 	stream := cipher.NewCTR(block, meta.IV)
     52 	mac := hmac.New(sha256.New, macKey)
     53 	return &StreamDecrypter{
     54 		Source: cipherText,
     55 		Block:  block,
     56 		Stream: stream,
     57 		Mac:    mac,
     58 		Meta:   meta,
     59 	}, nil
     60 }
     61 
     62 // StreamEncrypter is an encrypter for a stream of data with authentication
     63 type StreamEncrypter struct {
     64 	Source io.Reader
     65 	Block  cipher.Block
     66 	Stream cipher.Stream
     67 	Mac    hash.Hash
     68 	IV     []byte
     69 }
     70 
     71 // StreamDecrypter is a decrypter for a stream of data with authentication
     72 type StreamDecrypter struct {
     73 	Source io.Reader
     74 	Block  cipher.Block
     75 	Stream cipher.Stream
     76 	Mac    hash.Hash
     77 	Meta   StreamMeta
     78 }
     79 
     80 // Read encrypts the bytes of the inner reader and places them into p
     81 func (s *StreamEncrypter) Read(p []byte) (int, error) {
     82 	n, readErr := s.Source.Read(p)
     83 	if n > 0 {
     84 		s.Stream.XORKeyStream(p[:n], p[:n])
     85 		err := writeHash(s.Mac, p[:n])
     86 		if err != nil {
     87 			return n, err
     88 		}
     89 		return n, readErr
     90 	}
     91 	return 0, io.EOF
     92 }
     93 
     94 // Meta returns the encrypted stream metadata for use in decrypting. This should only be called after the stream is finished
     95 func (s *StreamEncrypter) Meta() StreamMeta {
     96 	return StreamMeta{IV: s.IV, Hash: s.Mac.Sum(nil)}
     97 }
     98 
     99 // Read reads bytes from the underlying reader and then decrypts them
    100 func (s *StreamDecrypter) Read(p []byte) (int, error) {
    101 	n, readErr := s.Source.Read(p)
    102 	if n > 0 {
    103 		err := writeHash(s.Mac, p[:n])
    104 		if err != nil {
    105 			return n, err
    106 		}
    107 		s.Stream.XORKeyStream(p[:n], p[:n])
    108 		return n, readErr
    109 	}
    110 	return 0, io.EOF
    111 }
    112 
    113 // Authenticate verifys that the hash of the stream is correct. This should only be called after processing is finished
    114 func (s *StreamDecrypter) Authenticate() error {
    115 	if !hmac.Equal(s.Meta.Hash, s.Mac.Sum(nil)) {
    116 		return errors.New("authentication failed")
    117 	}
    118 	return nil
    119 }
    120 
    121 func writeHash(mac hash.Hash, p []byte) error {
    122 	m, err := mac.Write(p)
    123 	if err != nil {
    124 		return err
    125 	}
    126 	if m != len(p) {
    127 		return errors.New("could not write all bytes to hmac")
    128 	}
    129 	return nil
    130 }
    131 
    132 func checkedWrite(dst io.Writer, p []byte) (int, error) {
    133 	n, err := dst.Write(p)
    134 	if err != nil {
    135 		return n, err
    136 	}
    137 	if n != len(p) {
    138 		return n, errors.New("unable to write all bytes")
    139 	}
    140 	return len(p), nil
    141 }
    142 
    143 // StreamMeta is metadata about an encrypted stream
    144 type StreamMeta struct {
    145 	// IV is the initial value for the crypto function
    146 	IV []byte
    147 	// Hash is the sha256 hmac of the stream
    148 	Hash []byte
    149 }