Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading