[cpp-xmltooling] branch master updated: SSPCPP-775 - Client-side session storage

Scott Cantor cantor.2 at osu.edu
Wed Mar 7 13:59:56 EST 2018


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch master
in repository cpp-xmltooling.

View the commit online:
http://git.shibboleth.net/view/?p=cpp-xmltooling.git;a=commit;h=d05ef36e2e4906109a753567a7eaf59c5d77b437

The following commit(s) were added to refs/heads/master by this push:
       new  d05ef36   SSPCPP-775 - Client-side session storage
d05ef36 is described below

commit d05ef36e2e4906109a753567a7eaf59c5d77b437
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Mar 7 13:58:43 2018 -0500

    SSPCPP-775 - Client-side session storage
    
    https://issues.shibboleth.net/jira/browse/SSPCPP-775
    
    Encryption added to DataSealer.
---
 xmltooling/security/DataSealer.h        |   4 +-
 xmltooling/security/impl/DataSealer.cpp | 179 ++++++++++++++++++++++++++------
 xmltoolingtest/DataSealerTest.h         |   6 +-
 3 files changed, 157 insertions(+), 32 deletions(-)

diff --git a/xmltooling/security/DataSealer.h b/xmltooling/security/DataSealer.h
index 47eccf7..775b566 100644
--- a/xmltooling/security/DataSealer.h
+++ b/xmltooling/security/DataSealer.h
@@ -32,7 +32,7 @@
 #include <ctime>
 #include <string>
 
-#include <xsec/enc/XSECCryptoSymmetricKey.hpp>
+class XSECCryptoSymmetricKey;
 
 namespace xmltooling {
 
@@ -120,7 +120,7 @@ namespace xmltooling {
         virtual std::string unwrap(const char* s) const;
 
     private:
-        const DataSealerKeyStrategy* m_strategy;
+		const DataSealerKeyStrategy* m_strategy;
     };
 
 };
diff --git a/xmltooling/security/impl/DataSealer.cpp b/xmltooling/security/impl/DataSealer.cpp
index 16ed0c1..2df326a 100644
--- a/xmltooling/security/impl/DataSealer.cpp
+++ b/xmltooling/security/impl/DataSealer.cpp
@@ -33,9 +33,19 @@
 #include <sstream>
 #include <xercesc/util/Base64.hpp>
 #include <xercesc/util/XMLDateTime.hpp>
+#include <xsec/framework/XSECAlgorithmHandler.hpp>
+#include <xsec/framework/XSECAlgorithmMapper.hpp>
+#include <xsec/framework/XSECEnv.hpp>
+#include <xsec/framework/XSECException.hpp>
+#include <xsec/transformers/TXFMChain.hpp>
+#include <xsec/transformers/TXFMBase64.hpp>
+#include <xsec/transformers/TXFMSB.hpp>
+#include <xsec/xenc/XENCEncryptionMethod.hpp>
 
 using namespace xmltooling;
 using xercesc::Base64;
+using xercesc::DOMDocument;
+using xercesc::Janitor;
 using xercesc::XMLDateTime;
 using namespace std;
 
@@ -69,10 +79,34 @@ DataSealer::~DataSealer()
 {
 }
 
