package aesctsimport ()// Encrypt the message with the key and the initial vector.// Returns: next iv, ciphertext bytes, errorfunc (, , []byte) ([]byte, []byte, error) { := len() , := aes.NewCipher()if != nil {return []byte{}, []byte{}, fmt.Errorf("error creating cipher: %v", ) } := cipher.NewCBCEncrypter(, ) := make([]byte, len())copy(, )/*For consistency, ciphertext stealing is always used for the last two blocks of the data to be encrypted, as in [RC5]. If the data length is a multiple of the block size, this is equivalent to plain CBC mode with the last two ciphertext blocks swapped.*/ /*The initial vector carried out from one encryption for use in a subsequent encryption is the next-to-last block of the encryption output; this is the encrypted form of the last plaintext block.*/if <= aes.BlockSize { , _ = zeroPad(, aes.BlockSize) .CryptBlocks(, )return , , nil }if %aes.BlockSize == 0 { .CryptBlocks(, ) = [len()-aes.BlockSize:] , := swapLastTwoBlocks(, aes.BlockSize)return , , nil } , _ = zeroPad(, aes.BlockSize) , , , := tailBlocks(, aes.BlockSize)if != nil {return []byte{}, []byte{}, fmt.Errorf("error tailing blocks: %v", ) }var []byteif != nil {// Encrpt all but the lats 2 blocks and update the rolling iv .CryptBlocks(, ) = [len()-aes.BlockSize:] = cipher.NewCBCEncrypter(, ) = append(, ...) } .CryptBlocks(, ) = cipher.NewCBCEncrypter(, ) .CryptBlocks(, )// Cipher Text Stealing (CTS) - Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing // Swap the last two cipher blocks // Truncate the ciphertext to the length of the original plaintext = append(, ...) = append(, ...)return , [:], nil}// Decrypt the ciphertext with the key and the initial vector.func (, , []byte) ([]byte, error) {// Copy the cipher text as golang slices even when passed by value to this method can result in the backing arrays of the calling code value being updated. := make([]byte, len())copy(, )iflen() < aes.BlockSize {return []byte{}, fmt.Errorf("ciphertext is not large enough. It is less that one block size. Blocksize:%v; Ciphertext:%v", aes.BlockSize, len()) }// Configure the CBC , := aes.NewCipher()if != nil {returnnil, fmt.Errorf("error creating cipher: %v", ) }varcipher.BlockMode//If ciphertext is multiple of blocksize we just need to swap back the last two blocks and then do CBC //If the ciphertext is just one block we can't swap so we just decryptiflen()%aes.BlockSize == 0 {iflen() > aes.BlockSize { , _ = swapLastTwoBlocks(, aes.BlockSize) } = cipher.NewCBCDecrypter(, ) := make([]byte, len()) .CryptBlocks(, )return [:len()], nil }// Cipher Text Stealing (CTS) using CBC interface. Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing // Get ciphertext of the 2nd to last (penultimate) block (cpb), the last block (clb) and the rest (crb) , , , := tailBlocks(, aes.BlockSize) := make([]byte, len(), len())copy(, )var []byteif != nil {//If there is more than just the last and the penultimate block we decrypt it and the last bloc of this becomes the iv for later := make([]byte, len()) = cipher.NewCBCDecrypter(, ) = [len()-aes.BlockSize:] .CryptBlocks(, ) = append(, ...) }// We need to modify the cipher text // Decryt the 2nd to last (penultimate) block with a the original iv := make([]byte, aes.BlockSize) = cipher.NewCBCDecrypter(, ) .CryptBlocks(, )// number of byte needed to pad := aes.BlockSize - len()%aes.BlockSize//pad last block using the number of bytes needed from the tail of the plaintext 2nd to last (penultimate) block = append(, [len()-:]...)// Now decrypt the last block in the penultimate position (iv will be from the crb, if the is no crb it's zeros) // iv for the penultimate block decrypted in the last position becomes the modified last block := make([]byte, aes.BlockSize) = cipher.NewCBCDecrypter(, ) = .CryptBlocks(, ) = append(, ...)// Now decrypt the penultimate block in the last position (iv will be from the modified last block) = cipher.NewCBCDecrypter(, ) .CryptBlocks(, ) = append(, ...)// Truncate to the size of the original cipher textreturn [:len()], nil}func tailBlocks( []byte, int) ([]byte, []byte, []byte, error) {iflen() <= {return []byte{}, []byte{}, []byte{}, errors.New("bytes slice is not larger than one block so cannot tail") }// Get size of last blockvarintif := len() % aes.BlockSize; == 0 { = aes.BlockSize } else { = }// Get last block := [len()-:]// Get 2nd to last (penultimate) block := [len()-- : len()-]iflen() > 2* { := [:len()--]return , , , nil }returnnil, , , nil}func swapLastTwoBlocks( []byte, int) ([]byte, error) { , , , := tailBlocks(, )if != nil {returnnil, }var []byteif != nil { = append(, ...) } = append(, ...) = append(, ...)return , nil}// zeroPad pads bytes with zeros to nearest multiple of message size m.func zeroPad( []byte, int) ([]byte, error) {if <= 0 {returnnil, errors.New("invalid message block size when padding") }if == nil || len() == 0 {returnnil, errors.New("data not valid to pad: Zero size") }if := len() % ; != 0 { := - := make([]byte, ) = append(, ...) }return , nil}
The pages are generated with Goldsv0.6.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds.