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

Commit 42e5ae3

Browse files
committed
1 parent d0be33c commit 42e5ae3

File tree

2 files changed

+146
-10
lines changed

2 files changed

+146
-10
lines changed

adb/android_pubkey.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
"""This file implements encoding and decoding logic for Android's custom RSA
2+
public key binary format. Public keys are stored as a sequence of
3+
little-endian 32 bit words. Note that Android only supports little-endian
4+
processors, so we don't do any byte order conversions when parsing the binary
5+
struct.
6+
7+
Structure from:
8+
https://github.com/aosp-mirror/platform_system_core/blob/c55fab4a59cfa461857c6a61d8a0f1ae4591900c/libcrypto_utils/android_pubkey.c
9+
10+
typedef struct RSAPublicKey {
11+
// Modulus length. This must be ANDROID_PUBKEY_MODULUS_SIZE.
12+
uint32_t modulus_size_words;
13+
14+
// Precomputed montgomery parameter: -1 / n[0] mod 2^32
15+
uint32_t n0inv;
16+
17+
// RSA modulus as a little-endian array.
18+
uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];
19+
20+
// Montgomery parameter R^2 as a little-endian array of little-endian words.
21+
uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];
22+
23+
// RSA modulus: 3 or 65537
24+
uint32_t exponent;
25+
} RSAPublicKey;"""
26+
27+
28+
from __future__ import print_function
29+
30+
import os
31+
import six
32+
import base64
33+
import socket
34+
import struct
35+
36+
import Crypto.Util
37+
import Crypto.PublicKey.RSA
38+
39+
40+
# Size of an RSA modulus such as an encrypted block or a signature.
41+
ANDROID_PUBKEY_MODULUS_SIZE = (2048 // 8)
42+
43+
# Size of an encoded RSA key.
44+
ANDROID_PUBKEY_ENCODED_SIZE = \
45+
(3 * 4 + 2 * ANDROID_PUBKEY_MODULUS_SIZE)
46+
# (3 * sizeof(uint32_t) + 2 * ANDROID_PUBKEY_MODULUS_SIZE)
47+
48+
# Size of the RSA modulus in words.
49+
ANDROID_PUBKEY_MODULUS_SIZE_WORDS = (ANDROID_PUBKEY_MODULUS_SIZE // 4)
50+
51+
52+
def _to_bytes(n, length, endianess='big'):
53+
"""partial python2 compatibility with int.to_bytes
54+
https://stackoverflow.com/a/20793663"""
55+
if six.PY2:
56+
h = '{:x}'.format(n)
57+
s = ('0' * (len(h) % 2) + h).zfill(length * 2).decode('hex')
58+
return s if endianess == 'big' else s[::-1]
59+
return n.to_bytes(length, endianess)
60+
61+
62+
def decode_pubkey(public_key):
63+
"""decodes a public RSA key stored in Android's custom binary format"""
64+
binary_key_data = base64.b64decode(public_key)
65+
key_struct = struct.unpack(('<LL' +
66+
'B' * ANDROID_PUBKEY_MODULUS_SIZE +
67+
'B' * ANDROID_PUBKEY_MODULUS_SIZE +
68+
'L'), binary_key_data)
69+
modulus_size_words = key_struct[0]
70+
n0inv = key_struct[1]
71+
modulus = reversed(key_struct[2: 2 + ANDROID_PUBKEY_MODULUS_SIZE])
72+
rr = reversed(key_struct[2 + ANDROID_PUBKEY_MODULUS_SIZE:
73+
2 + 2 * ANDROID_PUBKEY_MODULUS_SIZE])
74+
exponent = key_struct[-1]
75+
print('modulus_size_words:', hex(modulus_size_words))
76+
print('n0inv:', hex(n0inv))
77+
print('modulus: ', end='')
78+
print(*map(hex, modulus), sep=':')
79+
print('rr: ', end='')
80+
print(*map(hex, rr), sep=':')
81+
print('exponent:', hex(exponent))
82+
83+
84+
def decode_pubkey_file(public_key_path):
85+
with open(public_key_path, 'rb') as fd:
86+
decode_pubkey(fd.read())
87+
88+
89+
def encode_pubkey(private_key_path):
90+
"""encodes a public RSA key into Android's custom binary format"""
91+
key = Crypto.PublicKey.RSA.import_key(private_key_path)
92+
93+
# Store the modulus size.
94+
key_buffer = struct.pack('<L', ANDROID_PUBKEY_MODULUS_SIZE_WORDS)
95+
# Compute and store n0inv = -1 / N[0] mod 2^32.
96+
# BN_set_bit(r32, 32)
97+
r32 = 1 << 32
98+
# BN_mod(n0inv, key->n, r32, ctx)
99+
n0inv = key.n % r32
100+
# BN_mod_inverse(n0inv, n0inv, r32, ctx)
101+
n0inv = Crypto.Util.number.inverse(n0inv, r32)
102+
# BN_sub(n0inv, r32, n0inv)
103+
n0inv = r32 - n0inv
104+
key_buffer += struct.pack('<L', n0inv)
105+
106+
# Store the modulus.
107+
key_buffer += _to_bytes(key.n, ANDROID_PUBKEY_MODULUS_SIZE, 'little')
108+
# Compute and store rr = (2^(rsa_size)) ^ 2 mod N.
109+
# BN_set_bit(rr, ANDROID_PUBKEY_MODULUS_SIZE * 8)
110+
rr = 1 << (ANDROID_PUBKEY_MODULUS_SIZE * 8)
111+
# BN_mod_sqr(rr, rr, key->n, ctx)
112+
rr = (rr ** 2) % key.n
113+
key_buffer += _to_bytes(rr, ANDROID_PUBKEY_MODULUS_SIZE, 'little')
114+
115+
key_buffer += struct.pack('<L', key.e)
116+
return key_buffer
117+
118+
119+
def get_user_info():
120+
username = os.getlogin()
121+
if not username:
122+
username = 'unknown'
123+
124+
hostname = socket.gethostname()
125+
if not hostname:
126+
hostname = 'unknown'
127+
128+
return ' ' + username + '@' + hostname
129+
130+
131+
def write_public_keyfile(private_key_path, public_key_path):
132+
"""write public keyfile to public_key_path in Android's custom
133+
RSA public key format given a path to a private keyfile"""
134+
with open(private_key_path, 'rb') as private_key_file:
135+
private_key = private_key_file.read()
136+
137+
public_key = encode_pubkey(private_key)
138+
assert len(public_key) == ANDROID_PUBKEY_ENCODED_SIZE
139+
with open(public_key_path, 'wb') as public_key_file:
140+
public_key_file.write(base64.b64encode(public_key))
141+
public_key_file.write(get_user_info().encode())

setup.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,11 @@
1717
# Figure out if the system already has a supported Crypto library
1818
rsa_signer_library = 'cryptography'
1919
try:
20-
import rsa
20+
import rsa
2121

22-
rsa_signer_library = 'rsa'
22+
rsa_signer_library = 'rsa'
2323
except ImportError:
24-
try:
25-
from Crypto.Hash import SHA256
26-
from Crypto.PublicKey import RSA
27-
from Crypto.Signature import pkcs1_15
28-
29-
rsa_signer_library = 'pycryptodome'
30-
except ImportError:
31-
pass
24+
pass
3225

3326

3427
setup(
@@ -62,6 +55,8 @@
6255

6356
install_requires = [
6457
'libusb1>=1.0.16',
58+
'pycryptodome',
59+
'six',
6560
rsa_signer_library
6661
],
6762

0 commit comments

Comments
 (0)