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 }