tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

pk11sdr.c (12032B)


      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/. */
      4 
      5 #include "seccomon.h"
      6 #include "secoid.h"
      7 #include "secasn1.h"
      8 #include "pkcs11.h"
      9 #include "pk11func.h"
     10 #include "pk11sdr.h"
     11 
     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    SECItem keyid;
     18    SECAlgorithmID alg;
     19    SECItem data;
     20 };
     21 typedef struct SDRResult SDRResult;
     22 
     23 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
     24 
     25 static SEC_ASN1Template template[] = {
     26    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SDRResult) },
     27    { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, keyid) },
     28    { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(SDRResult, alg),
     29      SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
     30    { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, data) },
     31    { 0 }
     32 };
     33 
     34 static unsigned char keyID[] = {
     35    0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     36    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
     37 };
     38 
     39 static SECItem keyIDItem = {
     40    0,
     41    keyID,
     42    sizeof keyID
     43 };
     44 
     45 /* local utility function for padding an incoming data block
     46 * to the mechanism block size.
     47 */
     48 static SECStatus
     49 padBlock(SECItem *data, int blockSize, SECItem *result)
     50 {
     51    SECStatus rv = SECSuccess;
     52    int padLength;
     53    unsigned int i;
     54 
     55    result->data = 0;
     56    result->len = 0;
     57 
     58    /* This algorithm always adds to the block (to indicate the number
     59     * of pad bytes).  So allocate a block large enough.
     60     */
     61    padLength = blockSize - (data->len % blockSize);
     62    result->len = data->len + padLength;
     63    result->data = (unsigned char *)PORT_Alloc(result->len);
     64 
     65    /* Copy the data */
     66    PORT_Memcpy(result->data, data->data, data->len);
     67 
     68    /* Add the pad values */
     69    for (i = data->len; i < result->len; i++)
     70        result->data[i] = (unsigned char)padLength;
     71 
     72    return rv;
     73 }
     74 
     75 static SECStatus
     76 unpadBlock(SECItem *data, int blockSize, SECItem *result)
     77 {
     78    SECStatus rv = SECSuccess;
     79    int padLength;
     80    unsigned int i;
     81 
     82    result->data = 0;
     83    result->len = 0;
     84 
     85    /* Remove the padding from the end if the input data */
     86    if (data->len == 0 || data->len % blockSize != 0) {
     87        rv = SECFailure;
     88        goto loser;
     89    }
     90 
     91    padLength = data->data[data->len - 1];
     92    if (padLength > blockSize) {
     93        rv = SECFailure;
     94        goto loser;
     95    }
     96 
     97    /* verify padding */
     98    for (i = data->len - padLength; i < data->len; i++) {
     99        if (data->data[i] != padLength) {
    100            rv = SECFailure;
    101            goto loser;
    102        }
    103    }
    104 
    105    result->len = data->len - padLength;
    106    result->data = (unsigned char *)PORT_Alloc(result->len);
    107    if (!result->data) {
    108        rv = SECFailure;
    109        goto loser;
    110    }
    111 
    112    PORT_Memcpy(result->data, data->data, result->len);
    113 
    114    if (padLength < 2) {
    115        return SECWouldBlock;
    116    }
    117 
    118 loser:
    119    return rv;
    120 }
    121 
    122 static PRLock *pk11sdrLock = NULL;
    123 
    124 void
    125 pk11sdr_Init(void)
    126 {
    127    pk11sdrLock = PR_NewLock();
    128 }
    129 
    130 void
    131 pk11sdr_Shutdown(void)
    132 {
    133    if (pk11sdrLock) {
    134        PR_DestroyLock(pk11sdrLock);
    135        pk11sdrLock = NULL;
    136    }
    137 }
    138 
    139 /*
    140 * PK11SDR_Encrypt
    141 *  Deprecated version of PK11SDR_EncryptWithMechanism using DES3_CBC.
    142 */
    143 SECStatus
    144 PK11SDR_Encrypt(SECItem *keyid, SECItem *data, SECItem *result, void *cx)
    145 {
    146    return PK11SDR_EncryptWithMechanism(NULL, keyid, CKM_DES3_CBC, data, result, cx);
    147 }
    148 
    149 /*
    150 * PK11SDR_EncryptWithMechanism
    151 *  Encrypt a block of data using the symmetric key identified and the
    152 *  encryption mechanism specified (only AES_CBC and DES3_CBC are supported).
    153 *  The result is an ASN.1 (DER) encoded block of keyid, params and data.
    154 */
    155 SECStatus
    156 PK11SDR_EncryptWithMechanism(PK11SlotInfo *slot, SECItem *keyid, CK_MECHANISM_TYPE type, SECItem *data, SECItem *result, void *cx)
    157 {
    158    SECStatus rv = SECSuccess;
    159    PK11SymKey *key = 0;
    160    SECItem *params = 0;
    161    PK11Context *ctx = 0;
    162    SDRResult sdrResult;
    163    SECItem paddedData;
    164    SECItem *pKeyID;
    165    PLArenaPool *arena = 0;
    166    SECOidTag algtag;
    167    PK11SlotInfo *aSlot = slot;
    168 
    169    /* Initialize */
    170    paddedData.len = 0;
    171    paddedData.data = 0;
    172 
    173    arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
    174    if (!arena) {
    175        rv = SECFailure;
    176        goto loser;
    177    }
    178 
    179    /* 1. Locate the requested keyid, or the default key (which has a keyid)
    180     * 2. Create an encryption context
    181     * 3. Encrypt
    182     * 4. Encode the results (using ASN.1)
    183     */
    184 
    185    if (!slot) {
    186        slot = PK11_GetInternalKeySlot();
    187        if (!slot) {
    188            rv = SECFailure;
    189            goto loser;
    190        }
    191    }
    192 
    193    /*
    194     * Login to the internal token before we look for the key, otherwise we
    195     * won't find it.
    196     */
    197    rv = PK11_Authenticate(slot, PR_TRUE, cx);
    198    if (rv != SECSuccess)
    199        goto loser;
    200 
    201    /* Find the key to use */
    202    pKeyID = keyid;
    203    if (pKeyID->len == 0) {
    204        int keySize = PK11_GetBestKeyLength(slot, type);
    205        pKeyID = &keyIDItem;
    206 
    207        /* put in a course lock to prevent a race between not finding the
    208         * key and creating  one.
    209         */
    210 
    211        if (pk11sdrLock)
    212            PR_Lock(pk11sdrLock);
    213 
    214        /* Try to find the key */
    215        key = PK11_FindFixedKey(slot, type, pKeyID, cx);
    216 
    217        /* If the default key doesn't exist yet, try to create it */
    218        if (!key)
    219            key = PK11_TokenKeyGen(slot, type, 0, keySize, pKeyID, PR_TRUE, cx);
    220        if (pk11sdrLock)
    221            PR_Unlock(pk11sdrLock);
    222    } else {
    223        key = PK11_FindFixedKey(slot, type, pKeyID, cx);
    224    }
    225 
    226    if (!key) {
    227        rv = SECFailure;
    228        goto loser;
    229    }
    230 
    231    params = PK11_GenerateNewParam(type, key);
    232    if (!params) {
    233        rv = SECFailure;
    234        goto loser;
    235    }
    236 
    237    ctx = PK11_CreateContextBySymKey(type, CKA_ENCRYPT, key, params);
    238    if (!ctx) {
    239        rv = SECFailure;
    240        goto loser;
    241    }
    242 
    243    rv = padBlock(data, PK11_GetBlockSize(type, 0), &paddedData);
    244    if (rv != SECSuccess)
    245        goto loser;
    246 
    247    sdrResult.data.len = paddedData.len;
    248    sdrResult.data.data = (unsigned char *)PORT_ArenaAlloc(arena, sdrResult.data.len);
    249 
    250    rv = PK11_CipherOp(ctx, sdrResult.data.data, (int *)&sdrResult.data.len, sdrResult.data.len,
    251                       paddedData.data, paddedData.len);
    252    if (rv != SECSuccess)
    253        goto loser;
    254 
    255    PK11_Finalize(ctx);
    256 
    257    sdrResult.keyid = *pKeyID;
    258 
    259    algtag = SECOID_FindOIDByMechanism(type)->offset;
    260    rv = PK11_ParamToAlgid(algtag, params, arena, &sdrResult.alg);
    261    if (rv != SECSuccess)
    262        goto loser;
    263 
    264    if (!SEC_ASN1EncodeItem(0, result, &sdrResult, template)) {
    265        rv = SECFailure;
    266        goto loser;
    267    }
    268 
    269 loser:
    270    SECITEM_ZfreeItem(&paddedData, PR_FALSE);
    271    if (arena)
    272        PORT_FreeArena(arena, PR_TRUE);
    273    if (ctx)
    274        PK11_DestroyContext(ctx, PR_TRUE);
    275    if (params)
    276        SECITEM_ZfreeItem(params, PR_TRUE);
    277    if (key)
    278        PK11_FreeSymKey(key);
    279    if (slot && !aSlot)
    280        PK11_FreeSlot(slot);
    281 
    282    return rv;
    283 }
    284 
    285 /* decrypt a block */
    286 static SECStatus
    287 pk11Decrypt(PK11SlotInfo *slot, PLArenaPool *arena,
    288            CK_MECHANISM_TYPE type, PK11SymKey *key,
    289            SECItem *params, SECItem *in, SECItem *result)
    290 {
    291    PK11Context *ctx = 0;
    292    SECItem paddedResult;
    293    SECStatus rv;
    294 
    295    paddedResult.len = 0;
    296    paddedResult.data = 0;
    297 
    298    ctx = PK11_CreateContextBySymKey(type, CKA_DECRYPT, key, params);
    299    if (!ctx) {
    300        rv = SECFailure;
    301        goto loser;
    302    }
    303 
    304    paddedResult.len = in->len;
    305    paddedResult.data = PORT_ArenaAlloc(arena, paddedResult.len);
    306 
    307    rv = PK11_CipherOp(ctx, paddedResult.data,
    308                       (int *)&paddedResult.len, paddedResult.len,
    309                       in->data, in->len);
    310    if (rv != SECSuccess)
    311        goto loser;
    312 
    313    PK11_Finalize(ctx);
    314 
    315    /* Remove the padding */
    316    rv = unpadBlock(&paddedResult, PK11_GetBlockSize(type, 0), result);
    317    if (rv)
    318        goto loser;
    319 
    320 loser:
    321    if (ctx)
    322        PK11_DestroyContext(ctx, PR_TRUE);
    323    return rv;
    324 }
    325 
    326 /*
    327 * PK11SDR_Decrypt
    328 *  Decrypt a block of data produced by PK11SDR_EncryptWithMechanism. The key
    329 *  used is identified by the keyid field within the input.
    330 */
    331 SECStatus
    332 PK11SDR_Decrypt(SECItem *data, SECItem *result, void *cx)
    333 {
    334    SECStatus rv = SECSuccess;
    335    PK11SlotInfo *slot = 0;
    336    PK11SymKey *key = 0;
    337    CK_MECHANISM_TYPE type;
    338    SDRResult sdrResult;
    339    SECItem *params = 0;
    340    SECItem possibleResult = { 0, NULL, 0 };
    341    PLArenaPool *arena = 0;
    342    SECOidTag algtag;
    343 
    344    arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
    345    if (!arena) {
    346        rv = SECFailure;
    347        goto loser;
    348    }
    349 
    350    /* Decode the incoming data */
    351    memset(&sdrResult, 0, sizeof sdrResult);
    352    rv = SEC_QuickDERDecodeItem(arena, &sdrResult, template, data);
    353    if (rv != SECSuccess)
    354        goto loser; /* Invalid format */
    355 
    356    /* Find the slot and key for the given keyid */
    357    slot = PK11_GetInternalKeySlot();
    358    if (!slot) {
    359        rv = SECFailure;
    360        goto loser;
    361    }
    362 
    363    rv = PK11_Authenticate(slot, PR_TRUE, cx);
    364    if (rv != SECSuccess)
    365        goto loser;
    366 
    367    /* Get the parameter values from the data */
    368    params = PK11_ParamFromAlgid(&sdrResult.alg);
    369    if (!params) {
    370        rv = SECFailure;
    371        goto loser;
    372    }
    373 
    374    algtag = SECOID_GetAlgorithmTag(&sdrResult.alg);
    375    type = PK11_AlgtagToMechanism(algtag);
    376    key = PK11_FindFixedKey(slot, type, &sdrResult.keyid, cx);
    377    if (!key) {
    378        rv = SECFailure;
    379    } else {
    380        rv = pk11Decrypt(slot, arena, type, key, params,
    381                         &sdrResult.data, result);
    382    }
    383 
    384    /*
    385     * if the pad value was too small (1 or 2), then it's statistically
    386     * 'likely' that (1 in 256) that we may not have the correct key.
    387     * Check the other keys for a better match. If we find none, use
    388     * this result.
    389     */
    390    if (rv == SECWouldBlock) {
    391        possibleResult = *result;
    392    }
    393 
    394    /*
    395     * handle the case where your key indicies may have been broken
    396     */
    397    if (rv != SECSuccess) {
    398        PK11SymKey *keyList = PK11_ListFixedKeysInSlot(slot, NULL, cx);
    399        PK11SymKey *testKey = NULL;
    400        PK11SymKey *nextKey = NULL;
    401 
    402        for (testKey = keyList; testKey;
    403             testKey = PK11_GetNextSymKey(testKey)) {
    404            if (PK11_GetSymKeyType(testKey) != PK11_GetKeyType(type, 0)) {
    405                continue;
    406            }
    407 
    408            rv = pk11Decrypt(slot, arena, type, testKey, params,
    409                             &sdrResult.data, result);
    410            if (rv == SECSuccess) {
    411                break;
    412            }
    413            /* found a close match. If it's our first remember it */
    414            if (rv == SECWouldBlock) {
    415                if (possibleResult.data) {
    416                    /* this is unlikely but possible. If we hit this condition,
    417                     * we have no way of knowing which possibility to prefer.
    418                     * in this case we just match the key the application
    419                     * thought was the right one */
    420                    SECITEM_ZfreeItem(result, PR_FALSE);
    421                } else {
    422                    possibleResult = *result;
    423                }
    424            }
    425        }
    426 
    427        /* free the list */
    428        for (testKey = keyList; testKey; testKey = nextKey) {
    429            nextKey = PK11_GetNextSymKey(testKey);
    430            PK11_FreeSymKey(testKey);
    431        }
    432    }
    433 
    434    /* we didn't find a better key, use the one with a small pad value */
    435    if ((rv != SECSuccess) && (possibleResult.data)) {
    436        *result = possibleResult;
    437        possibleResult.data = NULL;
    438        rv = SECSuccess;
    439    }
    440 
    441 loser:
    442    if (arena)
    443        PORT_FreeArena(arena, PR_TRUE);
    444    if (key)
    445        PK11_FreeSymKey(key);
    446    if (params)
    447        SECITEM_ZfreeItem(params, PR_TRUE);
    448    if (slot)
    449        PK11_FreeSlot(slot);
    450    if (possibleResult.data)
    451        SECITEM_ZfreeItem(&possibleResult, PR_FALSE);
    452 
    453    return rv;
    454 }