summaryrefslogtreecommitdiff
path: root/chromium/net/android/keystore_openssl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/android/keystore_openssl.cc')
-rw-r--r--chromium/net/android/keystore_openssl.cc79
1 files changed, 65 insertions, 14 deletions
diff --git a/chromium/net/android/keystore_openssl.cc b/chromium/net/android/keystore_openssl.cc
index cc463f49539..cd55ece3336 100644
--- a/chromium/net/android/keystore_openssl.cc
+++ b/chromium/net/android/keystore_openssl.cc
@@ -35,7 +35,7 @@
//
// Generally speaking, OpenSSL provides many different ways to sign
// digests. This code doesn't support all these cases, only the ones that
-// are required to sign the MAC during the OpenSSL handshake for TLS < 1.2.
+// are required to sign the MAC during the OpenSSL handshake for TLS.
//
// The OpenSSL EVP_PKEY type is a generic wrapper around key pairs.
// Internally, it can hold a pointer to a RSA, DSA or ECDSA structure,
@@ -54,8 +54,8 @@
// are used to hold the typical modulus / exponent / parameters for the
// key pair).
//
-// This source file thus defines a custom RSA_METHOD structure, which
-// fields points to static methods used to implement the corresponding
+// This source file thus defines a custom RSA_METHOD structure whose
+// fields point to static methods used to implement the corresponding
// RSA operation using platform Android APIs.
//
// However, the platform APIs require a jobject JNI reference to work.
@@ -106,6 +106,7 @@ typedef crypto::ScopedOpenSSL<RSA, RSA_free> ScopedRSA;
typedef crypto::ScopedOpenSSL<DSA, DSA_free> ScopedDSA;
typedef crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> ScopedEC_KEY;
typedef crypto::ScopedOpenSSL<EC_GROUP, EC_GROUP_free> ScopedEC_GROUP;
+typedef crypto::ScopedOpenSSL<X509_SIG, X509_SIG_free> ScopedX509_SIG;
// Custom RSA_METHOD that uses the platform APIs.
// Note that for now, only signing through RSA_sign() is really supported.
@@ -172,28 +173,79 @@ int RsaMethodFinish(RSA* rsa) {
return 0;
}
+// Although these parameters are, per OpenSSL, named |message| and
+// |message_len|, RsaMethodSign is actually passed a message digest,
+// not the original message.
int RsaMethodSign(int type,
const unsigned char* message,
unsigned int message_len,
unsigned char* signature,
unsigned int* signature_len,
const RSA* rsa) {
- // This is only used for client certificate support, which
- // will always pass the NID_md5_sha1 |type| value.
- DCHECK_EQ(NID_md5_sha1, type);
- if (type != NID_md5_sha1) {
- RSAerr(RSA_F_RSA_SIGN, RSA_R_UNKNOWN_ALGORITHM_TYPE);
- return 0;
- }
// Retrieve private key JNI reference.
jobject private_key = reinterpret_cast<jobject>(RSA_get_app_data(rsa));
if (!private_key) {
LOG(WARNING) << "Null JNI reference passed to RsaMethodSign!";
return 0;
}
- // Sign message with it through JNI.
- base::StringPiece message_piece(reinterpret_cast<const char*>(message),
- static_cast<size_t>(message_len));
+
+ // See RSA_sign in third_party/openssl/openssl/crypto/rsa/rsa_sign.c.
+ base::StringPiece message_piece;
+ std::vector<uint8> buffer; // To store |message| wrapped in a DigestInfo.
+ if (type == NID_md5_sha1) {
+ // For TLS < 1.2, sign just |message|.
+ message_piece.set(message, static_cast<size_t>(message_len));
+ } else {
+ // For TLS 1.2, wrap |message| in a PKCS #1 DigestInfo before signing.
+ ScopedX509_SIG sig(X509_SIG_new());
+ if (!sig.get())
+ return 0;
+ if (X509_ALGOR_set0(sig.get()->algor,
+ OBJ_nid2obj(type), V_ASN1_NULL, 0) != 1) {
+ return 0;
+ }
+ if (sig.get()->algor->algorithm == NULL) {
+ RSAerr(RSA_F_RSA_SIGN, RSA_R_UNKNOWN_ALGORITHM_TYPE);
+ return 0;
+ }
+ if (sig.get()->algor->algorithm->length == 0) {
+ RSAerr(RSA_F_RSA_SIGN,
+ RSA_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD);
+ return 0;
+ }
+ if (ASN1_OCTET_STRING_set(sig.get()->digest, message, message_len) != 1)
+ return 0;
+
+ int len = i2d_X509_SIG(sig.get(), NULL);
+ if (len < 0) {
+ LOG(WARNING) << "Couldn't encode X509_SIG structure";
+ return 0;
+ }
+ buffer.resize(len);
+ // OpenSSL takes a pointer to a pointer so it can kindly increment
+ // it for you.
+ unsigned char* p = &buffer[0];
+ len = i2d_X509_SIG(sig.get(), &p);
+ if (len < 0) {
+ LOG(WARNING) << "Couldn't encode X509_SIG structure";
+ return 0;
+ }
+
+ message_piece.set(&buffer[0], static_cast<size_t>(len));
+ }
+
+ // Sanity-check the size.
+ //
+ // TODO(davidben): Do we need to do this? OpenSSL does, but
+ // RawSignDigestWithPrivateKey does error on sufficiently large
+ // input. However, it doesn't take the padding into account.
+ size_t expected_size = static_cast<size_t>(RSA_size(rsa));
+ if (message_piece.size() > expected_size - RSA_PKCS1_PADDING_SIZE) {
+ RSAerr(RSA_F_RSA_SIGN, RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY);
+ return 0;
+ }
+
+ // Sign |message_piece| with the private key through JNI.
std::vector<uint8> result;
if (!RawSignDigestWithPrivateKey(
@@ -202,7 +254,6 @@ int RsaMethodSign(int type,
return 0;
}
- size_t expected_size = static_cast<size_t>(RSA_size(rsa));
if (result.size() > expected_size) {
LOG(ERROR) << "RSA Signature size mismatch, actual: "
<< result.size() << ", expected <= " << expected_size;