| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/devtools/device/usb/android_rsa.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <array> |
| #include <string_view> |
| |
| #include "base/base64.h" |
| #include "base/check.h" |
| #include "base/containers/span.h" |
| #include "base/containers/span_writer.h" |
| #include "base/numerics/byte_conversions.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/pref_names.h" |
| #include "components/sync_preferences/pref_service_syncable.h" |
| #include "crypto/keypair.h" |
| #include "third_party/boringssl/src/include/openssl/bn.h" |
| #include "third_party/boringssl/src/include/openssl/evp.h" |
| #include "third_party/boringssl/src/include/openssl/rsa.h" |
| |
| namespace { |
| |
| // The Android RSA format is fixed-width and can only represent 2048-bit RSA. |
| constexpr size_t kRSAModulusBytes = 2048 / 8; |
| |
| // The Android RSA format is 524 bytes in total: |
| // - 4 bytes, little-endian: length of n in number of u32s, must be 64 |
| // - 4 bytes, little-endian: precomputed -1 / n[0] mod 2^32 (unused in modern |
| // Android) |
| // - 256 bytes, little-endian: modulus |
| // - 256 bytes, little-endian: precomputed R^2 (unused in modern Android) |
| // - 4 bytes, little-endian: public exponent |
| constexpr size_t kAndroidRSASize = 524; |
| |
| // http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm |
| // a * x + b * y = gcd(a, b) = d |
| void ExtendedEuclid(uint64_t a, |
| uint64_t b, |
| uint64_t* x, |
| uint64_t* y, |
| uint64_t* d) { |
| uint64_t x1 = 0, x2 = 1, y1 = 1, y2 = 0; |
| |
| while (b > 0) { |
| uint64_t q = a / b; |
| uint64_t r = a % b; |
| *x = x2 - q * x1; |
| *y = y2 - q * y1; |
| a = b; |
| b = r; |
| x2 = x1; |
| x1 = *x; |
| y2 = y1; |
| y1 = *y; |
| } |
| |
| *d = a; |
| *x = x2; |
| *y = y2; |
| } |
| |
| uint32_t ModInverse2_32(uint32_t a) { |
| CHECK_EQ(a & 1u, 1u); // a must be odd |
| uint64_t d, x, y; |
| ExtendedEuclid(a, 0x100000000, &x, &y, &d); |
| CHECK_EQ(d, 1u); // If a is odd, there is an inverse. |
| return static_cast<uint32_t>(x); |
| } |
| |
| bool WriteLittleEndianBignum(const BIGNUM* bn, base::span<uint8_t> out) { |
| return BN_bn2le_padded(out.data(), out.size(), bn); |
| } |
| |
| } // namespace |
| |
| crypto::keypair::PrivateKey AndroidRSAPrivateKey(Profile* profile) { |
| std::string encoded_key = |
| profile->GetPrefs()->GetString(prefs::kDevToolsAdbKey); |
| std::string decoded_key; |
| std::optional<crypto::keypair::PrivateKey> key; |
| if (!encoded_key.empty() && base::Base64Decode(encoded_key, &decoded_key)) { |
| key = crypto::keypair::PrivateKey::FromPrivateKeyInfo( |
| base::as_byte_span(decoded_key)); |
| } |
| if (!key) { |
| key = crypto::keypair::PrivateKey::GenerateRsa2048(); |
| profile->GetPrefs()->SetString(prefs::kDevToolsAdbKey, |
| base::Base64Encode(key->ToPrivateKeyInfo())); |
| } |
| return *key; |
| } |
| |
| std::optional<std::string> AndroidRSAPublicKey( |
| crypto::keypair::PrivateKey key) { |
| // Assemble Android's custom RSA format. This format dates to when Android was |
| // using a custom "minicrypt" RSA implementation and was just minicrypt's |
| // in-memory representation. The format assumes 2048-bit RSA (up to byte |
| // precision) and also includes precomputed information for Montgomery |
| // reduction with 32-bit words. |
| // |
| // This precomputed information no longer makes sense with modern 64-bit |
| // processors, and does not contain quite enough information for 64-bit |
| // Montgomery reduction. Starting Android O, it no longer looks at it at all. |
| // See https://r.android.com/212780 and https://r.android.com/212781. |
| // |
| // However, that information is still hashed into existing ADB key |
| // fingerprints, so continue computing them to keep the fingerprint stable. |
| RSA* rsa = EVP_PKEY_get0_RSA(key.key()); |
| uint64_t e; |
| if (RSA_size(rsa) != kRSAModulusBytes || // |
| !BN_get_u64(RSA_get0_e(rsa), &e) || |
| !base::IsValueInRangeForNumericType<uint32_t>(e)) { |
| return std::nullopt; |
| } |
| |
| std::array<uint8_t, kAndroidRSASize> out; |
| auto writer = base::SpanWriter(base::span(out)); |
| writer.WriteU32LittleEndian(kRSAModulusBytes / 4); |
| // Reserve space for ninv. We'll compute it after we've written N. |
| auto ninv = *writer.Skip<4>(); |
| auto n = *writer.Skip<kRSAModulusBytes>(); |
| CHECK(WriteLittleEndianBignum(RSA_get0_n(rsa), n)); |
| // Fill in RR, or 2^4096 mod N. |
| bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new()); |
| bssl::UniquePtr<BIGNUM> rr(BN_new()); |
| CHECK(BN_set_bit(rr.get(), 4096)); |
| CHECK(BN_mod(rr.get(), rr.get(), RSA_get0_n(rsa), ctx.get())); |
| CHECK(WriteLittleEndianBignum(rr.get(), *writer.Skip<kRSAModulusBytes>())); |
| writer.WriteU32LittleEndian(base::checked_cast<uint32_t>(e)); |
| |
| // Compute ninv = -1 / n[0] mod 2^32. This value being 32-bit makes the |
| // pre-computed Montgomery values useless on a modern system. A 64-bit |
| // Montgomery reduction needs it mod 2^64. But we compute it anyway. The mod |
| // inverse cannot fail because BoringSSL will ensure N is odd. |
| uint32_t n0 = base::U32FromLittleEndian(n.first<4>()); |
| ninv.copy_from(base::U32ToLittleEndian(0u - ModInverse2_32(n0))); |
| |
| // Make sure we've written everything. |
| CHECK_EQ(writer.remaining(), 0u); |
| return base::Base64Encode(out); |
| } |
| |
| std::string AndroidRSASign(crypto::keypair::PrivateKey key, |
| const std::string& body) { |
| RSA* rsa = EVP_PKEY_get0_RSA(key.key()); |
| if (!rsa) { |
| return std::string(); |
| } |
| |
| std::string result(RSA_size(rsa), 0); |
| unsigned int len = 0; |
| |
| auto body_bytes = base::as_byte_span(body); |
| auto result_bytes = base::as_writable_byte_span(result); |
| |
| // The ADB protocol requires us to sign a 20-byte challenge, and assumes the |
| // challenge is a pre-hashed SHA-1 digest, although there is no guarantee that |
| // that is true, and signs it without further hashing. In general this is not |
| // a secure signature scheme and should not be used elsewhere. |
| if (!RSA_sign(NID_sha1, body_bytes.data(), body_bytes.size(), |
| result_bytes.data(), &len, rsa)) { |
| return std::string(); |
| } |
| result.resize(len); |
| return result; |
| } |