-// TODO: add encryption ;-)
-
 string DataSealer::wrap(const char* s, time_t exp) const
 {
+    // Get default key to use.
+    pair<string,const XSECCryptoSymmetricKey*> defaultKey = m_strategy->getDefaultKey();
+
+	const XMLCh* algorithm = nullptr;
+	switch (defaultKey.second->getSymmetricKeyType()) {
+		case XSECCryptoSymmetricKey::SymmetricKeyType::KEY_AES_128:
+			algorithm = DSIGConstants::s_unicodeStrURIAES128_GCM;
+			break;
+
+		case XSECCryptoSymmetricKey::SymmetricKeyType::KEY_AES_192:
+			algorithm = DSIGConstants::s_unicodeStrURIAES192_GCM;
+			break;
+
+		case XSECCryptoSymmetricKey::SymmetricKeyType::KEY_AES_256:
+			algorithm = DSIGConstants::s_unicodeStrURIAES256_GCM;
+			break;
+
+		default:
+			throw XMLSecurityException("Unknown key type.");
+	}
+
+	const XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(algorithm);
+	if (!handler) {
+		throw XMLSecurityException("Unable to obtain algorithm handler.");
+	}
+
 #ifndef HAVE_GMTIME_R
     struct tm* ptime = gmtime(&exp);
 #else
@@ -82,47 +116,134 @@ string DataSealer::wrap(const char* s, time_t exp) const
     char timebuf[32];
     strftime(timebuf, 32, "%Y-%m-%dT%H:%M:%SZ", ptime);
 
-    string towrap(timebuf);
-    towrap += s;
+	// The data format of the plaintext packet is:
+	//    PLAINTEXT := KEYLABEL + ':' + ISOEXPTIME + DATA
+	// The plaintext is zipped, encrypted, base64'd, and prefixed with the
+	// KEYLABEL and a colon on the outside, as a key hint.
 
+    // Construct the plaintext packet.
+	string sb(defaultKey.first);
+	sb = sb + ':' + timebuf + s;
+
+    // zip the plaintext packet
     unsigned int len;
-    char* deflated = XMLHelper::deflate(const_cast<char*>(towrap.c_str()), towrap.length(), &len);
-    if (!deflated)
+    char* deflated = XMLHelper::deflate(const_cast<char*>(sb.c_str()), sb.length(), &len);
+    if (!deflated || !len)
         throw IOException("Failed to deflate data.");
 
-    XMLSize_t xlen;
-    XMLByte* encoded = Base64::encode(reinterpret_cast<XMLByte*>(deflated), len, &xlen);
-    delete[] deflated;
-    if (!encoded)
-        throw IOException("Base64 encoding of deflated data failed.");
+    // Finally we encrypt the data. We have to hack this a bit to reuse the xmlsec routines.
 
-    string wrapped;
-    for (const XMLByte* xb = encoded; *xb; ++xb) {
-        if (!isspace(*xb))
-            wrapped += *xb;
-    }
-    XMLString::release((char**)&encoded);
+	DOMDocument* dummydoc = XMLToolingConfig::getConfig().getParser().newDocument();
+	Janitor<DOMDocument> docjan(dummydoc);
+	auto_ptr<XSECEnv> env(new XSECEnv(dummydoc));
 
-    return wrapped;
+    safeBuffer plaintext;
+    plaintext.sbMemcpyIn(deflated, len);
+    delete[] deflated;
+    TXFMSB* sbt = new TXFMSB(dummydoc);
+    sbt->setInput(plaintext, len);
+    TXFMChain tx(sbt);
+
+	safeBuffer ciphertext;
+	try {
+		auto_ptr<XENCEncryptionMethod> method(XENCEncryptionMethod::create(env.get(), algorithm));
+		if (!handler->encryptToSafeBuffer(&tx, method.get(), defaultKey.second, dummydoc, ciphertext)) {
+			throw XMLSecurityException("Data encryption failed.");
+		}
+	}
+	catch (XSECException& ex) {
+		auto_ptr_char msg(ex.getMsg());
+		throw XMLSecurityException(msg.get());
+	}
+
+	defaultKey.first.append(":");
+	defaultKey.first.append(ciphertext.rawCharBuffer(), ciphertext.sbRawBufferSize());
+	return defaultKey.first;
 }
 
 string DataSealer::unwrap(const char* s) const
 {
-    XMLSize_t x;
-    XMLByte* decoded = Base64::decode(reinterpret_cast<const XMLByte*>(s), &x);
-    if (!decoded)
-        throw IOException("Unable to decode base64 data.");
+	// The data format of the plaintext packet is:
+	//    PLAINTEXT := KEYLABEL + ':' + ISOEXPTIME + DATA
+	// The plaintext is zipped, encrypted, base64'd, and prefixed with the
+	// KEYLABEL and a colon on the outside, as a key hint.
+
+	// First extract the key label up to the first colon.
+	pair<string, const XSECCryptoSymmetricKey*> requiredKey = make_pair(string(), nullptr);
+	const char* delim = strchr(s ? s : "", ':');
+	if (delim && delim > s) {
+		requiredKey.first.append(s, delim - s);
+		requiredKey.second = m_strategy->getKey(requiredKey.first.c_str());
+	}
+	if (!requiredKey.second)
+		throw IOException("Required decryption key not available.");
+
+	const XMLCh* algorithm = nullptr;
+	switch (requiredKey.second->getSymmetricKeyType()) {
+	case XSECCryptoSymmetricKey::SymmetricKeyType::KEY_AES_128:
+		algorithm = DSIGConstants::s_unicodeStrURIAES128_GCM;
+		break;
+
+	case XSECCryptoSymmetricKey::SymmetricKeyType::KEY_AES_192:
+		algorithm = DSIGConstants::s_unicodeStrURIAES192_GCM;
+		break;
+
+	case XSECCryptoSymmetricKey::SymmetricKeyType::KEY_AES_256:
+		algorithm = DSIGConstants::s_unicodeStrURIAES256_GCM;
+		break;
+
+	default:
+		throw XMLSecurityException("Unknown key type.");
+	}
+
+	const XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(algorithm);
+	if (!handler) {
+		throw XMLSecurityException("Unable to obtain algorithm handler.");
+	}
+
+	DOMDocument* dummydoc = XMLToolingConfig::getConfig().getParser().newDocument();
+	Janitor<DOMDocument> docjan(dummydoc);
+	auto_ptr<XSECEnv> env(new XSECEnv(dummydoc));
+
+	safeBuffer ciphertext;
+	ciphertext.sbStrcpyIn(++delim);
+	TXFMSB* sbt = new TXFMSB(dummydoc);
+	sbt->setInput(ciphertext, ciphertext.sbStrlen());
+	TXFMChain tx(sbt);
+	TXFMBase64* b64 = new TXFMBase64(dummydoc, true); // decodes
+	tx.appendTxfm(b64);
+
+	unsigned int len = 0;
+	safeBuffer plaintext;
+	try {
+		auto_ptr<XENCEncryptionMethod> method(XENCEncryptionMethod::create(env.get(), algorithm));
+		len = handler->decryptToSafeBuffer(&tx, method.get(), requiredKey.second, dummydoc, plaintext);
+	}
+	catch (XSECException& ex) {
+		auto_ptr_char msg(ex.getMsg());
+		throw XMLSecurityException(msg.get());
+	}
+
+	if (len == 0)
+		throw XMLSecurityException("No decrypted data available.");
 
     // Now we have to inflate it.
-    stringstream in;
-    if (XMLHelper::inflate(reinterpret_cast<char*>(decoded), x, in) == 0) {
-        XMLString::release((char**)&decoded);
+    stringstream out;
+    if (XMLHelper::inflate(const_cast<char*>(plaintext.rawCharBuffer()), len, out) == 0) {
         throw IOException("Unable to inflate wrapped data.");
     }
-    XMLString::release((char**)&decoded);
 
-    string decrypted = in.str();
-    string dstr = decrypted.substr(0, 20);
+    string decrypted = out.str();
+
+	// Pull off the key label to verify it.
+	size_t i = decrypted.find(':');
+	if (i == string::npos)
+		throw IOException("Unable to verify key used to decrypt data.");
+	string keyLabel = decrypted.substr(0, i);
+	if (keyLabel != requiredKey.first)
+		throw IOException("Embedded key label does not match key used to decrypt data.");
+
+    string dstr = decrypted.substr(++i, 20);
     auto_ptr_XMLCh expstr(dstr.c_str());
     XMLDateTime exp(expstr.get());
     exp.parseDateTime();
@@ -130,5 +251,5 @@ string DataSealer::unwrap(const char* s) const
         throw IOException("Decrypted data has expired.");
     }
 
-    return decrypted.substr(20);
+    return decrypted.substr(i + 20);
 }
diff --git a/xmltoolingtest/DataSealerTest.h b/xmltoolingtest/DataSealerTest.h
index 1672698..1ee35b0 100644
--- a/xmltoolingtest/DataSealerTest.h
+++ b/xmltoolingtest/DataSealerTest.h
@@ -68,5 +68,9 @@ public:
 
         wrapped = sealer->wrap(data.c_str(), time(nullptr) - 500);
         TSM_ASSERT_THROWS("DataSealer did not throw on expired data.", sealer->unwrap(wrapped.c_str()), IOException);
-    }
+
+		wrapped = sealer->wrap(data.c_str(), time(nullptr) - 500);
+		wrapped.insert(0, "invalid");
+		TSM_ASSERT_THROWS("DataSealer did not throw on wrong key label.", sealer->unwrap(wrapped.c_str()), IOException);
+	}
 };

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list