Skip to content

Commit

Permalink
Merge af06aad into b3a33b6
Browse files Browse the repository at this point in the history
  • Loading branch information
JeffLIrion committed Oct 19, 2023
2 parents b3a33b6 + af06aad commit 3b6b1b1
Show file tree
Hide file tree
Showing 36 changed files with 2,338 additions and 1,462 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/python-package.yml
Expand Up @@ -30,9 +30,9 @@ jobs:
run: |
python -m pip install --upgrade pip
make venv
- name: Linting checks with pylint, flake8, and (soon) black
- name: Linting checks with pylint, flake8, and black
run: |
make lint-flake8 lint-pylint
make lint
- name: Test with pytest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion adb_shell/__init__.py
Expand Up @@ -7,4 +7,4 @@
"""


__version__ = '0.4.4'
__version__ = "0.4.4"
275 changes: 200 additions & 75 deletions adb_shell/adb_device.py

Large diffs are not rendered by default.

302 changes: 223 additions & 79 deletions adb_shell/adb_device_async.py

Large diffs are not rendered by default.

13 changes: 9 additions & 4 deletions adb_shell/adb_message.py
Expand Up @@ -86,7 +86,7 @@ def int_to_cmd(n):
The ADB command (e.g., ``'CNXN'``)
"""
return ''.join(chr((n >> (i * 8)) % 256) for i in range(4)).encode('utf-8')
return "".join(chr((n >> (i * 8)) % 256) for i in range(4)).encode("utf-8")


def unpack(message):
Expand Down Expand Up @@ -119,7 +119,9 @@ def unpack(message):
try:
cmd, arg0, arg1, data_length, data_checksum, _ = struct.unpack(constants.MESSAGE_FORMAT, message)
except struct.error as e:
raise ValueError('Unable to unpack ADB command. (length={})'.format(len(message)), constants.MESSAGE_FORMAT, message, e)
raise ValueError(
"Unable to unpack ADB command. (length={})".format(len(message)), constants.MESSAGE_FORMAT, message, e
)

return cmd, arg0, arg1, data_length, data_checksum

Expand Down Expand Up @@ -152,7 +154,8 @@ class AdbMessage(object):
``self.command`` with its bits flipped; in other words, ``self.command + self.magic == 2**32 - 1``
"""
def __init__(self, command, arg0, arg1, data=b''):

def __init__(self, command, arg0, arg1, data=b""):
self.command = constants.ID_TO_WIRE[command]
self.magic = self.command ^ 0xFFFFFFFF
self.arg0 = arg0
Expand All @@ -168,7 +171,9 @@ def pack(self):
The message packed into the format required by ADB
"""
return struct.pack(constants.MESSAGE_FORMAT, self.command, self.arg0, self.arg1, len(self.data), self.checksum, self.magic)
return struct.pack(
constants.MESSAGE_FORMAT, self.command, self.arg0, self.arg1, len(self.data), self.checksum, self.magic
)

@property
def checksum(self):
Expand Down
76 changes: 44 additions & 32 deletions adb_shell/auth/keygen.py
Expand Up @@ -68,20 +68,20 @@

#: 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;
"<" # 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'):
def _to_bytes(n, length, endianess="big"):
"""Partial python2 compatibility with int.to_bytes
https://stackoverflow.com/a/20793663
Expand All @@ -101,10 +101,10 @@ def _to_bytes(n, length, endianess='big'):
TODO
"""
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]
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)


Expand All @@ -118,15 +118,17 @@ def decode_pubkey(public_key):
"""
binary_key_data = base64.b64decode(public_key)
modulus_size_words, n0inv, modulus_bytes, rr_bytes, exponent = struct.unpack(ANDROID_RSAPUBLICKEY_STRUCT, binary_key_data)
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)
_LOGGER.debug('modulus_size_words: %s', hex(modulus_size_words))
_LOGGER.debug('n0inv: %s', hex(n0inv))
_LOGGER.debug('modulus: %s', ':'.join((hex(m) for m in modulus)))
_LOGGER.debug('rr: %s', ':'.join((hex(r) for r in rr)))
_LOGGER.debug('exponent: %s', hex(exponent))
_LOGGER.debug("modulus_size_words: %s", hex(modulus_size_words))
_LOGGER.debug("n0inv: %s", hex(n0inv))
_LOGGER.debug("modulus: %s", ":".join((hex(m) for m in modulus)))
_LOGGER.debug("rr: %s", ":".join((hex(r) for r in rr)))
_LOGGER.debug("exponent: %s", hex(exponent))


def decode_pubkey_file(public_key_path):
Expand All @@ -138,7 +140,7 @@ def decode_pubkey_file(public_key_path):
TODO
"""
with open(public_key_path, 'rb') as fd:
with open(public_key_path, "rb") as fd:
decode_pubkey(fd.read())


