Skip to content

Commit

Permalink
add pure-go partial implementation of xmlenc
Browse files Browse the repository at this point in the history
This commit adds a (partial, but hopefully sufficient) implementation of 
the XML encryption standard, which along with a pure-go implementation of
xmldsig will remove the requirement to depend on native libxmlsec.

This commit includes a simple go-fuzz implementation which has found a
few issues with the initial implementation.
  • Loading branch information
crewjam committed Apr 23, 2017
1 parent 1a3f691 commit b5912d9
Show file tree
Hide file tree
Showing 49 changed files with 2,345 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ addons:
go:
- 1.6
- 1.7
- 1.8
- tip
187 changes: 187 additions & 0 deletions xmlenc/cbc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
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
}
42 changes: 42 additions & 0 deletions xmlenc/corpus/bad-encrypt-content-aes128-cbc-kw-aes192.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<PurchaseOrder xmlns="urn:example:po">
<Items>
<Item Code="001-001-001" Quantity="1">
spade
</Item>
<Item Code="001-001-002" Quantity="1">
shovel
</Item>
</Items>
<ShippingAddress>
Dig PLC, 1 First Ave, Dublin 1, Ireland
</ShippingAddress>
<PaymentInfo>
<EncryptedData xmlns="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Content">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#kw-aes192" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>jeb</KeyName>
</KeyInfo>
<CipherData>
<CipherValue>
JbjZH7Mq564oMybpvCHWYM/5ER3eFsAV
</CipherValue>
</CipherData>
</EncryptedKey>
</KeyInfo>
<CipherData>
<CipherValue>
YDYTxR+smxZDSVoXXEp3n6HzTgWqV7ZlG6I1lmEv7zLGZBF/o7eqe5QGT6L3DPNW
geflA8vVJHxwliixWcvHCnNKQkx+Sw8YbIknCQyr4mqtXEmHhsie5XYTEyqgKLVP
YdNXf56wLUTMEmBqq7cto9OrYcBWkrDcQQvHmDkHuG+Nom4m+623GsB0FNts6VyN
sdGMwo4K0bEFReLL04l6It+cgLJ2q+LKdBoMQL59IAQmrwi0bkiqee2cLlDuGyQ1
KD9IQ1qtlJpvQujN4xNVWT00UjtWxmpSMID/Kue/AnXn7Cf8zw1ZZQitgh8uWOX2
uMy99F2YlxqIK1r+MeXHuZDNf75S8dFaKIKtHMf7ioA=
</CipherValue>
</CipherData>
</EncryptedData>
</PaymentInfo>
</PurchaseOrder>
83 changes: 83 additions & 0 deletions xmlenc/corpus/decryption-transform-except.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<PurchaseOrder xmlns="urn:example:po">
<Items>
<Item Code="001-001-001" Quantity="1">
spade
</Item>
<Item Code="001-001-002" Quantity="1">
shovel
</Item>
</Items>
<ShippingAddress>
Dig PLC, 1 First Ave, Dublin 1, Ireland
</ShippingAddress>
<PaymentInfo>
<EncryptedData xmlns="http://www.w3.org/2001/04/xmlenc#" Id="encrypt-data-0" Type="http://www.w3.org/2001/04/xmlenc#Content">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>jed</KeyName>
</KeyInfo>
<CipherData>
<CipherValue>
cX6lnfgmvWuxyiQgNhzAq1lYggW2M5GziFgNBQju3xcnDqlzf5LSjeyBnbL0Q7ws
8XhySFCrdwIi5mVxyfdFkVrTlzQQ0viaqTDgi9PQRgZMOImGGWij3wbmf9XseHHt
6q8V7LPjMFQAnsLDQgKf4gzzOnhtKf15GfTEpGvUnNn2dLDxw+hDcD1N54/bjSQs
uTiL7PgGQ5g4u4eaXRRLWeAGsIf5QgdQG3GLiOZIX1LJ5bREKgXeKrtJJI97xUX3
3vaF+tKRcSFBFIMjFrw271bFj4vvvQZfSS6xX+BKXHOUu8C4NH9Le8pA9o4NgCB8
tWA8W3iI5/BGEZve0Me9byvPHYjRXlbG+YqysVTmzfw=
</CipherValue>
</CipherData>
</EncryptedData>
<EncryptedData xmlns="http://www.w3.org/2001/04/xmlenc#" Id="encrypt-data-1">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
<CipherData>
<CipherValue>
x3aR5pJ5pepFFH5ENv61pZG4pVwNKaM+H9oyY4qG6d8l/C0J1iGv6c8dyLp0YQ2k
</CipherValue>
</CipherData>
</EncryptedData>
</PaymentInfo>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<Transform Algorithm="http://www.w3.org/2001/04/decrypt#">
<Except xmlns="http://www.w3.org/2001/04/decrypt#" URI="#encrypt-data-1" />
</Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<DigestValue>5Oe9qba6preOZG1NZAYK2/6pu9RCon9vRJ9hVLDpeng=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>
LuHrz9+WG7/c4Q81tFboNZg2cktWbZcRfp08XrmgKy1GDm9xSfTYCA==
</SignatureValue>
<KeyInfo>
<KeyValue>
<DSAKeyValue>
<P>
imup6lmki4rAmUstKb/xdBRMWNtQ+pDN97ZnLA9X3lKbkEHtYFyjQ3uActgVSJ75
iVRuKxz4Cb5RzVm25EaKmKq8rif1MtBIi6jjDJxmIdNaEKG9zVTf9giJx1N9I0t3
oh1fAVZDSrzKzJGQ2WvDfIfFHdJMtB3C0VKGmLZR7Xk=
</P>
<Q>
xDve3j7sEnh4rIzM5gK+5/gxxFU=
</Q>
<G>
NLugAf6IZJxo3BCOi5yrGEVwtlEzXcnndXhd0Tz38CnQKc4SEupm4PyP5TmLvK64
TDfOD7sno/W5oI1KZdimfW2c4r/6waNzZSvicMOWhLYY621Nn6njBc8VNwoxWpzC
XhKm70b8+D4YZMn/eU5DN8dvhTv/bNK21FfJqjp033U=
</G>
<Y>
W7dOmH/vWqocVCiqaxj6soxVXfR8XpMdY2Zv4Amjr3n81geyOLb6IZ+l7MUbdp85
29DQzuoVTthVpB9X4JKCprZIzifOTM1PFflTBzjx7egJwJWAIVdWyiIPjke6Va+w
uV2n4Rl/cgCvrXK5cTov5C/Bpaf6o+qrrDGFBLLZTF4=
</Y>
</DSAKeyValue>
</KeyValue>
</KeyInfo>
</Signature>
</PurchaseOrder>
73 changes: 73 additions & 0 deletions xmlenc/corpus/decryption-transform.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<PurchaseOrder xmlns="urn:example:po">
<Items>
<Item Code="001-001-001" Quantity="1">
spade
</Item>
<Item Code="001-001-002" Quantity="1">
shovel
</Item>
</Items>
<ShippingAddress>
Dig PLC, 1 First Ave, Dublin 1, Ireland
</ShippingAddress>
<PaymentInfo>
<EncryptedData xmlns="http://www.w3.org/2001/04/xmlenc#" Id="encrypt-data-0" Type="http://www.w3.org/2001/04/xmlenc#Content">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>jed</KeyName>
</KeyInfo>
<CipherData>
<CipherValue>
SE3HkQevYxzuN9LoMH3QIYHK0X7DBlobhiTbRucgKcTKt9DsUJIcd6JZV6lrw/4x
YICyq6YM73IWpibspxgz/0chhvWem9sYZvWTuTtZgHzeY0Uri6bpXqBEn1YT0K6B
chwfv1myfp91EmdPHU+shH6ZEyYkHJUMss58iIawIuVsIfpCO7xDKgfs/glnN3os
epY0KvAMZSnwUAf42fQ3TlahLTR+B52AmdodwaCwQlwQwrC7RH0FtNiiLQA9SA2t
//StKWcyHjswUCejfKLdjv6bK+WmBxmnNWtmI9DYkjJ6V5pYU1MVw+JG410O+gaa
fnNWxlWa+BGwcTaz+KNrP8bIqli8IoJJgxXIUqfb734=
</CipherValue>
</CipherData>
</EncryptedData>
</PaymentInfo>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<Transform Algorithm="http://www.w3.org/2001/04/decrypt#" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<DigestValue>wSvPYqTcpLfX2mKXibtsmm7FDu8N+/BObM0+bGaeXhk=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>
O0VYUdslJ8t2EURD0T/v2nNrFQMo42vzvfAhooZrDbkuLbCj6/Hxmw==
</SignatureValue>
<KeyInfo>
<KeyValue>
<DSAKeyValue>
<P>
imup6lmki4rAmUstKb/xdBRMWNtQ+pDN97ZnLA9X3lKbkEHtYFyjQ3uActgVSJ75
iVRuKxz4Cb5RzVm25EaKmKq8rif1MtBIi6jjDJxmIdNaEKG9zVTf9giJx1N9I0t3
oh1fAVZDSrzKzJGQ2WvDfIfFHdJMtB3C0VKGmLZR7Xk=
</P>
<Q>
xDve3j7sEnh4rIzM5gK+5/gxxFU=
</Q>
<G>
NLugAf6IZJxo3BCOi5yrGEVwtlEzXcnndXhd0Tz38CnQKc4SEupm4PyP5TmLvK64
TDfOD7sno/W5oI1KZdimfW2c4r/6waNzZSvicMOWhLYY621Nn6njBc8VNwoxWpzC
XhKm70b8+D4YZMn/eU5DN8dvhTv/bNK21FfJqjp033U=
</G>
<Y>
W7dOmH/vWqocVCiqaxj6soxVXfR8XpMdY2Zv4Amjr3n81geyOLb6IZ+l7MUbdp85
29DQzuoVTthVpB9X4JKCprZIzifOTM1PFflTBzjx7egJwJWAIVdWyiIPjke6Va+w
uV2n4Rl/cgCvrXK5cTov5C/Bpaf6o+qrrDGFBLLZTF4=
</Y>
</DSAKeyValue>
</KeyValue>
</KeyInfo>
</Signature>
</PurchaseOrder>
Loading

0 comments on commit b5912d9

Please sign in to comment.