Skip to content

Commit 72d9dbe

Browse files
committed
Split off M2CryptoSigner into its own file.
Do not throw an ImportError if M2Crypto is not installed, there are alternatives. Add support for python-rsa based implementation.
1 parent 1c9ac27 commit 72d9dbe

5 files changed

Lines changed: 108 additions & 17 deletions

File tree

CONTRIBUTORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ Fahrzin Hemmati <fahhem@gmail.com>
22
Alex Lusco <alusco@google.com>
33
Simon Ye <sye737+github@gmail.com>
44
Jamey Hicks <jamey.hicks@gmail.com>
5+
Marc-Antoine Ruel <maruel@chromium.org>
56
Max Borghino <fmborghino@gmail.com>

adb/adb_commands.py

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@
2626
import os
2727
import socket
2828

29-
from M2Crypto import RSA
30-
3129
import adb_protocol
3230
import common
3331
import filesync_protocol
@@ -40,20 +38,12 @@
4038
DeviceIsAvailable = common.InterfaceMatcher(CLASS, SUBCLASS, PROTOCOL)
4139

4240

43-
class M2CryptoSigner(adb_protocol.AuthSigner):
44-
"""AuthSigner using M2Crypto."""
45-
46-
def __init__(self, rsa_key_path):
47-
with open(rsa_key_path + '.pub') as rsa_pub_file:
48-
self.public_key = rsa_pub_file.read()
49-
50-
self.rsa_key = RSA.load_key(rsa_key_path)
51-
52-
def Sign(self, data):
53-
return self.rsa_key.sign(data, 'sha1')
54-
55-
def GetPublicKey(self):
56-
return self.public_key
41+
try:
42+
# Imported locally to keep compatibility with previous code.
43+
from sign_m2crypto import M2CryptoSigner
44+
except ImportError:
45+
# Ignore this error when M2Crypto is not installed, there are other options.
46+
pass
5747

5848

5949
class AdbCommands(object):

adb/adb_debug.py

100644100755
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#!/usr/bin/env python
12
# Copyright 2014 Google Inc. All rights reserved.
23
#
34
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,6 +24,7 @@
2324

2425
import adb_commands
2526
import common_cli
27+
import sign_m2crypto
2628

2729
gflags.ADOPT_module_key_flags(common_cli)
2830

@@ -37,7 +39,7 @@
3739
def GetRSAKwargs():
3840
if FLAGS.rsa_key_path:
3941
return {
40-
'rsa_keys': [adb_commands.M2CryptoSigner(os.path.expanduser(path))
42+
'rsa_keys': [sign_m2crypto.M2CryptoSigner(os.path.expanduser(path))
4143
for path in FLAGS.rsa_key_path],
4244
'auth_timeout_ms': int(FLAGS.auth_timeout_s * 1000.0),
4345
}

adb/sign_m2crypto.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Copyright 2014 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from M2Crypto import RSA
16+
17+
from adb import adb_protocol
18+
19+
20+
class M2CryptoSigner(adb_protocol.AuthSigner):
21+
"""AuthSigner using M2Crypto."""
22+
23+
def __init__(self, rsa_key_path):
24+
with open(rsa_key_path + '.pub') as rsa_pub_file:
25+
self.public_key = rsa_pub_file.read()
26+
27+
self.rsa_key = RSA.load_key(rsa_key_path)
28+
29+
def Sign(self, data):
30+
return self.rsa_key.sign(data, 'sha1')
31+
32+
def GetPublicKey(self):
33+
return self.public_key
34+

adb/sign_pythonrsa.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Copyright 2014 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import rsa
16+
17+
from pyasn1.codec.der import decoder
18+
from pyasn1.type import univ
19+
from rsa import pkcs1
20+
21+
22+
# python-rsa lib hashes all messages it signs. ADB does it already, we just
23+
# need to slap a signature on top of already hashed message. Introduce "fake"
24+
# hashing algo for this.
25+
class _Accum(object):
26+
def __init__(self):
27+
self._buf = ''
28+
def update(self, msg):
29+
self._buf += msg
30+
def digest(self):
31+
return self._buf
32+
pkcs1.HASH_METHODS['SHA-1-PREHASHED'] = _Accum
33+
pkcs1.HASH_ASN1['SHA-1-PREHASHED'] = pkcs1.HASH_ASN1['SHA-1']
34+
35+
36+
def _load_rsa_private_key(pem):
37+
"""PEM encoded PKCS#8 private key -> rsa.PrivateKey."""
38+
# ADB uses private RSA keys in pkcs#8 format. 'rsa' library doesn't support
39+
# them natively. Do some ASN unwrapping to extract naked RSA key
40+
# (in der-encoded form). See https://www.ietf.org/rfc/rfc2313.txt.
41+
# Also http://superuser.com/a/606266.
42+
try:
43+
der = rsa.pem.load_pem(pem, 'PRIVATE KEY')
44+
keyinfo, _ = decoder.decode(der)
45+
if keyinfo[1][0] != univ.ObjectIdentifier(
46+
'1.2.840.113549.1.1.1'): # pragma: no cover
47+
raise ValueError('Not a DER-encoded OpenSSL private RSA key')
48+
private_key_der = keyinfo[2].asOctets()
49+
except IndexError: # pragma: no cover
50+
raise ValueError('Not a DER-encoded OpenSSL private RSA key')
51+
return rsa.PrivateKey.load_pkcs1(private_key_der, format='DER')
52+
53+
54+
class PythonRSASigner(object):
55+
"""Implements adb_protocol.AuthSigner using http://stuvel.eu/rsa."""
56+
def __init__(self, pub, priv):
57+
self.priv_key = _load_rsa_private_key(priv)
58+
self.pub_key = pub
59+
60+
def Sign(self, data):
61+
return rsa.sign(data, self.priv_key, 'SHA-1-PREHASHED')
62+
63+
def GetPublicKey(self):
64+
return self.pub_key

0 commit comments

Comments
 (0)