Expand All @@ -156,8 +158,12 @@ def encode_pubkey(private_key_path):
TODO
"""
with open(private_key_path, 'rb') as key_file:
key = serialization.load_pem_private_key(key_file.read(), password=None, backend=default_backend()).private_numbers().public_numbers
with open(private_key_path, "rb") as key_file:
key = (
serialization.load_pem_private_key(key_file.read(), password=None, backend=default_backend())
.private_numbers()
.public_numbers
)

# Compute and store n0inv = -1 / N[0] mod 2^32.
# BN_set_bit(r32, 32)
Expand All @@ -173,15 +179,15 @@ def encode_pubkey(private_key_path):
# 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
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
_to_bytes(key.n, ANDROID_PUBKEY_MODULUS_SIZE, "little"),
_to_bytes(rr, ANDROID_PUBKEY_MODULUS_SIZE, "little"),
key.e,
)


Expand All @@ -197,16 +203,16 @@ def get_user_info():
try:
username = os.getlogin()
except (FileNotFoundError, OSError):
username = 'unknown'
username = "unknown"

if not username:
username = 'unknown'
username = "unknown"

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

return ' ' + username + '@' + hostname
return " " + username + "@" + hostname


def write_public_keyfile(private_key_path, public_key_path):
Expand All @@ -224,7 +230,7 @@ def write_public_keyfile(private_key_path, public_key_path):
public_key = encode_pubkey(private_key_path)
assert len(public_key) == struct.calcsize(ANDROID_RSAPUBLICKEY_STRUCT)

with open(public_key_path, 'wb') as public_key_file:
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())

Expand All @@ -245,7 +251,13 @@ def keygen(filepath):
"""
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend())

with open(filepath, 'wb') as private_key_file:
private_key_file.write(private_key.private_bytes(encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption()))
with open(filepath, "wb") as private_key_file:
private_key_file.write(
private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
)
)

write_public_keyfile(filepath, filepath + '.pub')
write_public_keyfile(filepath, filepath + ".pub")
5 changes: 3 additions & 2 deletions adb_shell/auth/sign_cryptography.py
Expand Up @@ -48,11 +48,12 @@ class CryptographySigner(object):
The loaded private key
"""

def __init__(self, rsa_key_path):
with open(rsa_key_path + '.pub', 'rb') as rsa_pub_file:
with open(rsa_key_path + ".pub", "rb") as rsa_pub_file:
self.public_key = rsa_pub_file.read()

with open(rsa_key_path, 'rb') as rsa_prv_file:
with open(rsa_key_path, "rb") as rsa_prv_file:
self.rsa_key = serialization.load_pem_private_key(rsa_prv_file.read(), None, default_backend())

def Sign(self, data):
Expand Down
5 changes: 3 additions & 2 deletions adb_shell/auth/sign_pycryptodome.py
Expand Up @@ -45,14 +45,15 @@ class PycryptodomeAuthSigner(object):
The contents of theprivate key
"""

def __init__(self, rsa_key_path=None):
super(PycryptodomeAuthSigner, self).__init__()

if rsa_key_path:
with open(rsa_key_path + '.pub', 'rb') as rsa_pub_file:
with open(rsa_key_path + ".pub", "rb") as rsa_pub_file:
self.public_key = rsa_pub_file.read()

with open(rsa_key_path, 'rb') as rsa_priv_file:
with open(rsa_key_path, "rb") as rsa_priv_file:
self.rsa_key = RSA.import_key(rsa_priv_file.read())

def Sign(self, data):
Expand Down
22 changes: 12 additions & 10 deletions adb_shell/auth/sign_pythonrsa.py
Expand Up @@ -51,8 +51,9 @@ class _Accum(object):
A buffer for storing data before it is signed
"""

def __init__(self):
self._buf = b''
self._buf = b""

def update(self, msg):
"""Update this hash object's state with the provided ``msg``.
Expand All @@ -77,8 +78,8 @@ def digest(self):
return self._buf


pkcs1.HASH_METHODS['SHA-1-PREHASHED'] = _Accum
pkcs1.HASH_ASN1['SHA-1-PREHASHED'] = pkcs1.HASH_ASN1['SHA-1']
pkcs1.HASH_METHODS["SHA-1-PREHASHED"] = _Accum
pkcs1.HASH_ASN1["SHA-1-PREHASHED"] = pkcs1.HASH_ASN1["SHA-1"]


