Skip to content
This repository has been archived by the owner on Jan 10, 2023. It is now read-only.

Commit

Permalink
Merge 0a791b9 into d0be33c
Browse files Browse the repository at this point in the history
  • Loading branch information
joeleong committed Jul 24, 2019
2 parents d0be33c + 0a791b9 commit a10a1aa
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 10 deletions.
11 changes: 11 additions & 0 deletions adb/adb_debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

from adb import adb_commands
from adb import common_cli
from adb import android_pubkey

try:
from adb import sign_cryptography
Expand Down Expand Up @@ -149,6 +150,14 @@ def main():
subparser.add_argument(
'--output_port_path', action='store_true',
help='Outputs the port_path alongside the serial')
subparser = subparsers.add_parser(
name='keygen', help='generate adb public/private key '
'private key stored in {filepath} '
'public key stored in {filepath}.pub '
'(existing files overwritten)')
subparser.add_argument(
'filepath',
help='File path to write the private/public keypair (existing files overwritten)')

common_cli.MakeSubparser(
subparsers, parents, adb_commands.AdbCommands.Install)
Expand Down Expand Up @@ -195,6 +204,8 @@ def main():
if args.command_name == 'help':
parser.print_help()
return 0
if args.command_name == 'keygen':
return android_pubkey.keygen(args.filepath)
if args.command_name == 'logcat':
args.positional = args.options
elif args.command_name == 'shell':
Expand Down
157 changes: 157 additions & 0 deletions adb/android_pubkey.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
"""This file implements encoding and decoding logic for Android's custom RSA
public key binary format. Public keys are stored as a sequence of
little-endian 32 bit words. Note that Android only supports little-endian
processors, so we don't do any byte order conversions when parsing the binary
struct.
Structure from:
https://github.com/aosp-mirror/platform_system_core/blob/c55fab4a59cfa461857c6a61d8a0f1ae4591900c/libcrypto_utils/android_pubkey.c
typedef struct RSAPublicKey {
// Modulus length. This must be ANDROID_PUBKEY_MODULUS_SIZE_WORDS
uint32_t modulus_size_words;
// Precomputed montgomery parameter: -1 / n[0] mod 2^32
uint32_t n0inv;
// RSA modulus as a little-endian array
uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];
// Montgomery parameter R^2 as a little-endian array of little-endian words
uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];
// RSA modulus: 3 or 65537
uint32_t exponent;
} RSAPublicKey;"""


from __future__ import print_function

import os
import base64
import socket
import struct

import Crypto.Util
import Crypto.PublicKey.RSA


