// Copyright 2014 The Chromium Authors. All rights reserved.
// 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 <string.h>

#include <limits>
#include <memory>

#include "base/base64.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "crypto/rsa_private_key.h"
#include "crypto/signature_creator.h"
#include "net/cert/asn1_util.h"

namespace {

const size_t kRSANumWords = 64;
const size_t kBigIntSize = 1024;

static const char kDummyRSAPublicKey[] =
    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6OSJ64q+ZLg7VV2ojEPh5TRbYjwbT"
    "TifSPeFIV45CHnbTWYiiIn41wrozpYizNsMWZUBjdah1N78WVhbyDrnr0bDgFp+gXjfVppa3I"
    "gjiohEcemK3omXi3GDMK8ERhriLUKfQS842SXtQ8I+KoZtpCkGM//0h7+P+Rhm0WwdipIRMhR"
    "8haNAeyDiiCvqJcvevv2T52vqKtS3aWz+GjaTJJLVWydEpz9WdvWeLfFVhe2ZnqwwZNa30Qoj"
    "fsnvjaMwK2MU7uYfRBPuvLyK5QESWBpArNDd6ULl8Y+NU6kwNOVDc87OASCVEM1gw2IMi2mo2"
    "WO5ywp0UWRiGZCkK+wOFQIDAQAB";

typedef struct RSAPublicKey {
  int len;                    // Length of n[] in number of uint32_t
  uint32_t n0inv;             // -1 / n[0] mod 2^32
  uint32_t n[kRSANumWords];   // modulus as little endian array
  uint32_t rr[kRSANumWords];  // R^2 as little endian array
  int exponent;               // 3 or 65537
} RSAPublicKey;

// 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 ModInverse(uint64_t a, uint64_t m) {
  uint64_t d, x, y;
  ExtendedEuclid(a, m, &x, &y, &d);
  if (d == 1)
    return static_cast<uint32_t>(x);
  return 0;
}

uint32_t* BnNew() {
  uint32_t* result = new uint32_t[kBigIntSize];
  memset(result, 0, kBigIntSize * sizeof(uint32_t));
  return result;
}

void BnFree(uint32_t* a) {
  delete[] a;
}

uint32_t* BnCopy(uint32_t* a) {
  uint32_t* result = new uint32_t[kBigIntSize];
  memcpy(result, a, kBigIntSize * sizeof(uint32_t));
  return result;
}

uint32_t* BnMul(uint32_t* a, uint32_t b) {
  uint32_t* result = BnNew();
  uint64_t carry_over = 0;
  for (size_t i = 0; i < kBigIntSize; ++i) {
    carry_over += static_cast<uint64_t>(a[i]) * b;
    result[i] = carry_over & std::numeric_limits<uint32_t>::max();
    carry_over >>= 32;
  }
  return result;
}

void BnSub(uint32_t* a, uint32_t* b) {
  int carry_over = 0;
  for (size_t i = 0; i < kBigIntSize; ++i) {
    int64_t sub = static_cast<int64_t>(a[i]) - b[i] - carry_over;
    carry_over = 0;
    if (sub < 0) {
      carry_over = 1;
      sub += 0x100000000LL;
    }
    a[i] = static_cast<uint32_t>(sub);
  }
}

void BnLeftShift(uint32_t* a, int offset) {
  for (int i = kBigIntSize - offset - 1; i >= 0; --i)
    a[i + offset] = a[i];
  for (int i = 0; i < offset; ++i)
    a[i] = 0;
}

int BnCompare(uint32_t* a, uint32_t* b) {
  for (int i = kBigIntSize - 1; i >= 0; --i) {
    if (a[i] > b[i])
      return 1;
    if (a[i] < b[i])
      return -1;
  }
  return 0;
}

uint64_t BnGuess(uint32_t* a, uint32_t* b, uint64_t from, uint64_t to) {
  if (from + 1 >= to)
    return from;

  uint64_t guess = (from + to) / 2;
  uint32_t* t = BnMul(b, static_cast<uint32_t>(guess));
  int result = BnCompare(a, t);
  BnFree(t);
  if (result > 0)
    return BnGuess(a, b, guess, to);
  if (result < 0)
    return BnGuess(a, b, from, guess);
  return guess;
}

