security/nss/lib/pk11wrap/pk11sdr.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "seccomon.h"
     6 #include "secoid.h"
     7 #include "secasn1.h"
     8 #include "pkcs11.h"
     9 #include "pk11func.h"
    10 #include "pk11sdr.h"
    12 /*
    13  * Data structure and template for encoding the result of an SDR operation
    14  *  This is temporary.  It should include the algorithm ID of the encryption mechanism
    15  */
    16 struct SDRResult
    17 {
    18   SECItem keyid;
    19   SECAlgorithmID alg;
    20   SECItem data;
    21 };
    22 typedef struct SDRResult SDRResult;
    24 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
    26 static SEC_ASN1Template template[] = {
    27   { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (SDRResult) },
    28   { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, keyid) },
    29   { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(SDRResult, alg),
    30     SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
    31   { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, data) },
    32   { 0 }
    33 };
    35 static unsigned char keyID[] = {
    36   0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    37   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
    38 };
    40 static SECItem keyIDItem = {
    41   0,
    42   keyID,
    43   sizeof keyID
    44 };
    46 /* local utility function for padding an incoming data block
    47  * to the mechanism block size.
    48  */
    49 static SECStatus
    50 padBlock(SECItem *data, int blockSize, SECItem *result)
    51 {
    52   SECStatus rv = SECSuccess;
    53   int padLength;
    54   unsigned int i;
    56   result->data = 0;
    57   result->len = 0;
    59   /* This algorithm always adds to the block (to indicate the number
    60    * of pad bytes).  So allocate a block large enough.
    61    */
    62   padLength = blockSize - (data->len % blockSize);
    63   result->len = data->len + padLength;
    64   result->data = (unsigned char *)PORT_Alloc(result->len);
    66   /* Copy the data */
    67   PORT_Memcpy(result->data, data->data, data->len);
    69   /* Add the pad values */
    70   for(i = data->len; i < result->len; i++)
    71     result->data[i] = (unsigned char)padLength;
    73   return rv;
    74 }
    76 static SECStatus
    77 unpadBlock(SECItem *data, int blockSize, SECItem *result)
    78 {
    79   SECStatus rv = SECSuccess;
    80   int padLength;
    81   unsigned int i;
    83   result->data = 0;
    84   result->len = 0;
    86   /* Remove the padding from the end if the input data */
    87   if (data->len == 0 || data->len % blockSize  != 0) { rv = SECFailure; goto loser; }
    89   padLength = data->data[data->len-1];
    90   if (padLength > blockSize) { rv = SECFailure; goto loser; }
    92   /* verify padding */
    93   for (i=data->len - padLength; i < data->len; i++) {
    94     if (data->data[i] != padLength) {
    95 	rv = SECFailure;
    96 	goto loser;
    97     }
    98   }
   100   result->len = data->len - padLength;
   101   result->data = (unsigned char *)PORT_Alloc(result->len);
   102   if (!result->data) { rv = SECFailure; goto loser; }
   104   PORT_Memcpy(result->data, data->data, result->len);
   106   if (padLength < 2) {
   107     return SECWouldBlock;
   108   }
   110 loser:
   111   return rv;
   112 }
   114 static PRLock *pk11sdrLock = NULL;
   116 void
   117 pk11sdr_Init (void)
   118 {
   119    pk11sdrLock = PR_NewLock();
   120 }
   122 void
   123 pk11sdr_Shutdown(void)
   124 {
   125     if (pk11sdrLock) {
   126 	PR_DestroyLock(pk11sdrLock);
   127 	pk11sdrLock = NULL;
   128     }
   129 }
   131 /*
   132  * PK11SDR_Encrypt
   133  *  Encrypt a block of data using the symmetric key identified.  The result
   134  *  is an ASN.1 (DER) encoded block of keyid, params and data.
   135  */
   136 SECStatus
   137 PK11SDR_Encrypt(SECItem *keyid, SECItem *data, SECItem *result, void *cx)
   138 {
   139   SECStatus rv = SECSuccess;
   140   PK11SlotInfo *slot = 0;
   141   PK11SymKey *key = 0;
   142   SECItem *params = 0;
   143   PK11Context *ctx = 0;
   144   CK_MECHANISM_TYPE type;
   145   SDRResult sdrResult;
   146   SECItem paddedData;
   147   SECItem *pKeyID;
   148   PLArenaPool *arena = 0;
   150   /* Initialize */
   151   paddedData.len = 0;
   152   paddedData.data = 0;
   154   arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
   155   if (!arena) { rv = SECFailure; goto loser; }
   157   /* 1. Locate the requested keyid, or the default key (which has a keyid)
   158    * 2. Create an encryption context
   159    * 3. Encrypt
   160    * 4. Encode the results (using ASN.1)
   161    */
   163   slot = PK11_GetInternalKeySlot();
   164   if (!slot) { rv = SECFailure; goto loser; }
   166   /* Use triple-DES */
   167   type = CKM_DES3_CBC;
   169   /*
   170    * Login to the internal token before we look for the key, otherwise we
   171    * won't find it.
   172    */
   173   rv = PK11_Authenticate(slot, PR_TRUE, cx);
   174   if (rv != SECSuccess) goto loser;
   176   /* Find the key to use */
   177   pKeyID = keyid;
   178   if (pKeyID->len == 0) {
   179 	  pKeyID = &keyIDItem;  /* Use default value */
   181 	  /* put in a course lock to prevent a race between not finding the 
   182 	   * key and creating  one.
   183 	   */
   185 	  if (pk11sdrLock) PR_Lock(pk11sdrLock);
   187 	  /* Try to find the key */
   188 	  key = PK11_FindFixedKey(slot, type, pKeyID, cx);
   190 	  /* If the default key doesn't exist yet, try to create it */
   191 	  if (!key) key = PK11_GenDES3TokenKey(slot, pKeyID, cx);
   192 	  if (pk11sdrLock) PR_Unlock(pk11sdrLock);
   193   } else {
   194 	  key = PK11_FindFixedKey(slot, type, pKeyID, cx);
   195   }
   197   if (!key) { rv = SECFailure; goto loser; }
   199   params = PK11_GenerateNewParam(type, key);
   200   if (!params) { rv = SECFailure; goto loser; }
   202   ctx = PK11_CreateContextBySymKey(type, CKA_ENCRYPT, key, params);
   203   if (!ctx) { rv = SECFailure; goto loser; }
   205   rv = padBlock(data, PK11_GetBlockSize(type, 0), &paddedData);
   206   if (rv != SECSuccess) goto loser;
   208   sdrResult.data.len = paddedData.len;
   209   sdrResult.data.data = (unsigned char *)PORT_ArenaAlloc(arena, sdrResult.data.len);
   211   rv = PK11_CipherOp(ctx, sdrResult.data.data, (int*)&sdrResult.data.len, sdrResult.data.len,
   212                      paddedData.data, paddedData.len);
   213   if (rv != SECSuccess) goto loser;
   215   PK11_Finalize(ctx);
   217   sdrResult.keyid = *pKeyID;
   219   rv = PK11_ParamToAlgid(SEC_OID_DES_EDE3_CBC, params, arena, &sdrResult.alg);
   220   if (rv != SECSuccess) goto loser;
   222   if (!SEC_ASN1EncodeItem(0, result, &sdrResult, template)) { rv = SECFailure; goto loser; }
   224 loser:
   225   SECITEM_ZfreeItem(&paddedData, PR_FALSE);
   226   if (arena) PORT_FreeArena(arena, PR_TRUE);
   227   if (ctx) PK11_DestroyContext(ctx, PR_TRUE);
   228   if (params) SECITEM_ZfreeItem(params, PR_TRUE);
   229   if (key) PK11_FreeSymKey(key);
   230   if (slot) PK11_FreeSlot(slot);
   232   return rv;
   233 }
   235 /* decrypt a block */
   236 static SECStatus
   237 pk11Decrypt(PK11SlotInfo *slot, PLArenaPool *arena, 
   238 	    CK_MECHANISM_TYPE type, PK11SymKey *key, 
   239 	    SECItem *params, SECItem *in, SECItem *result)
   240 {
   241   PK11Context *ctx = 0;
   242   SECItem paddedResult;
   243   SECStatus rv;
   245   paddedResult.len = 0;
   246   paddedResult.data = 0;
   248   ctx = PK11_CreateContextBySymKey(type, CKA_DECRYPT, key, params);
   249   if (!ctx) { rv = SECFailure; goto loser; }
   251   paddedResult.len = in->len;
   252   paddedResult.data = PORT_ArenaAlloc(arena, paddedResult.len);
   254   rv = PK11_CipherOp(ctx, paddedResult.data, 
   255 			(int*)&paddedResult.len, paddedResult.len,
   256 			in->data, in->len);
   257   if (rv != SECSuccess) goto loser;
   259   PK11_Finalize(ctx);
   261   /* Remove the padding */
   262   rv = unpadBlock(&paddedResult, PK11_GetBlockSize(type, 0), result);
   263   if (rv) goto loser;
   265 loser:
   266   if (ctx) PK11_DestroyContext(ctx, PR_TRUE);
   267   return rv;
   268 }
   270 /*
   271  * PK11SDR_Decrypt
   272  *  Decrypt a block of data produced by PK11SDR_Encrypt.  The key used is identified
   273  *  by the keyid field within the input.
   274  */
   275 SECStatus
   276 PK11SDR_Decrypt(SECItem *data, SECItem *result, void *cx)
   277 {
   278   SECStatus rv = SECSuccess;
   279   PK11SlotInfo *slot = 0;
   280   PK11SymKey *key = 0;
   281   CK_MECHANISM_TYPE type;
   282   SDRResult sdrResult;
   283   SECItem *params = 0;
   284   SECItem possibleResult = { 0, NULL, 0 };
   285   PLArenaPool *arena = 0;
   287   arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
   288   if (!arena) { rv = SECFailure; goto loser; }
   290   /* Decode the incoming data */
   291   memset(&sdrResult, 0, sizeof sdrResult);
   292   rv = SEC_QuickDERDecodeItem(arena, &sdrResult, template, data);
   293   if (rv != SECSuccess) goto loser;  /* Invalid format */
   295   /* Find the slot and key for the given keyid */
   296   slot = PK11_GetInternalKeySlot();
   297   if (!slot) { rv = SECFailure; goto loser; }
   299   rv = PK11_Authenticate(slot, PR_TRUE, cx);
   300   if (rv != SECSuccess) goto loser;
   302   /* Get the parameter values from the data */
   303   params = PK11_ParamFromAlgid(&sdrResult.alg);
   304   if (!params) { rv = SECFailure; goto loser; }
   306   /* Use triple-DES (Should look up the algorithm) */
   307   type = CKM_DES3_CBC;
   308   key = PK11_FindFixedKey(slot, type, &sdrResult.keyid, cx);
   309   if (!key) { 
   310 	rv = SECFailure;  
   311   } else {
   312 	rv = pk11Decrypt(slot, arena, type, key, params, 
   313 			&sdrResult.data, result);
   314   }
   316   /*
   317    * if the pad value was too small (1 or 2), then it's statistically
   318    * 'likely' that (1 in 256) that we may not have the correct key.
   319    * Check the other keys for a better match. If we find none, use
   320    * this result.
   321    */
   322   if (rv == SECWouldBlock) {
   323 	possibleResult = *result;
   324   }
   326   /*
   327    * handle the case where your key indicies may have been broken
   328    */
   329   if (rv != SECSuccess) {
   330 	PK11SymKey *keyList = PK11_ListFixedKeysInSlot(slot, NULL, cx);
   331 	PK11SymKey *testKey = NULL;
   332 	PK11SymKey *nextKey = NULL;
   334 	for (testKey = keyList; testKey; 
   335 				testKey = PK11_GetNextSymKey(testKey)) {
   336 	    rv = pk11Decrypt(slot, arena, type, testKey, params, 
   337 			     &sdrResult.data, result);
   338 	    if (rv == SECSuccess) {
   339 		break;
   340 	    } 
   341 	    /* found a close match. If it's our first remember it */
   342 	    if (rv == SECWouldBlock) {
   343 		if (possibleResult.data) {
   344 		    /* this is unlikely but possible. If we hit this condition,
   345 		     * we have no way of knowing which possibility to prefer.
   346 		     * in this case we just match the key the application
   347 		     * thought was the right one */
   348 		    SECITEM_ZfreeItem(result, PR_FALSE);
   349 		} else {
   350 		    possibleResult = *result;
   351 		}
   352 	    }
   353 	}
   355 	/* free the list */
   356 	for (testKey = keyList; testKey; testKey = nextKey) {
   357 	    nextKey = PK11_GetNextSymKey(testKey);
   358 	    PK11_FreeSymKey(testKey);
   359 	}
   360   }
   362   /* we didn't find a better key, use the one with a small pad value */
   363   if ((rv != SECSuccess) && (possibleResult.data)) {
   364 	*result = possibleResult;
   365 	possibleResult.data = NULL;
   366 	rv = SECSuccess;
   367   }
   369 loser:
   370   if (arena) PORT_FreeArena(arena, PR_TRUE);
   371   if (key) PK11_FreeSymKey(key);
   372   if (params) SECITEM_ZfreeItem(params, PR_TRUE);
   373   if (slot) PK11_FreeSlot(slot);
   374   if (possibleResult.data) SECITEM_ZfreeItem(&possibleResult, PR_FALSE);
   376   return rv;
   377 }

mercurial