This repository was archived by the owner on Jan 10, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 365
Expand file tree
/
Copy pathandroid_pubkey.py
More file actions
166 lines (129 loc) · 5.54 KB
/
android_pubkey.py
File metadata and controls
166 lines (129 loc) · 5.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
"""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
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import 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"""
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)
r32 = 1 << 32
# BN_mod(n0inv, key->n, r32, ctx)
n0inv = key.n % r32
# BN_mod_inverse(n0inv, n0inv, r32, ctx)
n0inv = rsa._modinv(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"""
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:
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
"""
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()))
write_public_keyfile(filepath, filepath + '.pub')