/* * xmlenc.cpp - XML Encryption * Copyright (C) 2003 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include"xmlenc.h" #include"../util/bytestream.h" #include"../util/base64.h" #include"keyops.h" static QByteArray nodeToArray(const QDomNode &e) { QString out; QTextStream ts(&out, IO_WriteOnly); e.save(ts, 1); QCString xmlToEnc = out.utf8(); int len = xmlToEnc.length(); QByteArray b(len); memcpy(b.data(), xmlToEnc.data(), len); return b; } static QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found) { if(found) *found = false; for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement i = n.toElement(); if(i.isNull()) continue; if(i.tagName() == name) { if(found) *found = true; return i; } } QDomElement tmp; return tmp; } using namespace XmlEnc; //---------------------------------------------------------------------------- // KeyInfo //---------------------------------------------------------------------------- KeyInfo::KeyInfo() { } KeyInfo::~KeyInfo() { } bool KeyInfo::isEmpty() const { if(v_name.isEmpty() && v_value.isEmpty() && v_rmethods.isEmpty() && v_key.isNull()) return true; return false; } QString KeyInfo::name() const { return v_name; } QByteArray KeyInfo::value () const { return v_value; } QStringList KeyInfo::retrievalMethods() const { return v_rmethods; } QDomElement KeyInfo::encryptedKey() const { return v_key; } void KeyInfo::setName(const QString &s) { v_name = s; } void KeyInfo::setValue(const QByteArray &d) { v_value = d; } void KeyInfo::setRetrievalMethods(const QStringList &s) { v_rmethods = s; } void KeyInfo::attachEncryptedKey(const QDomElement &e) { v_key = e; } QDomElement KeyInfo::toXml(QDomDocument *doc) const { QDomElement e = doc->createElement("ds:KeyInfo"); e.setAttribute("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#"); if(!v_value.isEmpty()) { QDomElement v = doc->createElement("ds:KeyValue"); v.appendChild(doc->createTextNode(Base64::arrayToString(v_value))); e.appendChild(v); } if(!v_name.isEmpty()) { QDomElement n = doc->createElement("ds:KeyName"); n.appendChild(doc->createTextNode(v_name)); e.appendChild(n); } for(QStringList::ConstIterator it = v_rmethods.begin(); it != v_rmethods.end(); ++it) { QDomElement r = doc->createElement("ds:RetrievalMethod"); r.setAttribute("Type", "http://www.w3.org/2001/04/xmlenc#EncryptedKey"); r.setAttribute("URI", *it); e.appendChild(r); } if(!v_key.isNull()) e.appendChild(v_key); return e; } bool KeyInfo::fromXml(const QDomElement &e) { if(e.tagName() != "ds:KeyInfo" || e.attribute("xmlns:ds") != "http://www.w3.org/2000/09/xmldsig#") return false; QByteArray val; QString n; QStringList rml; QDomElement ek; bool found; QDomElement i; i = findSubTag(e, "ds:KeyValue", &found); if(found) val = Base64::stringToArray(i.text()); i = findSubTag(e, "ds:KeyName", &found); if(found) n = i.text(); QDomNodeList l = e.elementsByTagName("ds:RetrievalMethod"); for(int n = 0; n < (int)l.count(); ++n) { QDomElement r = l.item(n).toElement(); if(r.attribute("Type") == "http://www.w3.org/2001/04/xmlenc#EncryptedKey") rml += r.attribute("URI"); } i = findSubTag(e, "EncryptedKey", &found); if(found) ek = i; // all good v_value = val; v_name = n; v_rmethods = rml; v_key = ek; return true; } //---------------------------------------------------------------------------- // EncryptionProperty //---------------------------------------------------------------------------- EncryptionProperty::EncryptionProperty(const QString &target, const QString &id) { v_target = target; v_id = id; } QString EncryptionProperty::target() const { return v_target; } QString EncryptionProperty::id() const { return v_id; } QString EncryptionProperty::property(const QString &var) const { QStringList::ConstIterator it = vars.begin(); QStringList::ConstIterator it2 = vals.begin(); while(it != vars.end()) { if((*it) == var) return *it2; ++it; ++it2; } return ""; } void EncryptionProperty::setTarget(const QString &target) { v_target = target; } void EncryptionProperty::setId(const QString &id) { v_id = id; } void EncryptionProperty::setProperty(const QString &var, const QString &val) { if(var == "Target" || var == "Id") return; // see if we have it already QStringList::Iterator it = vars.begin(); QStringList::Iterator it2 = vals.begin(); while(it != vars.end()) { if((*it) == var) { *it2 = val; return; } ++it; ++it2; } vars += var; vals += val; } QDomElement EncryptionProperty::toXml(QDomDocument *doc) const { QDomElement e = doc->createElement("EncryptionProperty"); if(!v_target.isEmpty()) e.setAttribute("Target", v_target); if(!v_id.isEmpty()) e.setAttribute("Id", v_id); QStringList::ConstIterator it = vars.begin(); QStringList::ConstIterator it2 = vals.begin(); while(it != vars.end()) { e.setAttribute(*it, *it2); ++it; ++it2; } return e; } bool EncryptionProperty::fromXml(QDomElement &e) { if(e.tagName() != "EncryptionProperty") return false; v_target = e.attribute("Target"); v_id = e.attribute("Id"); vars.clear(); vals.clear(); QDomNamedNodeMap map = e.attributes(); for(int n = 0; n < (int)map.count(); ++n) { QDomAttr a = map.item(n).toAttr(); QString n = a.name(); if(n == "Target" || n == "Id") continue; vars += n; vals += a.value(); } return true; } //---------------------------------------------------------------------------- // EncryptionProperties //---------------------------------------------------------------------------- EncryptionProperties::EncryptionProperties(const QString &s) :QValueList() { v_id = s; } QString EncryptionProperties::id() const { return v_id; } void EncryptionProperties::setId(const QString &s) { v_id = s; } QDomElement EncryptionProperties::toXml(QDomDocument *doc) const { QDomElement e = doc->createElement("EncryptionProperties"); if(!v_id.isEmpty()) e.setAttribute("Id", v_id); for(QValueList::ConstIterator it = begin(); it != end(); ++it) { const EncryptionProperty &p = *it; e.appendChild(p.toXml(doc)); } return e; } bool EncryptionProperties::fromXml(QDomElement &e) { if(e.tagName() != "EncryptionProperties") return false; clear(); v_id = e.attribute("Id"); QDomNodeList l = e.elementsByTagName("EncryptionProperty"); for(int n = 0; n < (int)l.count(); ++n) { QDomElement r = l.item(n).toElement(); EncryptionProperty p; if(!p.fromXml(r)) return false; append(p); } return true; } //---------------------------------------------------------------------------- // Encrypted //---------------------------------------------------------------------------- Encrypted::Encrypted() { baseNS = "http://www.w3.org/2001/04/xmlenc#"; clear(); } Encrypted::~Encrypted() { clear(); } void Encrypted::clear() { v_method = None; v_id = ""; v_dataType = Arbitrary; v_type = Data; v_mimeType = ""; v_keyInfo = KeyInfo(); v_cval = ""; v_cref = Reference(); v_carrykeyname = ""; } Method Encrypted::cipherTypeToMethod(Cipher::Type t) const { if(t == Cipher::TripleDES) return TripleDES; else if(t == Cipher::AES_128) return AES_128; else if(t == Cipher::AES_256) return AES_256; else return None; } void Encrypted::setDataReference(const Reference &cref, Method m) { v_method = m; v_cref = cref; } bool Encrypted::encryptData(const QByteArray &data, const Cipher::Key &key) { QByteArray iv = Cipher::generateIV(key.type()); if(!sym_encrypt(data, key, iv, &v_cval)) return false; v_dataType = Arbitrary; v_method = cipherTypeToMethod(key.type()); return true; } bool Encrypted::encryptElement(const QDomElement &data, const Cipher::Key &key) { QByteArray iv = Cipher::generateIV(key.type()); if(!sym_encrypt(nodeToArray(data), key, iv, &v_cval)) return false; v_type = Data; v_dataType = Element; v_method = cipherTypeToMethod(key.type()); return true; } bool Encrypted::encryptContent(const QDomElement &data, const Cipher::Key &key) { // convert children to raw data QByteArray a; for(QDomNode n = data.firstChild(); !n.isNull(); n = n.nextSibling()) ByteStream::appendArray(&a, nodeToArray(n)); QByteArray iv = Cipher::generateIV(key.type()); if(!sym_encrypt(a, key, iv, &v_cval)) return false; v_type = Data; v_dataType = Content; v_method = cipherTypeToMethod(key.type()); return true; } bool Encrypted::encryptKey(const Cipher::Key &data, const Cipher::Key &key) { if(!sym_keywrap(data.data(), key, &v_cval)) return false; v_type = Key; v_dataType = Arbitrary; v_method = cipherTypeToMethod(key.type()); return true; } bool Encrypted::encryptKey(const Cipher::Key &data, const RSAKey &key) { bool useOAEP; if(data.type() == Cipher::TripleDES) useOAEP = false; else useOAEP = true; bool ok; QByteArray result; if(useOAEP) result = encryptRSA2(data.data(), key, &ok); else result = encryptRSA(data.data(), key, &ok); if(!ok) return false; v_type = Key; v_dataType = Arbitrary; v_method = useOAEP ? RSA_OAEP : RSA_1_5; v_cval = Base64::arrayToString(result); return true; } QByteArray Encrypted::decryptData(const Cipher::Key &key) const { QByteArray result; if(!sym_decrypt(v_cval, key, &result)) return QByteArray(); return result; } QDomElement Encrypted::decryptElement(QDomDocument *doc, const Cipher::Key &key) const { QByteArray result; if(!sym_decrypt(v_cval, key, &result)) return QDomElement(); QDomDocument d; if(!d.setContent(result)) return QDomElement(); return doc->importNode(d.documentElement(), true).toElement(); } QDomNodeList Encrypted::decryptContent(QDomDocument *doc, const Cipher::Key &key) const { QByteArray result; if(!sym_decrypt(v_cval, key, &result)) return QDomNodeList(); // nul-terminate result.resize(result.size()+1); result[result.size()-1] = 0; QCString cs = ""; cs += (char *)result.data(); cs += ""; QDomDocument d; if(!d.setContent(cs)) return QDomNodeList(); QDomElement e = d.documentElement().firstChild().toElement(); if(e.isNull() || e.tagName() != "dummy") return QDomNodeList(); return doc->importNode(e, true).childNodes(); } QByteArray Encrypted::decryptKey(const Cipher::Key &key) const { QByteArray result; if(!sym_keyunwrap(v_cval, key, &result)) return QByteArray(); return result; } QByteArray Encrypted::decryptKey(const RSAKey &key) const { QByteArray data = Base64::stringToArray(v_cval); QByteArray result; bool ok; if(v_method == RSA_OAEP) result = decryptRSA2(data, key, &ok); else result = decryptRSA(data, key, &ok); if(!ok) return QByteArray(); return result; } QDomElement Encrypted::toXml(QDomDocument *doc) const { QString baseNS = "http://www.w3.org/2001/04/xmlenc#"; QDomElement enc; // base xml if(v_type == Data) enc = doc->createElement("EncryptedData"); else enc = doc->createElement("EncryptedKey"); enc.setAttribute("xmlns", baseNS); if(!v_id.isEmpty()) enc.setAttribute("Id", v_id); if(v_type == Data) enc.setAttribute("Type", baseNS + "Element"); // method QDomElement meth = doc->createElement("EncryptionMethod"); meth.setAttribute("Algorithm", methodToAlgorithm(v_method, v_type)); if(v_method == RSA_OAEP) { // add digest method QDomElement dm = doc->createElement("ds:DigestMethod"); dm.setAttribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1"); meth.appendChild(dm); } enc.appendChild(meth); // keyinfo if(!v_keyInfo.isEmpty()) enc.appendChild(v_keyInfo.toXml(doc)); // cipherdata QDomElement cd = doc->createElement("CipherData"); if(!v_cref.uri().isEmpty()) { QDomElement cr = doc->createElement("CipherReference"); cr.setAttribute("URI", v_cref.uri()); if(!v_cref.transforms().isNull()) cr.appendChild(v_cref.transforms()); cd.appendChild(cr); } else { QDomElement cv = doc->createElement("CipherValue"); cv.appendChild(doc->createTextNode(v_cval)); cd.appendChild(cv); } enc.appendChild(cd); // encryption properties if(!v_props.isEmpty()) enc.appendChild(v_props.toXml(doc)); // reference list if(!v_reflist.isEmpty()) { QDomElement e = doc->createElement("ReferenceList"); for(QValueList::ConstIterator it = v_reflist.begin(); it != v_reflist.end(); ++it) { const Reference &r = *it; QDomElement df = doc->createElement("DataReference"); df.setAttribute("URI", r.uri()); if(!r.transforms().isNull()) df.appendChild(r.transforms()); e.appendChild(df); } enc.appendChild(e); } // carry key name if(!v_carrykeyname.isEmpty()) { QDomElement e = doc->createElement("CarriedKeyName"); e.appendChild(doc->createTextNode(v_carrykeyname)); enc.appendChild(e); } return enc; } bool Encrypted::fromXml(const QDomElement &e) { QString baseNS = "http://www.w3.org/2001/04/xmlenc#"; if(e.attribute("xmlns") != baseNS) return false; DataType dt = Arbitrary; if(e.hasAttribute("Type")) { QString str = e.attribute("Type"); int n = str.find('#'); if(n == -1) return false; ++n; if(str.mid(0, n) != baseNS) return false; str = str.mid(n); if(str == "Element") dt = Element; else if(str == "Content") dt = Content; } Type t; if(e.tagName() == "EncryptedData") t = Data; else if(e.tagName() == "EncryptedKey") t = Key; else return false; // method Method m = None; bool found; QDomElement i = findSubTag(e, "EncryptionMethod", &found); if(!found) return false; QString str = i.attribute("Algorithm"); int n = str.find('#'); if(n == -1) return false; ++n; if(str.mid(0, n) != baseNS) return false; str = str.mid(n); m = algorithmToMethod(str); if(m == None) return false; // keyinfo KeyInfo ki; i = findSubTag(e, "ds:KeyInfo", &found); if(found) { if(!ki.fromXml(i)) return false; } // cipherdata QString cval; Reference cref; i = findSubTag(e, "CipherData", &found); if(!found) return false; i = findSubTag(i, "CipherValue", &found); if(found) cval = i.text(); else { i = findSubTag(i, "CipherReference", &found); if(!found) return false; cref.setURI(i.attribute("URI")); QDomElement tf = i.firstChild().toElement(); if(!tf.isNull()) cref.setTransforms(tf); } // encryption properties EncryptionProperties props; i = findSubTag(e, "EncryptionProperties", &found); if(found) { if(!props.fromXml(i)) return false; } // reference list ReferenceList rl; i = findSubTag(e, "ReferenceList", &found); if(found) { QDomNodeList l = e.elementsByTagName("DataReference"); for(int n = 0; n < (int)l.count(); ++n) { QDomElement dr = l.item(n).toElement(); Reference r; r.setURI(dr.attribute("URI")); QDomElement tf = i.firstChild().toElement(); if(!tf.isNull()) r.setTransforms(tf); rl += r; } } // carry key name QString carrykey; i = findSubTag(e, "CarriedKeyName", &found); if(found) carrykey = i.text(); // looks good, let's take it clear(); v_type = t; v_dataType = dt; v_method = m; v_cval = cval; v_cref = cref; v_keyInfo = ki; v_props = props; v_reflist = rl; v_carrykeyname = carrykey; return true; } QString Encrypted::methodToAlgorithm(Method m, Type t) const { QString s; if(m == TripleDES) s = (t == Key ? "kw-tripledes": "tripledes-cbc"); else if(m == AES_128) s = (t == Key ? "kw-aes128": "aes128-cbc"); else if(m == AES_256) s = (t == Key ? "kw-aes256": "aes256-cbc"); else if(m == RSA_1_5) s = "rsa-1_5"; else if(m == RSA_OAEP) s = "rsa-oaep-mgf1p"; else return ""; return (baseNS + s); } Method Encrypted::algorithmToMethod(const QString &s) const { Method m; if(s == "tripledes-cbc" || s == "kw-tripledes") m = TripleDES; else if(s == "aes128-cbc" || s == "kw-aes128") m = AES_128; else if(s == "aes256-cbc" || s == "kw-aes256") m = AES_256; else if(s == "rsa-1_5") m = RSA_1_5; else if(s == "rsa-oaep-mgf1p") m = RSA_OAEP; else return None; return m; }