/* * Copyright (C) 2017 Denys Vlasenko * * Licensed under GPLv2, see file LICENSE in this source tree. */ #include "tls.h" /* The code below is taken from parts of * matrixssl-3-7-2b-open/crypto/pubkey/pkcs.c * matrixssl-3-7-2b-open/crypto/pubkey/rsa.c * and (so far) almost not modified. Changes are flagged with //bbox */ #define pkcs1Pad(in, inlen, out, outlen, cryptType, userPtr) \ pkcs1Pad(in, inlen, out, outlen, cryptType) static //bbox int32 pkcs1Pad(unsigned char *in, uint32 inlen, unsigned char *out, uint32 outlen, int32 cryptType, void *userPtr) { unsigned char *c; int32 randomLen; randomLen = outlen - 3 - inlen; if (randomLen < 8) { psTraceCrypto("pkcs1Pad failure\n"); return PS_LIMIT_FAIL; } c = out; *c = 0x00; c++; *c = (unsigned char)cryptType; c++; if (cryptType == PUBKEY_TYPE) { while (randomLen-- > 0) { *c++ = 0xFF; } } else { if (matrixCryptoGetPrngData(c, (uint32)randomLen, userPtr) < 0) { return PS_PLATFORM_FAIL; } /* SECURITY: Read through the random data and change all 0x0 to 0x01. This is per spec that no random bytes should be 0 */ while (randomLen-- > 0) { if (*c == 0x0) { *c = 0x01; } c++; } } *c = 0x00; c++; memcpy(c, in, inlen); return outlen; } #define psRsaCrypt(pool, in, inlen, out, outlen, key, type, data) \ psRsaCrypt( in, inlen, out, outlen, key, type) static //bbox int32 psRsaCrypt(psPool_t *pool, const unsigned char *in, uint32 inlen, unsigned char *out, uint32 *outlen, psRsaKey_t *key, int32 type, void *data) { pstm_int tmp, tmpa, tmpb; int32 res; uint32 x; //bbox // if (in == NULL || out == NULL || outlen == NULL || key == NULL) { // psTraceCrypto("NULL parameter error in psRsaCrypt\n"); // return PS_ARG_FAIL; // } tmp.dp = tmpa.dp = tmpb.dp = NULL; /* Init and copy into tmp */ if (pstm_init_for_read_unsigned_bin(pool, &tmp, inlen + sizeof(pstm_digit)) != PS_SUCCESS) { return PS_FAILURE; } if (pstm_read_unsigned_bin(&tmp, (unsigned char *)in, inlen) != PS_SUCCESS){ pstm_clear(&tmp); return PS_FAILURE; } /* Sanity check on the input */ if (pstm_cmp(&key->N, &tmp) == PSTM_LT) { res = PS_LIMIT_FAIL; goto done; } if (type == PRIVKEY_TYPE) { if (key->optimized) { if (pstm_init_size(pool, &tmpa, key->p.alloc) != PS_SUCCESS) { res = PS_FAILURE; goto done; } if (pstm_init_size(pool, &tmpb, key->q.alloc) != PS_SUCCESS) { pstm_clear(&tmpa); res = PS_FAILURE; goto done; } if (pstm_exptmod(pool, &tmp, &key->dP, &key->p, &tmpa) != PS_SUCCESS) { psTraceCrypto("decrypt error: pstm_exptmod dP, p\n"); goto error; } if (pstm_exptmod(pool, &tmp, &key->dQ, &key->q, &tmpb) != PS_SUCCESS) { psTraceCrypto("decrypt error: pstm_exptmod dQ, q\n"); goto error; } if (pstm_sub(&tmpa, &tmpb, &tmp) != PS_SUCCESS) { psTraceCrypto("decrypt error: sub tmpb, tmp\n"); goto error; } if (pstm_mulmod(pool, &tmp, &key->qP, &key->p, &tmp) != PS_SUCCESS) { psTraceCrypto("decrypt error: pstm_mulmod qP, p\n"); goto error; } if (pstm_mul_comba(pool, &tmp, &key->q, &tmp, NULL, 0) != PS_SUCCESS){ psTraceCrypto("decrypt error: pstm_mul q \n"); goto error; } if (pstm_add(&tmp, &tmpb, &tmp) != PS_SUCCESS) { psTraceCrypto("decrypt error: pstm_add tmp \n"); goto error; } } else { if (pstm_exptmod(pool, &tmp, &key->d, &key->N, &tmp) != PS_SUCCESS) { psTraceCrypto("psRsaCrypt error: pstm_exptmod\n"); goto error; } } } else if (type == PUBKEY_TYPE) { if (pstm_exptmod(pool, &tmp, &key->e, &key->N, &tmp) != PS_SUCCESS) { psTraceCrypto("psRsaCrypt error: pstm_exptmod\n"); goto error; } } else { psTraceCrypto("psRsaCrypt error: invalid type param\n"); goto error; } /* Read it back */ x = pstm_unsigned_bin_size(&key->N); if ((uint32)x > *outlen) { res = -1; psTraceCrypto("psRsaCrypt error: pstm_unsigned_bin_size\n"); goto done; } /* We want the encrypted value to always be the key size. Pad with 0x0 */ while ((uint32)x < (unsigned long)key->size) { *out++ = 0x0; x++; } *outlen = x; /* Convert it */ memset(out, 0x0, x); if (pstm_to_unsigned_bin(pool, &tmp, out+(x-pstm_unsigned_bin_size(&tmp))) != PS_SUCCESS) { psTraceCrypto("psRsaCrypt error: pstm_to_unsigned_bin\n"); goto error; } /* Clean up and return */ res = PS_SUCCESS; goto done; error: res = PS_FAILURE; done: if (type == PRIVKEY_TYPE && key->optimized) { //pstm_clear_multi(&tmpa, &tmpb, NULL, NULL, NULL, NULL, NULL, NULL); pstm_clear(&tmpa); pstm_clear(&tmpb); } pstm_clear(&tmp); return res; } int32 FAST_FUNC psRsaEncryptPub(psPool_t *pool, psRsaKey_t *key, unsigned char *in, uint32 inlen, unsigned char *out, uint32 outlen, void *data) { int32 err; uint32 size; size = key->size; if (outlen < size) { //bbox psTraceCrypto("Error on bad outlen parameter to psRsaEncryptPub\n"); bb_error_msg_and_die("RSA crypt outlen:%d < size:%d", outlen, size); return PS_ARG_FAIL; } if ((err = pkcs1Pad(in, inlen, out, size, PRIVKEY_TYPE, data)) < PS_SUCCESS) { psTraceCrypto("Error padding psRsaEncryptPub. Likely data too long\n"); return err; } if ((err = psRsaCrypt(pool, out, size, out, (uint32*)&outlen, key, PUBKEY_TYPE, data)) < PS_SUCCESS) { psTraceCrypto("Error performing psRsaEncryptPub\n"); return err; } if (outlen != size) { psTraceCrypto("Encrypted size error in psRsaEncryptPub\n"); return PS_FAILURE; } return size; } #if ENABLE_SSL_SERVER // || ENABLE_FEATURE_HTTPD_SSL #define psRsaEncryptPriv(pool, key, in, inlen, out, outlen, data) \ psRsaEncryptPriv( key, in, inlen, out, outlen) static //bbox int32 psRsaEncryptPriv(psPool_t *pool, psRsaKey_t *key, unsigned char *in, uint32 inlen, unsigned char *out, uint32 outlen, void *data) { int32 err; uint32 size; size = key->size; if (outlen < size) { psTraceCrypto("Error on bad outlen parameter to psRsaEncryptPriv\n"); return PS_ARG_FAIL; } if ((err = pkcs1Pad(in, inlen, out, size, PUBKEY_TYPE, data)) < PS_SUCCESS){ psTraceCrypto("Error padding psRsaEncryptPriv. Likely data too long\n"); return err; } if ((err = psRsaCrypt(pool, out, size, out, (uint32*)&outlen, key, PRIVKEY_TYPE, data)) < PS_SUCCESS) { psTraceCrypto("Error performing psRsaEncryptPriv\n"); return err; } if (outlen != size) { psTraceCrypto("Encrypted size error in psRsaEncryptPriv\n"); return PS_FAILURE; } return size; } #define ASN_OVERHEAD_LEN_RSA_SHA2 19 //#define ASN_OVERHEAD_LEN_RSA_SHA1 15 /* ASN.1 DigestInfo wrappers for hash algorithms */ static const unsigned char asn256dsWrap[] = {0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; //#ifdef USE_SHA384 //static const unsigned char asn384dsWrap[] = {0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, // 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}; //#endif //static const unsigned char asn1dsWrap[] = {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, // 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14}; int32 FAST_FUNC privRsaEncryptSignedElement(psPool_t *pool, psRsaKey_t *key, //bbox: was psPubKey_t unsigned char *in, uint32 inlen, unsigned char *out, uint32 outlen, void *data) { unsigned char *c; uint32 inlenWithAsn; int32 rc; if (inlen == 32) { //SHA256_HASH_SIZE inlenWithAsn = inlen + ASN_OVERHEAD_LEN_RSA_SHA2; c = psMalloc(pool, inlenWithAsn); memcpy(c, asn256dsWrap, ASN_OVERHEAD_LEN_RSA_SHA2); memcpy(c + ASN_OVERHEAD_LEN_RSA_SHA2, in, inlen); // } else if (inlen == SHA1_HASH_SIZE) { // inlenWithAsn = inlen + ASN_OVERHEAD_LEN_RSA_SHA1; // c = psMalloc(pool, inlenWithAsn); // memcpy(c, asn1dsWrap, ASN_OVERHEAD_LEN_RSA_SHA1); // memcpy(c + ASN_OVERHEAD_LEN_RSA_SHA1, in, inlen); //#ifdef USE_SHA384 // } else if (inlen == SHA384_HASH_SIZE) { // inlenWithAsn = inlen + ASN_OVERHEAD_LEN_RSA_SHA2; // c = psMalloc(pool, inlenWithAsn); // memcpy(c, asn384dsWrap, ASN_OVERHEAD_LEN_RSA_SHA2); // memcpy(c + ASN_OVERHEAD_LEN_RSA_SHA2, in, inlen); //#endif } else { return PS_UNSUPPORTED_FAIL; } rc = psRsaEncryptPriv(pool, key, c, inlenWithAsn, //bbox: was (psRsaKey_t*)key->key out, outlen, data); psFree(c, pool); return rc; } /* Remove PKCS#1 padding (Type 2) from decrypted data * Format: 00 || 02 || PS || 00 || M * Returns length of unpadded message, or negative on error */ #define pkcs1Unpad(in, inlen, out, outlen) \ pkcs1Unpad(in, inlen, out, outlen) static //bbox int32 pkcs1Unpad(unsigned char *in, uint32 inlen, unsigned char *out, uint32 outlen) { unsigned char *c, *end; uint32 msglen; if (inlen < 11) { /* Minimum: 00 02 [8 bytes PS] 00 */ psTraceCrypto("pkcs1Unpad: input too short\n"); return PS_FAILURE; } c = in; end = in + inlen; /* Check padding type byte */ if (*c++ != 0x00) { psTraceCrypto("pkcs1Unpad: bad first byte\n"); return PS_FAILURE; } if (*c++ != 0x02) { psTraceCrypto("pkcs1Unpad: bad padding type\n"); return PS_FAILURE; } /* Skip padding string (non-zero bytes) until we find 0x00 */ while (c < end && *c != 0x00) { c++; } if (c >= end) { psTraceCrypto("pkcs1Unpad: no 0x00 separator found\n"); return PS_FAILURE; } /* Skip the 0x00 separator */ c++; /* Calculate message length */ msglen = (uint32)(end - c); if (msglen > outlen) { psTraceCrypto("pkcs1Unpad: output buffer too small\n"); return PS_FAILURE; } /* Copy message to output */ memcpy(out, c, msglen); return msglen; } /* RSA private key decryption (PKCS#1 v1.5) * Decrypts with private key and removes PKCS#1 padding */ #define psRsaDecryptPriv(pool, key, in, inlen, out, outlen, data) \ psRsaDecryptPriv( key, in, inlen, out, outlen) int32 FAST_FUNC psRsaDecryptPriv(psPool_t *pool, psRsaKey_t *key, unsigned char *in, uint32 inlen, unsigned char *out, uint32 outlen, void *data) { int32 err; uint32 ptLen; if (inlen != key->size) { psTraceCrypto("Error on bad inlen parameter to psRsaDecryptPriv\n"); return PS_ARG_FAIL; } ptLen = inlen; if ((err = psRsaCrypt(pool, in, inlen, in, &ptLen, key, PRIVKEY_TYPE, data)) < PS_SUCCESS) { psTraceCrypto("Error performing psRsaDecryptPriv\n"); return err; } if (ptLen != inlen) { psTraceCrypto("Decrypted size error in psRsaDecryptPriv\n"); return PS_FAILURE; } err = pkcs1Unpad(in, inlen, out, outlen); memset(in, 0x0, inlen); return err; } #endif /* server */