| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| /* |
| * XSEC |
| * |
| * XSECCryptoUtils:= Helper crypo utilities that make life easier |
| * |
| * Author(s): Berin Lautenbach |
| * |
| * $Id$ |
| * |
| */ |
| |
| #include <xsec/framework/XSECDefs.hpp> |
| #include <xsec/framework/XSECError.hpp> |
| #include <xsec/enc/XSECCryptoUtils.hpp> |
| #include <xsec/enc/XSECCryptoKeyHMAC.hpp> |
| #include <xsec/utils/XSECPlatformUtils.hpp> |
| |
| #include "../utils/XSECAlgorithmSupport.hpp" |
| #include "../utils/XSECAutoPtr.hpp" |
| #include "../utils/XSECDOMUtils.hpp" |
| |
| #include <xercesc/util/Janitor.hpp> |
| #include <xercesc/util/XMLString.hpp> |
| #include <xercesc/util/XMLUniDefs.hpp> |
| |
| XERCES_CPP_NAMESPACE_USE |
| |
| #ifdef XSEC_XKMS_ENABLED |
| |
| // -------------------------------------------------------------------------------- |
| // XKMS Limited-Use Shared Secret handling |
| // -------------------------------------------------------------------------------- |
| |
| int CleanXKMSPassPhrase(unsigned char * input, int inputLen, safeBuffer &output) { |
| |
| // Now obsolete - use SASLCleanXKMSPassPhrase instead |
| int j = 0; |
| unsigned char c; |
| for (int i = 0; i < inputLen; ++i) { |
| |
| c = input[i]; |
| |
| if (c >= 'A' && c <= 'Z') { |
| output[j++] = c - 'A' + 'a'; |
| } |
| else if (c != '\n' && c != '\r' && c != '\t' && c != ' ') { |
| output[j++] = c; |
| } |
| |
| } |
| |
| return j; |
| |
| } |
| |
| int SASLCleanXKMSPassPhrase(unsigned char * input, int inputLen, safeBuffer &output) { |
| |
| // For now - this does *not* implement the full SASLPrep algorithm. |
| // THe NFKC form is not trivial to implement - so this is kept very simple. |
| // TODO - Fix this - it can be an interoperability issue as pass phrases from |
| // different implementations could be treated differently. |
| // Currently we only check for prohibited unput for chars < 0xFFFF and drop any |
| // chars over 0xFFFF |
| |
| unsigned char * inp = new unsigned char[inputLen + 1]; |
| ArrayJanitor<unsigned char> j_inp(inp); |
| memcpy(inp, input, inputLen); |
| inp[inputLen] = '\0'; |
| |
| XSECAutoPtrXMLCh uinput((char *) inp); |
| XMLSize_t l = XMLString::stringLen(uinput.get()); |
| XMLCh* uoutput = new XMLCh[l + 1]; |
| ArrayJanitor<XMLCh> j_uoutput(uoutput); |
| |
| XMLSize_t i, j; |
| j = 0; |
| |
| XMLCh ch1; |
| |
| for (i = 0; i < l; ++i) { |
| |
| ch1 = uinput.get()[i]; |
| // Case one - char is < 0x10000 |
| if (ch1 < 0xD800 || ch1 > 0xDFFF) { |
| |
| // OK - ch1 is "real" value - let's see if it is legal |
| // The following switch tables are derived from |
| // RFC 3454 - see http://www.ietf.org/rfc/rfc3454.txt |
| |
| // Non-ASCII Spaces - C.1.2 |
| switch (ch1) { |
| |
| case 0x00A0: // NO-BREAK SPACE |
| case 0x1680: // OGHAM SPACE MARK |
| case 0x2000: // EN QUAD |
| case 0x2001: // EM QUAD |
| case 0x2002: // EN SPACE |
| case 0x2003: // EM SPACE |
| case 0x2004: // THREE-PER-EM SPACE |
| case 0x2005: // FOUR-PER-EM SPACE |
| case 0x2006: // SIX-PER-EM SPACE |
| case 0x2007: // FIGURE SPACE |
| case 0x2008: // PUNCTUATION SPACE |
| case 0x2009: // THIN SPACE |
| case 0x200A: // HAIR SPACE |
| case 0x200B: // ZERO WIDTH SPACE |
| case 0x202F: // NARROW NO-BREAK SPACE |
| case 0x205F: // MEDIUM MATHEMATICAL SPACE |
| case 0x3000: // IDEOGRAPHIC SPACE |
| |
| throw XSECException(XSECException::XKMSError, |
| "SASLCleanXKMSPassPhrase - Non ASCII space character in XKMS pass phrase"); |
| default: |
| |
| break; |
| |
| } |
| |
| // ASCII Control characters |
| // Note - us unsigned, so always >= 0) |
| if ((ch1 <= 0x1F) || (ch1 == 0x7F)) { |
| throw XSECException(XSECException::XKMSError, |
| "SASLCleanXKMSPassPhrase - ASCII control character in XKMS pass phrase"); |
| } |
| |
| // Non-ASCII Control Characters |
| if ((ch1 >= 0x80 && ch1 <= 0x9F) || |
| (ch1 >= 0x206A && ch1 <= 0x206F) || |
| (ch1 >= 0xFFF9 && ch1 <= 0xFFFC)) { |
| |
| throw XSECException(XSECException::XKMSError, |
| "SASLCleanXKMSPassPhrase - NON ASCII control character in XKMS pass phrase"); |
| } |
| |
| switch (ch1) { |
| |
| |
| case 0x06DD: // ARABIC END OF AYAH |
| case 0x070F: // SYRIAC ABBREVIATION MARK |
| case 0x180E: // MONGOLIAN VOWEL SEPARATOR |
| case 0x200C: // ZERO WIDTH NON-JOINER |
| case 0x200D: // ZERO WIDTH JOINER |
| case 0x2028: // LINE SEPARATOR |
| case 0x2029: // PARAGRAPH SEPARATOR |
| case 0x2060: // WORD JOINER |
| case 0x2061: // FUNCTION APPLICATION |
| case 0x2062: // INVISIBLE TIMES |
| case 0x2063: // INVISIBLE SEPARATOR |
| case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE |
| throw XSECException(XSECException::XKMSError, |
| "SASLCleanXKMSPassPhrase - NON ASCII control character in XKMS pass phrase"); |
| default: |
| break; } |
| // 1D173-1D17A; [MUSICAL CONTROL CHARACTERS] is not relevant as we are looking at |
| // ch1 at the moment |
| |
| // Private Use characters |
| if ((ch1 >= 0xE000 && ch1 <= 0xF8FF)) { |
| |
| throw XSECException(XSECException::XKMSError, |
| "SASLCleanXKMSPassPhrase - Private Use character in XKMS pass phrase"); |
| } |
| |
| // Non-character code points |
| if ((ch1 >= 0xFDD0 && ch1 <= 0xFDEF) || |
| (ch1 >= 0xFFFE)) { |
| |
| throw XSECException(XSECException::XKMSError, |
| "SASLCleanXKMSPassPhrase - Non-character code points in XKMS pass phrase"); |
| } |
| |
| // Inappropriate for plain text characters |
| |
| switch (ch1) { |
| case 0xFFF9: // INTERLINEAR ANNOTATION ANCHOR |
| case 0xFFFA: // INTERLINEAR ANNOTATION SEPARATOR |
| case 0xFFFB: // INTERLINEAR ANNOTATION TERMINATOR |
| case 0xFFFC: // OBJECT REPLACEMENT CHARACTER |
| case 0xFFFD: // REPLACEMENT CHARACTER |
| throw XSECException(XSECException::XKMSError, |
| "SASLCleanXKMSPassPhrase - Innappropriate for plain text chararcters in XKMS pass phrase"); |
| default: |
| break; |
| } |
| |
| // Inappripriate for canonical representation characters |
| if (ch1 >= 0x2FF0 && ch1 <= 0x2FFB) { |
| throw XSECException(XSECException::XKMSError, |
| "SASLCleanXKMSPassPhrase - Innappropriate for canonicalisation chararcters in XKMS pass phrase"); |
| } |
| |
| // Change display properties or are deprecated |
| switch (ch1) { |
| case 0x0340: // COMBINING GRAVE TONE MARK |
| case 0x0341: // COMBINING ACUTE TONE MARK |
| case 0x200E: // LEFT-TO-RIGHT MARK |
| case 0x200F: // RIGHT-TO-LEFT MARK |
| case 0x202A: // LEFT-TO-RIGHT EMBEDDING |
| case 0x202B: // RIGHT-TO-LEFT EMBEDDING |
| case 0x202C: // POP DIRECTIONAL FORMATTING |
| case 0x202D: // LEFT-TO-RIGHT OVERRIDE |
| case 0x202E: // RIGHT-TO-LEFT OVERRIDE |
| case 0x206A: // INHIBIT SYMMETRIC SWAPPING |
| case 0x206B: // ACTIVATE SYMMETRIC SWAPPING |
| case 0x206C: // INHIBIT ARABIC FORM SHAPING |
| case 0x206D: // ACTIVATE ARABIC FORM SHAPING |
| case 0x206E: // NATIONAL DIGIT SHAPES |
| case 0x206F: // NOMINAL DIGIT SHAPES |
| throw XSECException(XSECException::XKMSError, |
| "SASLCleanXKMSPassPhrase - change display or deprecated chararcters in XKMS pass phrase"); |
| default: |
| break; |
| } |
| |
| // We got this far = just run with it for now |
| uoutput[j++] = ch1; |
| } |
| else { |
| throw XSECException(XSECException::XKMSError, |
| "SASLCleanXKMSPassPhrase - don't support XKMS pass phrase chars > 0xFFFF"); |
| } |
| } /* for */ |
| uoutput[j++] = chNull; |
| |
| // Now transcode |
| char * utf8output= transcodeToUTF8(uoutput); |
| output.sbStrcpyIn(utf8output); |
| |
| int ret = (int)strlen(utf8output); |
| XSEC_RELEASE_XMLCH(utf8output); |
| return ret; |
| } |
| |
| int XSEC_EXPORT CalculateXKMSAuthenticationKey(unsigned char * input, int inputLen, unsigned char * output, int maxOutputLen) { |
| |
| unsigned char keyVal[] = {XKMSAuthenticationValue}; |
| |
| XSECCryptoKeyHMAC * k = XSECPlatformUtils::g_cryptoProvider->keyHMAC(); |
| Janitor<XSECCryptoKeyHMAC> j_k(k); |
| k->setKey(keyVal, 1); |
| |
| XSECCryptoHash *h = XSECPlatformUtils::g_cryptoProvider->HMAC(XSECCryptoHash::HASH_SHA1); |
| Janitor<XSECCryptoHash> j_h(h); |
| h->setKey(k); |
| |
| // Clean the input |
| safeBuffer sb; |
| int l = SASLCleanXKMSPassPhrase(input, inputLen, sb); |
| |
| h->hash((unsigned char *) sb.rawBuffer(), l); |
| return h->finish(output, maxOutputLen); |
| |
| } |
| |
| |
| int XSEC_EXPORT CalculateXKMSRevocationCodeIdentifierEncoding1(unsigned char * input, int inputLen, unsigned char * output, int maxOutputLen) { |
| |
| unsigned char keyVal[] = {XKMSRevocationCodeIdenfitierEncoding1}; |
| |
| XSECCryptoKeyHMAC * k = XSECPlatformUtils::g_cryptoProvider->keyHMAC(); |
| Janitor<XSECCryptoKeyHMAC> j_k(k); |
| k->setKey(keyVal, 1); |
| |
| XSECCryptoHash *h = XSECPlatformUtils::g_cryptoProvider->HMAC(XSECCryptoHash::HASH_SHA1); |
| Janitor<XSECCryptoHash> j_h(h); |
| |
| h->setKey(k); |
| |
| // Clean the input |
| safeBuffer sb; |
| int l = CleanXKMSPassPhrase(input, inputLen, sb); |
| |
| h->hash((unsigned char *) sb.rawBuffer(), l); |
| return h->finish(output, maxOutputLen); |
| |
| } |
| |
| int XSEC_EXPORT CalculateXKMSRevocationCodeIdentifierEncoding2(unsigned char * input, int inputLen, unsigned char * output, int maxOutputLen) { |
| |
| |
| unsigned char tmpBuf[XSEC_MAX_HASH_SIZE]; |
| int tmpLen = CalculateXKMSRevocationCodeIdentifierEncoding1(input, inputLen, tmpBuf, XSEC_MAX_HASH_SIZE); |
| return CalculateXKMSRevocationCodeIdentifierEncoding2From1(tmpBuf, tmpLen, output, maxOutputLen); |
| |
| } |
| |
| int XSEC_EXPORT CalculateXKMSRevocationCodeIdentifierEncoding2From1(unsigned char * input, int inputLen, unsigned char * output, int maxOutputLen) { |
| |
| unsigned char keyVal[] = {XKMSRevocationCodeIdenfitierEncoding2}; |
| |
| XSECCryptoKeyHMAC * k = XSECPlatformUtils::g_cryptoProvider->keyHMAC(); |
| Janitor<XSECCryptoKeyHMAC> j_k(k); |
| k->setKey(keyVal, 1); |
| |
| XSECCryptoHash *h = XSECPlatformUtils::g_cryptoProvider->HMAC(XSECCryptoHash::HASH_SHA1); |
| Janitor<XSECCryptoHash> j_h(h); |
| |
| h->setKey(k); |
| |
| h->hash(input, inputLen); |
| return h->finish(output, maxOutputLen); |
| |
| } |
| |
| int XSEC_EXPORT CalculateXKMSKEK(unsigned char * input, int inputLen, unsigned char * output, int maxOutputLen) { |
| |
| unsigned char keyVal[] = {XKMSKeyEncryption}; |
| unsigned char shaOutput[22]; // SHA1 has 20 bytes of output |
| |
| // Clean the input |
| safeBuffer sb; |
| int l = SASLCleanXKMSPassPhrase(input, inputLen, sb); |
| |
| // Need to iterate through until we have enough data |
| int bytesDone = 0, bytesToDo;; |
| shaOutput[0] = keyVal[0]; |
| int keyLen = 1; |
| while (bytesDone < maxOutputLen) { |
| XSECCryptoKeyHMAC * k = XSECPlatformUtils::g_cryptoProvider->keyHMAC(); |
| k->setKey(shaOutput, keyLen); |
| |
| XSECCryptoHash *h = XSECPlatformUtils::g_cryptoProvider->HMAC(XSECCryptoHash::HASH_SHA1); |
| Janitor<XSECCryptoHash> j_h(h); |
| |
| h->setKey(k); |
| delete(k); |
| |
| // Now hash next round of data |
| h->hash((unsigned char *) sb.rawBuffer(), l); |
| keyLen = h->finish(shaOutput, 22); |
| |
| // Copy into the output buffer |
| bytesToDo = maxOutputLen - bytesDone; |
| bytesToDo = bytesToDo > 20 ? 20 : bytesToDo; |
| memcpy(&output[bytesDone], shaOutput, bytesToDo); |
| bytesDone += bytesToDo; |
| |
| // Set up for next key |
| shaOutput[0] ^= keyVal[0]; |
| keyLen = 20; |
| |
| j_h.release(); |
| delete h; |
| } |
| |
| return bytesDone; |
| |
| } |
| |
| #endif |
| |
| // -------------------------------------------------------------------------------- |
| // Some Base64 helpers |
| // -------------------------------------------------------------------------------- |
| |
| XMLCh XSEC_EXPORT * EncodeToBase64XMLCh(unsigned char * input, int inputLen) { |
| |
| XSECCryptoBase64 * b64 = XSECPlatformUtils::g_cryptoProvider->base64(); |
| Janitor<XSECCryptoBase64> j_b64(b64); |
| unsigned char * output; |
| int outputLen = ((4 * inputLen) / 3) + 5; |
| XSECnew(output, unsigned char[outputLen]); |
| ArrayJanitor<unsigned char> j_output(output); |
| |
| b64->encodeInit(); |
| int j = b64->encode(input, inputLen, output, outputLen - 1); |
| j += b64->encodeFinish(&output[j], outputLen - j - 1); |
| |
| // Strip any trailing \n\r |
| while (j > 0 && (output[j-1] == '\n' || output[j-1] == '\r')) |
| j--; |
| |
| // Now transcode and get out of here |
| output[j] = '\0'; |
| return XMLString::transcode((char *) output); |
| |
| } |
| |
| unsigned int XSEC_EXPORT DecodeFromBase64XMLCh(const XMLCh * input, unsigned char * output, int maxOutputLen) { |
| |
| XSECCryptoBase64 * b64 = XSECPlatformUtils::g_cryptoProvider->base64(); |
| Janitor<XSECCryptoBase64> j_b64(b64); |
| |
| XSECAutoPtrChar tinput(input); |
| |
| b64->decodeInit(); |
| unsigned int j = b64->decode((unsigned char *) tinput.get(), (unsigned int) strlen(tinput.get()), output, maxOutputLen - 1); |
| j += b64->decodeFinish(&output[j], maxOutputLen - j - 1); |
| |
| return j; |
| } |
| |
| unsigned int XSEC_EXPORT DecodeFromBase64(const char * input, unsigned char * output, int maxOutputLen) { |
| |
| XSECCryptoBase64 * b64 = XSECPlatformUtils::g_cryptoProvider->base64(); |
| Janitor<XSECCryptoBase64> j_b64(b64); |
| |
| b64->decodeInit(); |
| unsigned int j = b64->decode((unsigned char *) input, (unsigned int) strlen(input), output, maxOutputLen - 1); |
| j += b64->decodeFinish(&output[j], maxOutputLen - j - 1); |
| |
| return j; |
| } |
| |
| |
| // -------------------------------------------------------------------------------- |
| // Some stuff to help with wierd signatures |
| // -------------------------------------------------------------------------------- |
| |
| const unsigned char ASNDSAProlog[] = {0x30, 0x2c, 0x02, 0x14}; |
| const unsigned char ASNDSAMiddle[] = {0x02, 0x14}; |
| |
| bool ASN2DSASig(const unsigned char * input, unsigned char * r, unsigned char * s) { |
| |
| if (memcmp(ASNDSAProlog, input, 4) != 0 || |
| memcmp(ASNDSAMiddle, &input[24], 2) != 0) |
| |
| return false; |
| |
| memcpy(r, &input[4], 20); |
| memcpy(s, &input[26], 20); |
| |
| return true; |
| |
| } |
| |
| |
| // -------------------------------------------------------------------------------- |
| // Calculate correct OIDs for an RSA sig |
| // -------------------------------------------------------------------------------- |
| |
| /* As per RSA's PKCS #1 v 2.1, Section 9.2 Note 1, the DER encodings for |
| * the has types are as follows: |
| * |
| * MD2: (0x)30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 02 05 00 04 10 || H. |
| * MD5: (0x)30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 05 05 00 04 10 || H. |
| * SHA-1: (0x)30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14 || H. |
| * SHA-256: (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H. |
| * SHA-384: (0x)30 41 30 0d 06 09 60 86 48 01 65 03 04 02 02 05 00 04 30 || H. |
| * SHA-512: (0x)30 51 30 0d 06 09 60 86 48 01 65 03 04 02 03 05 00 04 40 || H. |
| * |
| * More recently the following has been provided for SHA-224 |
| * |
| * SHA-224: 30 2d 30 0d 06 09 60 86 48 01 65 03 04 02 04 05 00 04 1c |
| * |
| */ |
| |
| int MD5OIDLen = 18; |
| unsigned char MD5OID[] = { |
| 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, |
| 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, |
| 0x04, 0x10 |
| }; |
| |
| |
| int sha1OIDLen = 15; |
| unsigned char sha1OID[] = { |
| 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, |
| 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14 |
| }; |
| |
| int sha224OIDLen = 19; |
| unsigned char sha224OID[] = { |
| 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, |
| 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, |
| 0x00, 0x04, 0x1c |
| }; |
| |
| int sha256OIDLen = 19; |
| unsigned char sha256OID[] = { |
| 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, |
| 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, |
| 0x00, 0x04, 0x20 |
| }; |
| |
| int sha384OIDLen = 19; |
| unsigned char sha384OID[] = { |
| 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, |
| 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, |
| 0x00, 0x04, 0x30 |
| }; |
| |
| int sha512OIDLen = 19; |
| unsigned char sha512OID[] = { |
| 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, |
| 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, |
| 0x00, 0x04, 0x40 |
| }; |
| |
| |
| unsigned char* getRSASigOID(XSECCryptoHash::HashType type, int& oidLen) { |
| |
| switch (type) { |
| |
| case (XSECCryptoHash::HASH_MD5): |
| oidLen = MD5OIDLen; |
| return MD5OID; |
| case (XSECCryptoHash::HASH_SHA1): |
| oidLen = sha1OIDLen; |
| return sha1OID; |
| case (XSECCryptoHash::HASH_SHA224): |
| oidLen = sha224OIDLen; |
| return sha224OID; |
| case (XSECCryptoHash::HASH_SHA256): |
| oidLen = sha256OIDLen; |
| return sha256OID; |
| case (XSECCryptoHash::HASH_SHA384): |
| oidLen = sha384OIDLen; |
| return sha384OID; |
| case (XSECCryptoHash::HASH_SHA512): |
| oidLen = sha512OIDLen; |
| return sha512OID; |
| default: |
| oidLen = 0; |
| return NULL; |
| |
| } |
| } |
| |
| XSECCryptoHash* XSECCryptoProvider::hash(const XMLCh* uri) const { |
| |
| return hash(XSECAlgorithmSupport::getHashType(uri)); |
| } |
| |
| XSECCryptoHash* XSECCryptoProvider::HMAC(const XMLCh* uri) const { |
| |
| return HMAC(XSECAlgorithmSupport::getHashType(uri)); |
| } |