136 lines
4.0 KiB
C
136 lines
4.0 KiB
C
/* Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
|
source code may only be used and distributed under the Widevine
|
|
License Agreement. */
|
|
|
|
#include "rsa_util.h"
|
|
|
|
#include "oemcrypto_key_types.h"
|
|
#include "openssl/bio.h"
|
|
#include "openssl/err.h"
|
|
#include "openssl/rsa.h"
|
|
#include "openssl/sha.h"
|
|
#include "openssl/x509.h"
|
|
#include "wtpi_abort_interface.h"
|
|
#include "wtpi_logging_interface.h"
|
|
|
|
void dump_ssl_error(void) {
|
|
int count = 0;
|
|
unsigned long err;
|
|
while ((err = ERR_get_error())) {
|
|
count++;
|
|
char buffer[120];
|
|
ERR_error_string_n((int)err, buffer, sizeof(buffer));
|
|
LOGE("SSL Error %d -- %lu -- %s", count, err, buffer);
|
|
}
|
|
}
|
|
|
|
bool CheckRSAKey(const RSA* rsa) {
|
|
ABORT_IF(rsa == NULL, "rsa is NULL");
|
|
switch (RSA_check_key(rsa)) {
|
|
case 1: /* valid. */
|
|
return true;
|
|
case 0: /* not valid. */
|
|
LOGE("RSA key not valid");
|
|
dump_ssl_error();
|
|
return false;
|
|
default: /* -1 == check failed. */
|
|
LOGE("Error checking RSA key");
|
|
dump_ssl_error();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool DeserializePKCS8PrivateKey(const uint8_t* serialized_bytes, size_t size,
|
|
RSA** rsa) {
|
|
ABORT_IF(serialized_bytes == NULL || size <= 0 || rsa == NULL,
|
|
"Parameters are NULL or 0");
|
|
BIO* bio = BIO_new_mem_buf(serialized_bytes, (int)size);
|
|
if (bio == NULL) {
|
|
LOGE("Could not allocate bio buffer");
|
|
return false;
|
|
}
|
|
bool success = false;
|
|
EVP_PKEY* evp = NULL;
|
|
PKCS8_PRIV_KEY_INFO* pkcs8_pki = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL);
|
|
if (pkcs8_pki == NULL) {
|
|
LOGE("d2i_PKCS8_PRIV_KEY_INFO_bio returned NULL");
|
|
dump_ssl_error();
|
|
goto cleanup;
|
|
}
|
|
evp = EVP_PKCS82PKEY(pkcs8_pki);
|
|
if (evp == NULL) {
|
|
LOGE("EVP_PKCS82PKEY returned NULL");
|
|
dump_ssl_error();
|
|
goto cleanup;
|
|
}
|
|
*rsa = EVP_PKEY_get1_RSA(evp);
|
|
if (*rsa == NULL) {
|
|
LOGE("PrivateKeyInfo did not contain an RSA key");
|
|
goto cleanup;
|
|
}
|
|
success = true;
|
|
|
|
cleanup:
|
|
if (evp != NULL) {
|
|
EVP_PKEY_free(evp);
|
|
}
|
|
if (pkcs8_pki != NULL) {
|
|
PKCS8_PRIV_KEY_INFO_free(pkcs8_pki);
|
|
}
|
|
BIO_free(bio);
|
|
return success;
|
|
}
|
|
|
|
bool RSASignSSAPSSSHA1(RSA* rsa, const uint8_t* message, size_t message_length,
|
|
uint8_t* signature, size_t* signature_length) {
|
|
ABORT_IF(rsa == NULL || message == NULL || message_length <= 0 ||
|
|
signature == NULL || signature_length == NULL,
|
|
"Parameters are NULL or 0");
|
|
size_t private_key_size = RSA_size(rsa);
|
|
ABORT_IF(*signature_length < private_key_size,
|
|
"signature_length is not long enough");
|
|
/* Hash the message using SHA1. */
|
|
uint8_t hash[SHA_DIGEST_LENGTH];
|
|
if (!SHA1(message, message_length, hash)) {
|
|
dump_ssl_error();
|
|
return false;
|
|
}
|
|
/* Add PSS padding. */
|
|
ABORT_IF(private_key_size > KEY_SIZE_3072, "rsa size is too large");
|
|
uint8_t padding[KEY_SIZE_3072];
|
|
const int kPssSaltLength = 20;
|
|
int status = RSA_padding_add_PKCS1_PSS_mgf1(rsa, padding, hash, EVP_sha1(),
|
|
NULL, kPssSaltLength);
|
|
if (status == 0) {
|
|
dump_ssl_error();
|
|
return false;
|
|
}
|
|
|
|
/* Encrypt PSS padded digest. */
|
|
int bytes_written = RSA_private_encrypt(private_key_size, padding, signature,
|
|
rsa, RSA_NO_PADDING);
|
|
if (bytes_written == -1) {
|
|
dump_ssl_error();
|
|
return false;
|
|
}
|
|
*signature_length = bytes_written;
|
|
return true;
|
|
}
|
|
|
|
bool RSAPrivateDecrypt(RSA* rsa, const uint8_t* in, size_t in_length,
|
|
uint8_t* out, size_t* out_length) {
|
|
ABORT_IF(rsa == NULL || in == NULL || in_length <= 0 || out == NULL ||
|
|
out_length == NULL,
|
|
"Parameters are NULL or 0");
|
|
size_t private_key_size = RSA_size(rsa);
|
|
ABORT_IF(*out_length < private_key_size, "out_length is not long enough");
|
|
int decrypted_size =
|
|
RSA_private_decrypt(in_length, in, out, rsa, RSA_PKCS1_OAEP_PADDING);
|
|
if (decrypted_size == -1) {
|
|
dump_ssl_error();
|
|
return false;
|
|
}
|
|
*out_length = decrypted_size;
|
|
return true;
|
|
}
|