github.com/ronperry/cryptoedge@v0.0.0-20150815114006-cc363e290743/lioness/cipher.go (about)

     1  // Package lioness implements the LIONESS Large Block Cipher with some modifications:
     2  // Instead of using a standard stream cipher, it only allows usage of block ciphers that
     3  // implement the cipher.Block interface.
     4  // Furthermore it does not use the suggested hash operation but instead uses an HMAC everywhere a hash is used
     5  // in the original design
     6  // Paper: http://www.cl.cam.ac.uk/~rja14/Papers/bear-lion.pdf
     7  // In addition, this implementation supports two modes. ModeZero, which uses an all zero IV for the R-operation, and
     8  // ModeIV, which uses the content of L as the IV.
     9  package lioness
    10  
    11  import (
    12  	"crypto/aes"
    13  	"crypto/cipher"
    14  	"crypto/hmac"
    15  	"crypto/sha256"
    16  	"errors"
    17  	"hash"
    18  )
    19  
    20  const (
    21  	// ModeIV uses L as the IV for encryption
    22  	ModeIV = iota
    23  	// ModeZero uses all zeros as the IV for encryption
    24  	ModeZero
    25  )
    26  
    27  // Lioness holds internal data
    28  type Lioness struct {
    29  	blockcipher    func([]byte) (cipher.Block, error)
    30  	hash           func() hash.Hash
    31  	keylen         int
    32  	blocksize      int // blocksize of cipher
    33  	mode           int
    34  	k1, k2, k3, k4 []byte
    35  }
    36  
    37  var (
    38  	// ErrConstructed is returned if working with a lioness that hasn't been set up
    39  	ErrConstructed = errors.New("lioness: Missing setup")
    40  	// ErrKeyHashSize is returned if the key size is larger than the hash size
    41  	ErrKeyHashSize = errors.New("lioness: Hash smaller than key")
    42  	// ErrKeyLen is returned when the keys given do not match the key length
    43  	ErrKeyLen = errors.New("lioness: Keys have wrong size")
    44  	// ErrDataSize is returned when the data is too small (<=keylen)
    45  	ErrDataSize = errors.New("lioness: Not enough data")
    46  	// ErrNoKeys is returned if using a lioness without keys
    47  	ErrNoKeys = errors.New("lioness: Keys not set")
    48  )
    49  
    50  var zeroIV []byte
    51  
    52  // Construct returns a new Lioness. Takes block cipher, hash function and keylen. Key can be nil. mode is either ModeIV or ModeZero
    53  func Construct(blockcipher func([]byte) (cipher.Block, error), hash func() hash.Hash, keylen int, key []byte, mode int) (*Lioness, error) {
    54  	h := hash()
    55  	if h.Size() < keylen {
    56  		return nil, ErrKeyHashSize
    57  	}
    58  	algo, err := blockcipher(make([]byte, keylen))
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	t := new(Lioness)
    63  	t.blockcipher = blockcipher
    64  	t.blocksize = algo.BlockSize()
    65  	t.hash = hash
    66  	t.keylen = keylen
    67  	t.mode = mode
    68  	if key != nil {
    69  		err := t.ExplodeKey(key)
    70  		if err != nil {
    71  			return nil, err
    72  		}
    73  	}
    74  	return t, nil
    75  }
    76  
    77  // New is shorthand for Construct with aes256 and sha256 in ModeZero
    78  func New(key []byte) (*Lioness, error) {
    79  	return Construct(aes.NewCipher, sha256.New, 32, key, ModeZero)
    80  }
    81  
    82  // Setkeys sets the keys and verifies that they are of keylen length
    83  func (l *Lioness) Setkeys(k1, k2, k3, k4 []byte) error {
    84  	if !l.isConstructed() {
    85  		return ErrConstructed
    86  	}
    87  	if len(k1) != l.keylen || len(k2) != l.keylen || len(k3) != l.keylen || len(k4) != l.keylen {
    88  		return ErrKeyLen
    89  	}
    90  	l.k1, l.k2, l.k3, l.k4 = k1, k2, k3, k4
    91  	return nil
    92  }
    93  
    94  // ExplodeKey generates the Lioness keys from a single input key by calculating repeated HMACs (which is of questionable security)
    95  func (l *Lioness) ExplodeKey(key []byte) error {
    96  	if !l.isConstructed() {
    97  		return ErrConstructed
    98  	}
    99  	l.k1 = l.ropHMAC(key, append(key, key...))[0:l.keylen]
   100  	l.k2 = l.ropHMAC(key, append(l.k1, key...))[0:l.keylen]
   101  	l.k3 = l.ropHMAC(key, append(l.k2, key...))[0:l.keylen]
   102  	l.k4 = l.ropHMAC(key, append(l.k3, key...))[0:l.keylen]
   103  	return nil
   104  }
   105  
   106  // Encrypt the data, return error if too little data is given
   107  func (l *Lioness) Encrypt(data []byte) ([]byte, error) {
   108  	if !l.isConstructed() {
   109  		return nil, ErrConstructed
   110  	}
   111  	if !l.hasKeys() {
   112  		return nil, ErrNoKeys
   113  	}
   114  	if len(data) < l.keylen+1 {
   115  		return nil, ErrDataSize
   116  	}
   117  	L := data[:l.keylen]
   118  	R := data[l.keylen:]
   119  	R, err := l.rop(l.getIV(L), l.ropHMAC(l.k1, L), R)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	L = l.xor(L, l.ropHMAC(R, l.k2))
   124  	R, err = l.rop(l.getIV(L), l.ropHMAC(l.k3, L), R)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	L = l.xor(L, l.ropHMAC(R, l.k4))
   129  	return append(L, R...), nil
   130  }
   131  
   132  // Decrypt the data, return error if too little data is given
   133  func (l *Lioness) Decrypt(data []byte) ([]byte, error) {
   134  	if !l.isConstructed() {
   135  		return nil, ErrConstructed
   136  	}
   137  	if !l.hasKeys() {
   138  		return nil, ErrNoKeys
   139  	}
   140  	if len(data) < l.keylen+1 {
   141  		return nil, ErrDataSize
   142  	}
   143  	L := data[:l.keylen]
   144  	R := data[l.keylen:]
   145  	L = l.xor(L, l.ropHMAC(R, l.k4))
   146  	R, err := l.rop(l.getIV(L), l.ropHMAC(l.k3, L), R)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	L = l.xor(L, l.ropHMAC(R, l.k2))
   152  	R, err = l.rop(l.getIV(L), l.ropHMAC(l.k1, L), R)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	return append(L, R...), nil
   157  }
   158  
   159  // getIV returns the IV depending on mode
   160  func (l *Lioness) getIV(L []byte) []byte {
   161  	if l.mode == ModeZero {
   162  		if zeroIV == nil {
   163  			zeroIV = make([]byte, l.blocksize)
   164  		}
   165  		return zeroIV
   166  	}
   167  	return L[:l.blocksize]
   168  }
   169  
   170  // isConstructed verifies that the lioness has been constructed. To create more meaningful errors
   171  func (l *Lioness) isConstructed() bool {
   172  	if l == nil {
   173  		return false
   174  	}
   175  	if l.keylen < 1 {
   176  		return false
   177  	}
   178  	return true
   179  }
   180  
   181  // hasKeys verifies that the lioness has keys. To create more meaningful errors
   182  func (l *Lioness) hasKeys() bool {
   183  	if len(l.k1) != l.keylen || len(l.k2) != l.keylen || len(l.k3) != l.keylen || len(l.k4) != l.keylen {
   184  		return false
   185  	}
   186  	return true
   187  }
   188  
   189  // Rop implements the R operation, encrypting with stream cipher
   190  func (l *Lioness) rop(iv, key, plaintext []byte) (ciphertext []byte, err error) {
   191  	algo, err := l.blockcipher(key)
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  	ciphertext = make([]byte, len(plaintext))
   196  	stream := cipher.NewCTR(algo, iv)
   197  	stream.XORKeyStream(ciphertext, plaintext)
   198  	return ciphertext, nil
   199  }
   200  
   201  // RopHMAC generates the hmac of R for the L operation
   202  func (l *Lioness) ropHMAC(key, data []byte) []byte {
   203  	mac := hmac.New(l.hash, key)
   204  	mac.Write(data)
   205  	return mac.Sum(nil)
   206  }
   207  
   208  // Xor two byte slices a and b. Extra input bytes are dropped. Extra output bytes over keylen are dropped
   209  func (l *Lioness) xor(a, b []byte) []byte {
   210  	x, y := a, b
   211  	if len(a) < len(b) {
   212  		x, y = b, a
   213  	}
   214  	t := make([]byte, len(y))
   215  	for i := 0; i < len(y); i++ {
   216  		t[i] = x[i] ^ y[i]
   217  	}
   218  	if len(y) > l.keylen {
   219  		return t[:l.keylen]
   220  	}
   221  	return t
   222  }