summaryrefslogtreecommitdiff
path: root/src/kw_aes_des.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kw_aes_des.c')
-rw-r--r--src/kw_aes_des.c493
1 files changed, 493 insertions, 0 deletions
diff --git a/src/kw_aes_des.c b/src/kw_aes_des.c
new file mode 100644
index 00000000..022e720a
--- /dev/null
+++ b/src/kw_aes_des.c
@@ -0,0 +1,493 @@
+/**
+ * XML Security Library (http://www.aleksey.com/xmlsec).
+ *
+ * Implementation of AES/DES Key Transport algorithm
+ *
+ * This is free software; see Copyright file in the source
+ * distribution for preciese wording.
+ *
+ * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com>
+ */
+#include "globals.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libxml/tree.h>
+
+#include <xmlsec/xmlsec.h>
+#include <xmlsec/errors.h>
+
+#include "kw_aes_des.h"
+
+#ifndef XMLSEC_NO_DES
+
+static int xmlSecKWDes3BufferReverse (xmlSecByte *buf,
+ xmlSecSize size);
+
+/********************************************************************
+ *
+ * CMS Triple DES Key Wrap
+ *
+ * http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap
+ *
+ * The following algorithm wraps (encrypts) a key (the wrapped key, WK)
+ * under a TRIPLEDES key-encryption-key (KEK) as specified in [CMS-Algorithms]:
+ *
+ * 1. Represent the key being wrapped as an octet sequence. If it is a
+ * TRIPLEDES key, this is 24 octets (192 bits) with odd parity bit as
+ * the bottom bit of each octet.
+ * 2. Compute the CMS key checksum (section 5.6.1) call this CKS.
+ * 3. Let WKCKS = WK || CKS, where || is concatenation.
+ * 4. Generate 8 random octets [RANDOM] and call this IV.
+ * 5. Encrypt WKCKS in CBC mode using KEK as the key and IV as the
+ * initialization vector. Call the results TEMP1.
+ * 6. Left TEMP2 = IV || TEMP1.
+ * 7. Reverse the order of the octets in TEMP2 and call the result TEMP3.
+ * 8. Encrypt TEMP3 in CBC mode using the KEK and an initialization vector
+ * of 0x4adda22c79e82105. The resulting cipher text is the desired result.
+ * It is 40 octets long if a 168 bit key is being wrapped.
+ *
+ * The following algorithm unwraps (decrypts) a key as specified in
+ * [CMS-Algorithms]:
+ *
+ * 1. Check if the length of the cipher text is reasonable given the key type.
+ * It must be 40 bytes for a 168 bit key and either 32, 40, or 48 bytes for
+ * a 128, 192, or 256 bit key. If the length is not supported or inconsistent
+ * with the algorithm for which the key is intended, return error.
+ * 2. Decrypt the cipher text with TRIPLEDES in CBC mode using the KEK and
+ * an initialization vector (IV) of 0x4adda22c79e82105. Call the output TEMP3.
+ * 3. Reverse the order of the octets in TEMP3 and call the result TEMP2.
+ * 4. Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining
+ * octets.
+ * 5. Decrypt TEMP1 using TRIPLEDES in CBC mode using the KEK and the IV found
+ * in the previous step. Call the result WKCKS.
+ * 6. Decompose WKCKS. CKS is the last 8 octets and WK, the wrapped key, are
+ * those octets before the CKS.
+ * 7. Calculate a CMS key checksum (section 5.6.1) over the WK and compare
+ * with the CKS extracted in the above step. If they are not equal, return
+ * error.
+ * 8. WK is the wrapped key, now extracted for use in data decryption.
+ *
+ ********************************************************************/
+static xmlSecByte xmlSecKWDes3Iv[XMLSEC_KW_DES3_IV_LENGTH] = {
+ 0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05
+};
+
+int
+xmlSecKWDes3Encode(xmlSecKWDes3Id kwDes3Id, void *context,
+ const xmlSecByte *in, xmlSecSize inSize,
+ xmlSecByte *out, xmlSecSize outSize) {
+ xmlSecByte sha1[XMLSEC_KW_DES3_SHA_DIGEST_LENGTH];
+ xmlSecByte iv[XMLSEC_KW_DES3_IV_LENGTH];
+ xmlSecSize s;
+ int ret;
+
+ xmlSecAssert2(xmlSecKWDes3CheckId(kwDes3Id), -1);
+ xmlSecAssert2(context != NULL, -1);
+ xmlSecAssert2(in != NULL, -1);
+ xmlSecAssert2(inSize > 0, -1);
+ xmlSecAssert2(out != NULL, -1);
+ xmlSecAssert2(outSize >= inSize + XMLSEC_KW_DES3_BLOCK_LENGTH + XMLSEC_KW_DES3_IV_LENGTH, -1);
+
+ /* step 2: calculate sha1 and CMS */
+ ret = kwDes3Id->sha1(context, in, inSize, sha1, sizeof(sha1));
+ if((ret < 0) || (ret != sizeof(sha1))) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "kwDes3Id->sha1",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ "ret=%d", ret);
+ return(-1);
+ }
+
+ /* step 3: construct WKCKS as WK || CKS */
+ memcpy(out, in, inSize);
+ memcpy(out + inSize, sha1, XMLSEC_KW_DES3_BLOCK_LENGTH);
+
+ /* step 4: generate random iv */
+ ret = kwDes3Id->generateRandom(context, iv, sizeof(iv));
+ if((ret < 0) || (ret != sizeof(iv))) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "kwDes3Id->generateRandom",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ "ret=%d", ret);
+ return(-1);
+ }
+
+ /* step 5: first encryption, result is TEMP1 */
+ ret = kwDes3Id->encrypt(context,
+ iv, sizeof(iv),
+ out, inSize + XMLSEC_KW_DES3_BLOCK_LENGTH,
+ out, outSize);
+ if((ret < 0) || ((xmlSecSize)ret != inSize + XMLSEC_KW_DES3_BLOCK_LENGTH)) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "kwDes3Id->encrypt",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ "ret=%d", ret);
+ return(-1);
+ }
+
+ /* step 6: construct TEMP2=IV || TEMP1 */
+ memmove(out + XMLSEC_KW_DES3_IV_LENGTH, out, inSize + XMLSEC_KW_DES3_BLOCK_LENGTH);
+ memcpy(out, iv, XMLSEC_KW_DES3_IV_LENGTH);
+ s = inSize + XMLSEC_KW_DES3_BLOCK_LENGTH + XMLSEC_KW_DES3_IV_LENGTH;
+
+ /* step 7: reverse octets order, result is TEMP3 */
+ ret = xmlSecKWDes3BufferReverse(out, s);
+ if(ret < 0) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "xmlSecKWDes3BufferReverse",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ "ret=%d", ret);
+ return(-1);
+ }
+
+ /* step 8: second encryption with static IV */
+ ret = kwDes3Id->encrypt(context,
+ xmlSecKWDes3Iv, sizeof(xmlSecKWDes3Iv),
+ out, s,
+ out, outSize);
+ if((ret < 0) || ((xmlSecSize)ret != s)) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "kwDes3Id->encrypt",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ "ret=%d", ret);
+ return(-1);
+ }
+
+ s = ret;
+ return(s);
+}
+
+int
+xmlSecKWDes3Decode(xmlSecKWDes3Id kwDes3Id, void *context,
+ const xmlSecByte *in, xmlSecSize inSize,
+ xmlSecByte *out, xmlSecSize outSize)
+{
+ xmlSecByte sha1[XMLSEC_KW_DES3_SHA_DIGEST_LENGTH];
+ xmlSecSize s;
+ int ret;
+
+ xmlSecAssert2(xmlSecKWDes3CheckId(kwDes3Id), -1);
+ xmlSecAssert2(context != NULL, -1);
+ xmlSecAssert2(in != NULL, -1);
+ xmlSecAssert2(inSize > 0, -1);
+ xmlSecAssert2(out != NULL, -1);
+ xmlSecAssert2(outSize >= inSize, -1);
+
+
+ /* step 2: first decryption with static IV, result is TEMP3 */
+ ret = kwDes3Id->decrypt(context,
+ xmlSecKWDes3Iv, sizeof(xmlSecKWDes3Iv),
+ in, inSize,
+ out, outSize);
+ if((ret < 0) || (ret < XMLSEC_KW_DES3_IV_LENGTH)) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "kwDes3Id->decrypt",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ "ret=%d", ret);
+ return(-1);
+ }
+ s = ret;
+
+ /* step 3: reverse octets order in TEMP3, result is TEMP2 */
+ ret = xmlSecKWDes3BufferReverse(out, s);
+ if(ret < 0) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "xmlSecKWDes3BufferReverse",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ "ret=%d", ret);
+ return(-1);
+ }
+
+ /* steps 4 and 5: get IV and decrypt second time, result is WKCKS */
+ ret = kwDes3Id->decrypt(context,
+ out, XMLSEC_KW_DES3_IV_LENGTH,
+ out + XMLSEC_KW_DES3_IV_LENGTH, s - XMLSEC_KW_DES3_IV_LENGTH,
+ out, outSize);
+ if((ret < 0) || (ret < XMLSEC_KW_DES3_BLOCK_LENGTH)) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "kwDes3Id->decrypt",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ "ret=%d", ret);
+ return(-1);
+ }
+ s = ret - XMLSEC_KW_DES3_BLOCK_LENGTH;
+
+ /* steps 6 and 7: calculate SHA1 and validate it */
+ ret = kwDes3Id->sha1(context,
+ out, s,
+ sha1, sizeof(sha1));
+ if((ret < 0) || (ret != sizeof(sha1))) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "kwDes3Id->sha1",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ "ret=%d", ret);
+ return(-1);
+ }
+
+ /* check sha1 */
+ xmlSecAssert2(XMLSEC_KW_DES3_BLOCK_LENGTH <= sizeof(sha1), -1);
+ if(memcmp(sha1, out + s, XMLSEC_KW_DES3_BLOCK_LENGTH) != 0) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ NULL,
+ XMLSEC_ERRORS_R_INVALID_DATA,
+ "SHA1 does not match");
+ return(-1);
+ }
+
+ /* done */
+ return(s);
+}
+
+static int
+xmlSecKWDes3BufferReverse(xmlSecByte *buf, xmlSecSize size)
+{
+ xmlSecByte * p;
+ xmlSecByte ch;
+
+ xmlSecAssert2(buf != NULL, -1);
+ xmlSecAssert2(size > 0, -1);
+
+ for(p = buf + size - 1; p >= buf; ++buf, --p) {
+ ch = (*p);
+ (*p) = (*buf);
+ (*buf) = ch;
+ }
+ return (0);
+}
+
+#endif /* XMLSEC_NO_DES */
+
+
+
+#ifndef XMLSEC_NO_AES
+/********************************************************************
+ *
+ * KT AES
+ *
+ * http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap:
+ *
+ * Assume that the data to be wrapped consists of N 64-bit data blocks
+ * denoted P(1), P(2), P(3) ... P(N). The result of wrapping will be N+1
+ * 64-bit blocks denoted C(0), C(1), C(2), ... C(N). The key encrypting
+ * key is represented by K. Assume integers i, j, and t and intermediate
+ * 64-bit register A, 128-bit register B, and array of 64-bit quantities
+ * R(1) through R(N).
+ *
+ * "|" represents concatentation so x|y, where x and y and 64-bit quantities,
+ * is the 128-bit quantity with x in the most significant bits and y in the
+ * least significant bits. AES(K)enc(x) is the operation of AES encrypting
+ * the 128-bit quantity x under the key K. AES(K)dec(x) is the corresponding
+ * decryption opteration. XOR(x,y) is the bitwise exclusive or of x and y.
+ * MSB(x) and LSB(y) are the most significant 64 bits and least significant
+ * 64 bits of x and y respectively.
+ *
+ * If N is 1, a single AES operation is performed for wrap or unwrap.
+ * If N>1, then 6*N AES operations are performed for wrap or unwrap.
+ *
+ * The key wrap algorithm is as follows:
+ *
+ * 1. If N is 1:
+ * * B=AES(K)enc(0xA6A6A6A6A6A6A6A6|P(1))
+ * * C(0)=MSB(B)
+ * * C(1)=LSB(B)
+ * If N>1, perform the following steps:
+ * 2. Initialize variables:
+ * * Set A to 0xA6A6A6A6A6A6A6A6
+ * * Fori=1 to N,
+ * R(i)=P(i)
+ * 3. Calculate intermediate values:
+ * * Forj=0 to 5,
+ * o For i=1 to N,
+ * t= i + j*N
+ * B=AES(K)enc(A|R(i))
+ * A=XOR(t,MSB(B))
+ * R(i)=LSB(B)
+ * 4. Output the results:
+ * * Set C(0)=A
+ * * For i=1 to N,
+ * C(i)=R(i)
+ *
+ * The key unwrap algorithm is as follows:
+ *
+ * 1. If N is 1:
+ * * B=AES(K)dec(C(0)|C(1))
+ * * P(1)=LSB(B)
+ * * If MSB(B) is 0xA6A6A6A6A6A6A6A6, return success. Otherwise,
+ * return an integrity check failure error.
+ * If N>1, perform the following steps:
+ * 2. Initialize the variables:
+ * * A=C(0)
+ * * For i=1 to N,
+ * R(i)=C(i)
+ * 3. Calculate intermediate values:
+ * * For j=5 to 0,
+ * o For i=N to 1,
+ * t= i + j*N
+ * B=AES(K)dec(XOR(t,A)|R(i))
+ * A=MSB(B)
+ * R(i)=LSB(B)
+ * 4. Output the results:
+ * * For i=1 to N,
+ * P(i)=R(i)
+ * * If A is 0xA6A6A6A6A6A6A6A6, return success. Otherwise, return
+ * an integrity check failure error.
+ ********************************************************************/
+static const xmlSecByte xmlSecKWAesMagicBlock[XMLSEC_KW_AES_MAGIC_BLOCK_SIZE] = {
+ 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6
+};
+
+int
+xmlSecKWAesEncode(xmlSecKWAesId kwAesId, void *context,
+ const xmlSecByte *in, xmlSecSize inSize,
+ xmlSecByte *out, xmlSecSize outSize) {
+ xmlSecByte block[XMLSEC_KW_AES_BLOCK_SIZE];
+ xmlSecByte *p;
+ int N, i, j, t;
+ int ret;
+
+ xmlSecAssert2(kwAesId != NULL, -1);
+ xmlSecAssert2(kwAesId->encrypt != NULL, -1);
+ xmlSecAssert2(kwAesId->decrypt != NULL, -1);
+ xmlSecAssert2(context != NULL, -1);
+ xmlSecAssert2(in != NULL, -1);
+ xmlSecAssert2(inSize > 0, -1);
+ xmlSecAssert2(out != NULL, -1);
+ xmlSecAssert2(outSize >= inSize + XMLSEC_KW_AES_MAGIC_BLOCK_SIZE, -1);
+
+ /* prepend magic block */
+ if(in != out) {
+ memcpy(out + XMLSEC_KW_AES_MAGIC_BLOCK_SIZE, in, inSize);
+ } else {
+ memmove(out + XMLSEC_KW_AES_MAGIC_BLOCK_SIZE, out, inSize);
+ }
+ memcpy(out, xmlSecKWAesMagicBlock, XMLSEC_KW_AES_MAGIC_BLOCK_SIZE);
+
+ N = (inSize / 8);
+ if(N == 1) {
+ ret = kwAesId->encrypt(out, inSize + XMLSEC_KW_AES_MAGIC_BLOCK_SIZE, out, outSize, context);
+ if(ret < 0) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "kwAesId->encrypt",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ return(-1);
+ }
+ } else {
+ for(j = 0; j <= 5; ++j) {
+ for(i = 1; i <= N; ++i) {
+ t = i + (j * N);
+ p = out + i * 8;
+
+ memcpy(block, out, 8);
+ memcpy(block + 8, p, 8);
+
+ ret = kwAesId->encrypt(block, sizeof(block), block, sizeof(block), context);
+ if(ret < 0) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "kwAesId->encrypt",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ return(-1);
+ }
+ block[7] ^= t;
+ memcpy(out, block, 8);
+ memcpy(p, block + 8, 8);
+ }
+ }
+ }
+
+ return(inSize + 8);
+}
+
+int
+xmlSecKWAesDecode(xmlSecKWAesId kwAesId, void *context,
+ const xmlSecByte *in, xmlSecSize inSize,
+ xmlSecByte *out, xmlSecSize outSize) {
+ xmlSecByte block[XMLSEC_KW_AES_BLOCK_SIZE];
+ xmlSecByte *p;
+ int N, i, j, t;
+ int ret;
+
+ xmlSecAssert2(kwAesId != NULL, -1);
+ xmlSecAssert2(kwAesId->encrypt != NULL, -1);
+ xmlSecAssert2(kwAesId->decrypt != NULL, -1);
+ xmlSecAssert2(context != NULL, -1);
+ xmlSecAssert2(in != NULL, -1);
+ xmlSecAssert2(inSize > 0, -1);
+ xmlSecAssert2(out != NULL, -1);
+ xmlSecAssert2(outSize >= inSize, -1);
+
+ /* copy input */
+ if(in != out) {
+ memcpy(out, in, inSize);
+ }
+
+ N = (inSize / 8) - 1;
+ if(N == 1) {
+ ret = kwAesId->decrypt(out, inSize, out, outSize, context);
+ if(ret < 0) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "kwAesId->decrypt",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ return(-1);
+ }
+ } else {
+ for(j = 5; j >= 0; --j) {
+ for(i = N; i > 0; --i) {
+ t = i + (j * N);
+ p = out + i * 8;
+
+ memcpy(block, out, 8);
+ memcpy(block + 8, p, 8);
+ block[7] ^= t;
+
+ ret = kwAesId->decrypt(block, sizeof(block), block, sizeof(block), context);
+ if(ret < 0) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ "kwAesId->decrypt",
+ XMLSEC_ERRORS_R_XMLSEC_FAILED,
+ XMLSEC_ERRORS_NO_MESSAGE);
+ return(-1);
+ }
+ memcpy(out, block, 8);
+ memcpy(p, block + 8, 8);
+ }
+ }
+ }
+ /* do not left data in memory */
+ memset(block, 0, sizeof(block));
+
+ /* check the output */
+ if(memcmp(xmlSecKWAesMagicBlock, out, XMLSEC_KW_AES_MAGIC_BLOCK_SIZE) != 0) {
+ xmlSecError(XMLSEC_ERRORS_HERE,
+ NULL,
+ NULL,
+ XMLSEC_ERRORS_R_INVALID_DATA,
+ "bad magic block");
+ return(-1);
+ }
+
+ /* get rid of magic block */
+ memmove(out, out + XMLSEC_KW_AES_MAGIC_BLOCK_SIZE, inSize - XMLSEC_KW_AES_MAGIC_BLOCK_SIZE);
+ return(inSize - XMLSEC_KW_AES_MAGIC_BLOCK_SIZE);
+}
+
+#endif /* XMLSEC_NO_AES */
+