def _load_rsa_private_key(pem):
Expand All @@ -105,18 +106,18 @@ def _load_rsa_private_key(pem):
"""
try:
der = rsa.pem.load_pem(pem, 'PRIVATE KEY')
der = rsa.pem.load_pem(pem, "PRIVATE KEY")
keyinfo, _ = decoder.decode(der)

if keyinfo[1][0] != univ.ObjectIdentifier('1.2.840.113549.1.1.1'):
raise ValueError('Not a DER-encoded OpenSSL private RSA key')
if keyinfo[1][0] != univ.ObjectIdentifier("1.2.840.113549.1.1.1"):
raise ValueError("Not a DER-encoded OpenSSL private RSA key")

private_key_der = keyinfo[2].asOctets()

except IndexError:
raise ValueError('Not a DER-encoded OpenSSL private RSA key')
raise ValueError("Not a DER-encoded OpenSSL private RSA key")

return rsa.PrivateKey.load_pkcs1(private_key_der, format='DER')
return rsa.PrivateKey.load_pkcs1(private_key_der, format="DER")


class PythonRSASigner(object):
Expand All @@ -137,6 +138,7 @@ class PythonRSASigner(object):
The contents of the public key file
"""

def __init__(self, pub=None, priv=None):
self.priv_key = _load_rsa_private_key(priv)
self.pub_key = pub
Expand All @@ -156,7 +158,7 @@ def FromRSAKeyPath(cls, rsa_key_path):
A :class:`PythonRSASigner` with private key ``rsa_key_path`` and public key ``rsa_key_path + '.pub'``
"""
with open(rsa_key_path + '.pub') as f:
with open(rsa_key_path + ".pub") as f:
pub = f.read()
with open(rsa_key_path) as f:
priv = f.read()
Expand All @@ -176,7 +178,7 @@ def Sign(self, data):
The signed ``data``
"""
return rsa.sign(data, self.priv_key, 'SHA-1-PREHASHED')
return rsa.sign(data, self.priv_key, "SHA-1-PREHASHED")

def GetPublicKey(self):
"""Returns the public key in PEM format without headers or newlines.
Expand Down
48 changes: 24 additions & 24 deletions adb_shell/constants.py
Expand Up @@ -61,23 +61,23 @@
#: AUTH constant for ``arg0``
AUTH_RSAPUBLICKEY = 3

AUTH = b'AUTH'
CLSE = b'CLSE'
CNXN = b'CNXN'
FAIL = b'FAIL'
OKAY = b'OKAY'
OPEN = b'OPEN'
SYNC = b'SYNC'
WRTE = b'WRTE'

DATA = b'DATA'
DENT = b'DENT'
DONE = b'DONE'
LIST = b'LIST'
QUIT = b'QUIT'
RECV = b'RECV'
SEND = b'SEND'
STAT = b'STAT'
AUTH = b"AUTH"
CLSE = b"CLSE"
CNXN = b"CNXN"
FAIL = b"FAIL"
OKAY = b"OKAY"
OPEN = b"OPEN"
SYNC = b"SYNC"
WRTE = b"WRTE"

DATA = b"DATA"
DENT = b"DENT"
DONE = b"DONE"
LIST = b"LIST"
QUIT = b"QUIT"
RECV = b"RECV"
SEND = b"SEND"
STAT = b"STAT"

#: Commands that are recognized by :meth:`adb_shell.adb_device._AdbIOManager._read_packet_from_device` and :meth:`adb_shell.adb_device_async._AdbIOManagerAsync._read_packet_from_device`
IDS = (AUTH, CLSE, CNXN, OKAY, OPEN, SYNC, WRTE)
Expand All @@ -98,25 +98,25 @@
FILESYNC_WIRE_TO_ID = {wire: cmd_id for cmd_id, wire in FILESYNC_ID_TO_WIRE.items()}

#: An ADB message is 6 words in little-endian.
MESSAGE_FORMAT = b'<6I'
MESSAGE_FORMAT = b"<6I"

#: The format for FileSync "list" messages
FILESYNC_LIST_FORMAT = b'<5I'
FILESYNC_LIST_FORMAT = b"<5I"

#: The format for FileSync "pull" messages
FILESYNC_PULL_FORMAT = b'<2I'
FILESYNC_PULL_FORMAT = b"<2I"

#: The format for FileSync "push" messages
FILESYNC_PUSH_FORMAT = b'<2I'
FILESYNC_PUSH_FORMAT = b"<2I"

#: The format for FileSync "stat" messages
FILESYNC_STAT_FORMAT = b'<4I'
FILESYNC_STAT_FORMAT = b"<4I"

#: The size of an ADB message
MESSAGE_SIZE = struct.calcsize(MESSAGE_FORMAT)

#: Default authentication timeout (in s) for :meth:`adb_shell.adb_device.AdbDevice.connect` and :meth:`adb_shell.adb_device_async.AdbDeviceAsync.connect`
DEFAULT_AUTH_TIMEOUT_S = 10.
DEFAULT_AUTH_TIMEOUT_S = 10.0

#: Default total timeout (in s) for reading data from the device
DEFAULT_READ_TIMEOUT_S = 10.
DEFAULT_READ_TIMEOUT_S = 10.0

0 comments on commit 3b6b1b1

Please sign in to comment.