// verify the signature in sig. The original (hash of the message) message is in digest int ccrsa_verify_pss(ccrsa_pub_ctx_t key, const struct ccdigest_info* di, const struct ccdigest_info* MgfDi, size_t digestSize, const uint8_t *digest, size_t sigSize, const uint8_t *sig, size_t saltSize, bool *valid) { const cc_size modBits =ccn_bitlen(ccrsa_ctx_n(key), ccrsa_ctx_m(key)); const cc_size modBytes = cc_ceiling(modBits, 8); const cc_size emBits = modBits-1; //as defined in §8.1.1 const cc_size emSize = cc_ceiling(emBits, 8); *valid = false; int rc=0; //1. if(modBytes!= sigSize) return CCRSA_INVALID_INPUT; if(digestSize != di->output_size) return CCRSA_INVALID_INPUT; //2. const cc_size modWords=ccrsa_ctx_n(key); cc_unit EM[modWords]; //EM islarge enough to fit sig variable //2.a read sig to tmp array and make sure it fits cc_require_action(ccn_read_uint(modWords, EM, sigSize, sig)==0,errOut,rc=CCRSA_INVALID_INPUT); //2.b cc_require((rc=ccrsa_pub_crypt(key, EM, EM))==0,errOut); //2.c ccn_swap(modWords, EM); //3 const size_t ofs = modWords*sizeof(cc_unit)-emSize; cc_assert(ofs<=sizeof(cc_unit)); //make sure sizes are consistent and we don't overrun buffers. rc|= ccrsa_emsa_pss_decode(di, MgfDi, saltSize, digestSize, digest, emBits, (uint8_t *) EM+ofs); *valid = (rc==0)?true:false; errOut: return rc; }
int ccrsa_eme_pkcs1v15_encode(struct ccrng_state *rng, size_t r_size, cc_unit *r, size_t s_size, const uint8_t *s) { cc_size n = ccrsa_n_from_size(r_size); uint8_t *out = ccrsa_block_start(r_size, r, 1); uint8_t *pad; size_t padlen; size_t i; for(uint8_t *p = (uint8_t *) r; p < out; p++) *p = 0; pad = out + 2; if ((r_size < 3) || ((r_size - 3) < s_size)) { return CCRSA_INVALID_INPUT; } padlen = r_size - s_size - 3; int result = ccrng_generate(rng, padlen, pad); if (result) { return result; } for(i=0; i<padlen; i++) { // pad can't have zero bytes while(pad[i] == 0) if((result = ccrng_generate(rng, 1, &pad[i])) != 0) return result; } out[0] = 0x00; out[1] = 0x02; out[2+padlen] = 0x00; CC_MEMCPY(out+3+padlen, s, s_size); ccn_swap(n, r); return 0; }
static OSStatus SecRSAPrivateKeyRawDecrypt(SecKeyRef key, SecPadding padding, const uint8_t *cipherText, size_t cipherTextLen, uint8_t *plainText, size_t *plainTextLen) { OSStatus result = errSSLCrypto; ccrsa_full_ctx_t fullkey; fullkey.full = key->key; size_t m_size = ccn_write_uint_size(ccrsa_ctx_n(fullkey), ccrsa_ctx_m(fullkey)); cc_unit s[ccrsa_ctx_n(fullkey)]; uint8_t recoveredData[ccn_sizeof_n(ccrsa_ctx_n(fullkey))]; ccn_read_uint(ccrsa_ctx_n(fullkey), s, cipherTextLen, cipherText); ccrsa_priv_crypt(ccrsa_ctx_private(fullkey), s, s); const uint8_t* sBytes = (uint8_t*) s; const uint8_t* sEnd = (uint8_t*) (s + ccrsa_ctx_n(fullkey)); require(plainTextLen, errOut); switch (padding) { case kSecPaddingNone: ccn_swap(ccrsa_ctx_n(fullkey), s); // Skip Zeros since our contract is to do so. while (sBytes < sEnd && *sBytes == 0x00) ++sBytes; break; case kSecPaddingPKCS1: { ccn_swap(ccrsa_ctx_n(fullkey), s); // Verify and skip PKCS1 padding: // // 0x00, 0x01 (RSA_PKCS1_PAD_ENCRYPT), 0xFF .. 0x00, signedData // size_t prefix_zeros = ccn_sizeof_n(ccrsa_ctx_n(fullkey)) - m_size; while (prefix_zeros--) require_quiet(*sBytes++ == 0x00, errOut); require_quiet(*sBytes++ == 0x00, errOut); require_quiet(*sBytes++ == RSA_PKCS1_PAD_ENCRYPT, errOut); while (*sBytes != 0x00) { require_quiet(++sBytes < sEnd, errOut); } // Required to have at least 8 non-zeros require_quiet((sBytes - (uint8_t*)s) - 2 >= 8, errOut); require_quiet(*sBytes == 0x00, errOut); require_quiet(++sBytes < sEnd, errOut); break; } case kSecPaddingOAEP: { size_t length = sizeof(recoveredData); require_noerr_quiet(ccrsa_oaep_decode(ccsha1_di(), ccn_write_uint_size(ccrsa_ctx_n(fullkey),ccrsa_ctx_m(fullkey)), s, &length, recoveredData), errOut); sBytes = recoveredData; sEnd = recoveredData + length; break; } default: goto errOut; } require((sEnd - sBytes) <= (ptrdiff_t)*plainTextLen, errOut); *plainTextLen = sEnd - sBytes; memcpy(plainText, sBytes, *plainTextLen); result = errSecSuccess; errOut: bzero(recoveredData, sizeof(recoveredData)); ccn_zero(ccrsa_ctx_n(fullkey), s); return result; }
static OSStatus SecRSAPrivateKeyRawSign(SecKeyRef key, SecPadding padding, const uint8_t *dataToSign, size_t dataToSignLen, uint8_t *sig, size_t *sigLen) { OSStatus result = errSecParam; ccrsa_full_ctx_t fullkey; fullkey.full = key->key; size_t m_size = ccn_write_uint_size(ccrsa_ctx_n(fullkey), ccrsa_ctx_m(fullkey)); cc_unit s[ccrsa_ctx_n(fullkey)]; uint8_t* sBytes = (uint8_t*) s; require(sigLen, errOut); require(*sigLen >= m_size, errOut); switch (padding) { case kSecPaddingNone: require_noerr_quiet(ccn_read_uint(ccrsa_ctx_n(fullkey), s, dataToSignLen, dataToSign), errOut); require_quiet(ccn_cmp(ccrsa_ctx_n(fullkey), s, ccrsa_ctx_m(fullkey)) < 0, errOut); break; case kSecPaddingPKCS1: { // Create PKCS1 padding: // // 0x00, 0x01 (RSA_PKCS1_PAD_SIGN), 0xFF .. 0x00, signedData // const int kMinimumPadding = 1 + 1 + 8 + 1; require(dataToSignLen < m_size - kMinimumPadding, errOut); size_t prefix_zeros = ccn_sizeof_n(ccrsa_ctx_n(fullkey)) - m_size; while (prefix_zeros--) *sBytes++ = 0x00; size_t pad_size = m_size - dataToSignLen; *sBytes++ = 0x00; *sBytes++ = RSA_PKCS1_PAD_SIGN; size_t ff_size; for(ff_size = pad_size - 3; ff_size > 0; --ff_size) *sBytes++ = 0xFF; *sBytes++ = 0x00; // Get the user data into s looking like a ccn. memcpy(sBytes, dataToSign, dataToSignLen); ccn_swap(ccrsa_ctx_n(fullkey), s); break; } case kSecPaddingOAEP: result = errSecParam; default: goto errOut; } ccrsa_priv_crypt(ccrsa_ctx_private(fullkey), s, s); // Pad with leading zeros to fit in modulus size ccn_write_uint_padded(ccrsa_ctx_n(fullkey), s, m_size, sig); *sigLen = m_size; result = errSecSuccess; errOut: ccn_zero(ccrsa_ctx_n(fullkey), s); return result; }
static OSStatus SecRSAPublicKeyRawDecrypt(SecKeyRef key, SecPadding padding, const uint8_t *cipherText, size_t cipherTextLen, uint8_t *plainText, size_t *plainTextLen) { OSStatus result = errSSLCrypto; ccrsa_pub_ctx_t pubkey; pubkey.pub = key->key; cc_unit s[ccrsa_ctx_n(pubkey)]; require_action_quiet(cipherText != NULL, errOut, result = errSecParam); require_action_quiet(plainText != NULL, errOut, result = errSecParam); require_action_quiet(plainTextLen != NULL, errOut, result = errSecParam); ccn_read_uint(ccrsa_ctx_n(pubkey), s, cipherTextLen, cipherText); ccrsa_pub_crypt(pubkey, s, s); ccn_swap(ccrsa_ctx_n(pubkey), s); const uint8_t* sBytes = (uint8_t*) s; const uint8_t* sEnd = (uint8_t*) (s + ccrsa_ctx_n(pubkey)); switch (padding) { case kSecPaddingNone: // Skip leading zeros // We return the bytes for a number and // trim leading zeroes while (sBytes < sEnd && *sBytes == 0x00) ++sBytes; break; case kSecPaddingPKCS1: { // Verify and skip PKCS1 padding: // // 0x00, 0x01 (RSA_PKCS1_PAD_ENCRYPT), 0xFF .. 0x00, signedData // size_t m_size = ccn_write_uint_size(ccrsa_ctx_n(pubkey), ccrsa_ctx_m(pubkey)); size_t prefix_zeros = ccn_sizeof_n(ccrsa_ctx_n(pubkey)) - m_size; while (prefix_zeros--) require_quiet(*sBytes++ == 0x00, errOut); require_quiet(*sBytes++ == 0x00, errOut); require_quiet(*sBytes++ == RSA_PKCS1_PAD_ENCRYPT, errOut); while (*sBytes != 0x00) { require_quiet(++sBytes < sEnd, errOut); } // Required to have at least 8 0xFFs require_quiet((sBytes - (uint8_t*)s) - 2 >= 8, errOut); require_quiet(*sBytes == 0x00, errOut); require_quiet(++sBytes < sEnd, errOut); break; } case kSecPaddingOAEP: result = errSecParam; default: goto errOut; } // Return the rest. require_action((sEnd - sBytes) <= (ptrdiff_t)*plainTextLen, errOut, result = errSecParam); *plainTextLen = sEnd - sBytes; memcpy(plainText, sBytes, *plainTextLen); result = errSecSuccess; errOut: ccn_zero(ccrsa_ctx_n(pubkey), s); return result; }
static OSStatus SecRSAPublicKeyRawEncrypt(SecKeyRef key, SecPadding padding, const uint8_t *plainText, size_t plainTextLen, uint8_t *cipherText, size_t *cipherTextLen) { OSStatus result = errSecParam; ccrsa_pub_ctx_t pubkey; pubkey.pub = key->key; cc_unit s[ccrsa_ctx_n(pubkey)]; const size_t m_size = ccn_write_uint_size(ccrsa_ctx_n(pubkey), ccrsa_ctx_m(pubkey)); require(cipherTextLen, errOut); require(*cipherTextLen >= m_size, errOut); uint8_t* sBytes = (uint8_t*) s; switch (padding) { case kSecPaddingNone: require_noerr_quiet(ccn_read_uint(ccrsa_ctx_n(pubkey), s, plainTextLen, plainText), errOut); require_quiet(ccn_cmp(ccrsa_ctx_n(pubkey), s, ccrsa_ctx_m(pubkey)) < 0, errOut); break; case kSecPaddingPKCS1: { // Create PKCS1 padding: // // 0x00, 0x01 (RSA_PKCS1_PAD_ENCRYPT), 0xFF .. 0x00, signedData // const int kMinimumPadding = 1 + 1 + 8 + 1; require_quiet(plainTextLen < m_size - kMinimumPadding, errOut); size_t prefix_zeros = ccn_sizeof_n(ccrsa_ctx_n(pubkey)) - m_size; while (prefix_zeros--) *sBytes++ = 0x00; size_t pad_size = m_size - plainTextLen; *sBytes++ = 0x00; *sBytes++ = RSA_PKCS1_PAD_ENCRYPT; ccrng_generate(ccrng_seckey, pad_size - 3, sBytes); // Remove zeroes from the random pad const uint8_t* sEndOfPad = sBytes + (pad_size - 3); while (sBytes < sEndOfPad) { if (*sBytes == 0x00) *sBytes = 0xFF; // Michael said 0xFF was good enough. ++sBytes; } *sBytes++ = 0x00; memcpy(sBytes, plainText, plainTextLen); ccn_swap(ccrsa_ctx_n(pubkey), s); break; } case kSecPaddingOAEP: { const struct ccdigest_info* di = ccsha1_di(); const size_t encodingOverhead = 2 + 2 * di->output_size; require_action(m_size > encodingOverhead, errOut, result = errSecParam); require_action_quiet(plainTextLen < m_size - encodingOverhead, errOut, result = errSSLCrypto); require_noerr_action(ccrsa_oaep_encode(di, ccrng_seckey, m_size, s, plainTextLen, plainText), errOut, result = errSecInternal); break; } default: goto errOut; } ccrsa_pub_crypt(pubkey, s, s); ccn_write_uint_padded(ccrsa_ctx_n(pubkey), s, m_size, cipherText); *cipherTextLen = m_size; result = errSecSuccess; errOut: ccn_zero(ccrsa_ctx_n(pubkey), s); return result; }
static OSStatus SecRSAPublicKeyRawVerify(SecKeyRef key, SecPadding padding, const uint8_t *signedData, size_t signedDataLen, const uint8_t *sig, size_t sigLen) { OSStatus result = errSSLCrypto; ccrsa_pub_ctx_t pubkey; pubkey.pub = key->key; cc_unit s[ccrsa_ctx_n(pubkey)]; ccn_read_uint(ccrsa_ctx_n(pubkey), s, sigLen, sig); ccrsa_pub_crypt(pubkey, s, s); ccn_swap(ccrsa_ctx_n(pubkey), s); const uint8_t* sBytes = (uint8_t*) s; const uint8_t* sEnd = (uint8_t*) (s + ccrsa_ctx_n(pubkey)); switch (padding) { case kSecPaddingNone: // Skip leading zeros as long as s is bigger than signedData. while (((ptrdiff_t)signedDataLen < (sEnd - sBytes)) && (*sBytes == 0)) ++sBytes; break; case kSecPaddingPKCS1: { // Verify and skip PKCS1 padding: // // 0x00, 0x01 (RSA_PKCS1_PAD_SIGN), 0xFF .. 0x00, signedData // size_t m_size = ccn_write_uint_size(ccrsa_ctx_n(pubkey), ccrsa_ctx_m(pubkey)); size_t prefix_zeros = ccn_sizeof_n(ccrsa_ctx_n(pubkey)) - m_size; while (prefix_zeros--) require_quiet(*sBytes++ == 0x00, errOut); require_quiet(*sBytes++ == 0x00, errOut); require_quiet(*sBytes++ == RSA_PKCS1_PAD_SIGN, errOut); while (*sBytes == 0xFF) { require_quiet(++sBytes < sEnd, errOut); } // Required to have at least 8 0xFFs require_quiet((sBytes - (uint8_t*)s) - 2 >= 8, errOut); require_quiet(*sBytes == 0x00, errOut); require_quiet(++sBytes < sEnd, errOut); break; } case kSecPaddingOAEP: result = errSecParam; goto errOut; default: result = errSecUnimplemented; goto errOut; } // Compare the rest. require_quiet((sEnd - sBytes) == (ptrdiff_t)signedDataLen, errOut); require_quiet(memcmp(sBytes, signedData, signedDataLen) == 0, errOut); result = errSecSuccess; errOut: cc_zero(ccrsa_ctx_n(pubkey), s); return result; }