# Size of an RSA modulus such as an encrypted block or a signature.
ANDROID_PUBKEY_MODULUS_SIZE = (2048 // 8)

# Python representation of "struct RSAPublicKey":
ANDROID_RSAPUBLICKEY_STRUCT = (
'<' # Little-endian
'L' # uint32_t modulus_size_words;
'L' # uint32_t n0inv;
'{modulus_size}s' # uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];
'{modulus_size}s' # uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];
'L' # uint32_t exponent;
).format(modulus_size=ANDROID_PUBKEY_MODULUS_SIZE)


# Size of the RSA modulus in words.
ANDROID_PUBKEY_MODULUS_SIZE_WORDS = (ANDROID_PUBKEY_MODULUS_SIZE // 4)


def _to_bytes(n, length, endianess='big'):
"""partial python2 compatibility with int.to_bytes
https://stackoverflow.com/a/20793663"""
if not hasattr(n, 'to_bytes'):
h = '{:x}'.format(n)
s = ('0' * (len(h) % 2) + h).zfill(length * 2).decode('hex')
return s if endianess == 'big' else s[::-1]
return n.to_bytes(length, endianess)


def decode_pubkey(public_key):
"""decodes a public RSA key stored in Android's custom binary format"""
binary_key_data = base64.b64decode(public_key)
modulus_size_words, n0inv, modulus_bytes, rr_bytes, exponent = \
struct.unpack(ANDROID_RSAPUBLICKEY_STRUCT, binary_key_data)
assert modulus_size_words == ANDROID_PUBKEY_MODULUS_SIZE_WORDS
modulus = reversed(modulus_bytes)
rr = reversed(rr_bytes)
print('modulus_size_words:', hex(modulus_size_words))
print('n0inv:', hex(n0inv))
print('modulus: ', end='')
print(*map(hex, modulus), sep=':')
print('rr: ', end='')
print(*map(hex, rr), sep=':')
print('exponent:', hex(exponent))


def decode_pubkey_file(public_key_path):
with open(public_key_path, 'rb') as fd:
decode_pubkey(fd.read())


def encode_pubkey(private_key_path):
"""encodes a public RSA key into Android's custom binary format"""
key = Crypto.PublicKey.RSA.import_key(private_key_path)

# Compute and store n0inv = -1 / N[0] mod 2^32.
# BN_set_bit(r32, 32)
r32 = 1 << 32
# BN_mod(n0inv, key->n, r32, ctx)
n0inv = key.n % r32
# BN_mod_inverse(n0inv, n0inv, r32, ctx)
n0inv = Crypto.Util.number.inverse(n0inv, r32)
# BN_sub(n0inv, r32, n0inv)
n0inv = r32 - n0inv

# Compute and store rr = (2^(rsa_size)) ^ 2 mod N.
# BN_set_bit(rr, ANDROID_PUBKEY_MODULUS_SIZE * 8)
rr = 1 << (ANDROID_PUBKEY_MODULUS_SIZE * 8)
# BN_mod_sqr(rr, rr, key->n, ctx)
rr = (rr ** 2) % key.n

return struct.pack(
ANDROID_RSAPUBLICKEY_STRUCT,
ANDROID_PUBKEY_MODULUS_SIZE_WORDS,
n0inv,
_to_bytes(key.n, ANDROID_PUBKEY_MODULUS_SIZE, 'little'),
_to_bytes(rr, ANDROID_PUBKEY_MODULUS_SIZE, 'little'),
key.e
)


def get_user_info():
username = os.getlogin()
if not username:
username = 'unknown'

hostname = socket.gethostname()
if not hostname:
hostname = 'unknown'

return ' ' + username + '@' + hostname


def write_public_keyfile(private_key_path, public_key_path):
"""write public keyfile to public_key_path in Android's custom
RSA public key format given a path to a private keyfile"""
with open(private_key_path, 'rb') as private_key_file:
private_key = private_key_file.read()

public_key = encode_pubkey(private_key)
assert len(public_key) == struct.calcsize(ANDROID_RSAPUBLICKEY_STRUCT)
with open(public_key_path, 'wb') as public_key_file:
public_key_file.write(base64.b64encode(public_key))
public_key_file.write(get_user_info().encode())


def keygen(filepath):
"""generate adb public/private key
private key stored in {filepath}
public key stored in {filepath}.pub
(existing files overwritten)
Args:
filepath: File path to write the private/public keypair
"""
key = Crypto.PublicKey.RSA.generate(2048)
with open(filepath, 'wb') as private_key_file:
private_key_file.write(key.export_key(format='PEM', pkcs=8))

write_public_keyfile(filepath, filepath + '.pub')
14 changes: 4 additions & 10 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,11 @@
# Figure out if the system already has a supported Crypto library
rsa_signer_library = 'cryptography'
try:
import rsa
import rsa

rsa_signer_library = 'rsa'
rsa_signer_library = 'rsa'
except ImportError:
try:
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15

rsa_signer_library = 'pycryptodome'
except ImportError:
pass
pass


setup(
Expand Down Expand Up @@ -62,6 +55,7 @@

install_requires = [
'libusb1>=1.0.16',
'pycryptodome',
rsa_signer_library
],

Expand Down

0 comments on commit a10a1aa

Please sign in to comment.