void BnDiv(uint32_t* a, uint32_t* b, uint32_t** pq, uint32_t** pr) {
  if (BnCompare(a, b) < 0) {
    if (pq)
      *pq = BnNew();
    if (pr)
      *pr = BnCopy(a);
    return;
  }

  int oa = kBigIntSize - 1;
  int ob = kBigIntSize - 1;
  for (; oa > 0 && !a[oa]; --oa) {}
  for (; ob > 0 && !b[ob]; --ob) {}
  uint32_t* q = BnNew();
  uint32_t* ca = BnCopy(a);

  int digit = a[oa] < b[ob] ? oa - ob - 1 : oa - ob;

  for (; digit >= 0; --digit) {
    uint32_t* shifted_b = BnCopy(b);
    BnLeftShift(shifted_b, digit);
    uint32_t value = static_cast<uint32_t>(BnGuess(
        ca, shifted_b, 0,
        static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + 1));
    q[digit] = value;
    uint32_t* t = BnMul(shifted_b, value);
    BnSub(ca, t);
    BnFree(t);
    BnFree(shifted_b);
  }

  if (pq)
    *pq = q;
  else
    BnFree(q);
  if (pr)
    *pr = ca;
  else
    BnFree(ca);
}

}  // namespace

std::unique_ptr<crypto::RSAPrivateKey> AndroidRSAPrivateKey(Profile* profile) {
  std::string encoded_key =
      profile->GetPrefs()->GetString(prefs::kDevToolsAdbKey);
  std::string decoded_key;
  std::unique_ptr<crypto::RSAPrivateKey> key;
  if (!encoded_key.empty() && base::Base64Decode(encoded_key, &decoded_key)) {
    std::vector<uint8_t> key_info(decoded_key.begin(), decoded_key.end());
    key = crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_info);
  }
  if (!key) {
    key = crypto::RSAPrivateKey::Create(2048);
    std::vector<uint8_t> key_info;
    if (!key || !key->ExportPrivateKey(&key_info))
      return nullptr;

    std::string key_string(key_info.begin(), key_info.end());
    base::Base64Encode(key_string, &encoded_key);
    profile->GetPrefs()->SetString(prefs::kDevToolsAdbKey,
                                   encoded_key);
  }
  return key;
}

std::string AndroidRSAPublicKey(crypto::RSAPrivateKey* key) {
  std::vector<uint8_t> public_key;
  if (!key)
    return kDummyRSAPublicKey;

  key->ExportPublicKey(&public_key);
  std::string asn1(public_key.begin(), public_key.end());

  base::StringPiece pk;
  if (!net::asn1::ExtractSubjectPublicKeyFromSPKI(asn1, &pk))
    return kDummyRSAPublicKey;

  // Skip 10 byte asn1 prefix to the modulus.
  std::vector<uint8_t> pk_data(pk.data() + 10, pk.data() + pk.length());
  uint32_t* n = BnNew();
  for (size_t i = 0; i < kRSANumWords; ++i) {
    uint32_t t = pk_data[4 * i];
    t = t << 8;
    t += pk_data[4 * i + 1];
    t = t << 8;
    t += pk_data[4 * i + 2];
    t = t << 8;
    t += pk_data[4 * i + 3];
    n[kRSANumWords - i - 1] = t;
  }
  uint64_t n0 = n[0];

  RSAPublicKey pkey;
  pkey.len = kRSANumWords;
  pkey.exponent = 65537; // Fixed public exponent
  pkey.n0inv = 0 - ModInverse(n0, 0x100000000LL);
  if (pkey.n0inv == 0)
    return kDummyRSAPublicKey;

  uint32_t* r = BnNew();
  r[kRSANumWords * 2] = 1;

  uint32_t* rr;
  BnDiv(r, n, NULL, &rr);

  for (size_t i = 0; i < kRSANumWords; ++i) {
    pkey.n[i] = n[i];
    pkey.rr[i] = rr[i];
  }

  BnFree(n);
  BnFree(r);
  BnFree(rr);

  std::string output;
  std::string input(reinterpret_cast<char*>(&pkey), sizeof(pkey));
  base::Base64Encode(input, &output);
  return output;
}

std::string AndroidRSASign(crypto::RSAPrivateKey* key,
                           const std::string& body) {
  std::vector<uint8_t> digest(body.begin(), body.end());
  std::vector<uint8_t> result;
  if (!crypto::SignatureCreator::Sign(key, crypto::SignatureCreator::SHA1,
                                      digest.data(), digest.size(), &result)) {
    return std::string();
  }
  return std::string(result.begin(), result.end());
}
