| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- package xmlenc
- import (
- "crypto/aes"
- "crypto/cipher"
- "crypto/des" // nolint: gas
- "encoding/base64"
- "errors"
- "fmt"
- "github.com/beevik/etree"
- )
- // CBC implements Decrypter and Encrypter for block ciphers in CBC mode
- type CBC struct {
- keySize int
- algorithm string
- cipher func([]byte) (cipher.Block, error)
- }
- // KeySize returns the length of the key required.
- func (e CBC) KeySize() int {
- return e.keySize
- }
- // Algorithm returns the name of the algorithm, as will be found
- // in an xenc:EncryptionMethod element.
- func (e CBC) Algorithm() string {
- return e.algorithm
- }
- // Encrypt encrypts plaintext with key, which should be a []byte of length KeySize().
- // It returns an xenc:EncryptedData element.
- func (e CBC) Encrypt(key interface{}, plaintext []byte) (*etree.Element, error) {
- keyBuf, ok := key.([]byte)
- if !ok {
- return nil, ErrIncorrectKeyType("[]byte")
- }
- if len(keyBuf) != e.keySize {
- return nil, ErrIncorrectKeyLength(e.keySize)
- }
- block, err := e.cipher(keyBuf)
- if err != nil {
- return nil, err
- }
- encryptedDataEl := etree.NewElement("xenc:EncryptedData")
- encryptedDataEl.CreateAttr("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#")
- {
- randBuf := make([]byte, 16)
- if _, err := RandReader.Read(randBuf); err != nil {
- return nil, err
- }
- encryptedDataEl.CreateAttr("Id", fmt.Sprintf("_%x", randBuf))
- }
- em := encryptedDataEl.CreateElement("xenc:EncryptionMethod")
- em.CreateAttr("Algorithm", e.algorithm)
- em.CreateAttr("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#")
- plaintext = appendPadding(plaintext, block.BlockSize())
- iv := make([]byte, block.BlockSize())
- if _, err := RandReader.Read(iv); err != nil {
- return nil, err
- }
- mode := cipher.NewCBCEncrypter(block, iv)
- ciphertext := make([]byte, len(plaintext))
- mode.CryptBlocks(ciphertext, plaintext)
- ciphertext = append(iv, ciphertext...)
- cd := encryptedDataEl.CreateElement("xenc:CipherData")
- cd.CreateAttr("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#")
- cd.CreateElement("xenc:CipherValue").SetText(base64.StdEncoding.EncodeToString(ciphertext))
- return encryptedDataEl, nil
- }
- // Decrypt decrypts an encrypted element with key. If the ciphertext contains an
- // EncryptedKey element, then the type of `key` is determined by the registered
- // Decryptor for the EncryptedKey element. Otherwise, `key` must be a []byte of
- // length KeySize().
- func (e CBC) Decrypt(key interface{}, ciphertextEl *etree.Element) ([]byte, error) {
- // If the key is encrypted, decrypt it.
- if encryptedKeyEl := ciphertextEl.FindElement("./KeyInfo/EncryptedKey"); encryptedKeyEl != nil {
- var err error
- key, err = Decrypt(key, encryptedKeyEl)
- if err != nil {
- return nil, err
- }
- }
- keyBuf, ok := key.([]byte)
- if !ok {
- return nil, ErrIncorrectKeyType("[]byte")
- }
- if len(keyBuf) != e.KeySize() {
- return nil, ErrIncorrectKeyLength(e.KeySize())
- }
- block, err := e.cipher(keyBuf)
- if err != nil {
- return nil, err
- }
- ciphertext, err := getCiphertext(ciphertextEl)
- if err != nil {
- return nil, err
- }
- if len(ciphertext) < block.BlockSize() {
- return nil, errors.New("ciphertext too short")
- }
- iv := ciphertext[:aes.BlockSize]
- ciphertext = ciphertext[aes.BlockSize:]
- mode := cipher.NewCBCDecrypter(block, iv)
- plaintext := make([]byte, len(ciphertext))
- mode.CryptBlocks(plaintext, ciphertext) // decrypt in place
- plaintext, err = stripPadding(plaintext)
- if err != nil {
- return nil, err
- }
- return plaintext, nil
- }
- var (
- // AES128CBC implements AES128-CBC symetric key mode for encryption and decryption
- AES128CBC BlockCipher = CBC{
- keySize: 16,
- algorithm: "http://www.w3.org/2001/04/xmlenc#aes128-cbc",
- cipher: aes.NewCipher,
- }
- // AES192CBC implements AES192-CBC symetric key mode for encryption and decryption
- AES192CBC BlockCipher = CBC{
- keySize: 24,
- algorithm: "http://www.w3.org/2001/04/xmlenc#aes192-cbc",
- cipher: aes.NewCipher,
- }
- // AES256CBC implements AES256-CBC symetric key mode for encryption and decryption
- AES256CBC BlockCipher = CBC{
- keySize: 32,
- algorithm: "http://www.w3.org/2001/04/xmlenc#aes256-cbc",
- cipher: aes.NewCipher,
- }
- // TripleDES implements 3DES in CBC mode for encryption and decryption
- TripleDES BlockCipher = CBC{
- keySize: 8,
- algorithm: "http://www.w3.org/2001/04/xmlenc#tripledes-cbc",
- cipher: des.NewCipher,
- }
- )
- func init() {
- RegisterDecrypter(AES128CBC)
- RegisterDecrypter(AES192CBC)
- RegisterDecrypter(AES256CBC)
- RegisterDecrypter(TripleDES)
- }
- func appendPadding(buf []byte, blockSize int) []byte {
- paddingBytes := blockSize - (len(buf) % blockSize)
- padding := make([]byte, paddingBytes)
- padding[len(padding)-1] = byte(paddingBytes)
- return append(buf, padding...)
- }
- func stripPadding(buf []byte) ([]byte, error) {
- if len(buf) < 1 {
- return nil, errors.New("buffer is too short for padding")
- }
- paddingBytes := int(buf[len(buf)-1])
- if paddingBytes > len(buf)-1 {
- return nil, errors.New("buffer is too short for padding")
- }
- if paddingBytes < 1 {
- return nil, errors.New("padding must be at least one byte")
- }
- return buf[:len(buf)-paddingBytes], nil
- }
|