1#
2# Copyright 2017 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16"""Methods to call native OpenSSL APIs to support P256 with compression.
17
18Since there are no Python crypto libraries that support P256 EC keys with
19with X9.62 compression, this module uses the native OpenSSL APIs to support
20key generation and deriving a shared secret using generated P256 EC keys.
21"""
22
23from ctypes import byref
24from ctypes import c_int
25from ctypes import c_ubyte
26from ctypes import c_uint
27from ctypes import cdll
28from ctypes import create_string_buffer
29from ctypes import POINTER
30from ctypes.util import find_library
31
32_ECDH_KEY_LEN = 33
33
34
35def _ec_helper_native():
36  """Loads the ec_helper_native library.
37
38  Returns:
39    The ctypes ec_helper_native library.
40  """
41  cdll.LoadLibrary(find_library('crypto'))
42  cdll.LoadLibrary(find_library('ssl'))
43  return cdll.LoadLibrary('./ec_helper_native.so')
44
45
46def generate_p256_key():
47  """Geneates a new p256 key pair.
48
49  Raises:
50    RuntimeError: Generating a new key fails.
51
52  Returns:
53    A tuple containing the der-encoded private key and the X9.62 compressed
54    public key.
55  """
56  ec_helper = _ec_helper_native()
57  native_generate_p256_key = ec_helper.generate_p256_key
58  native_generate_p256_key.argtypes = [
59      POINTER(POINTER(c_ubyte)),
60      POINTER(c_uint),
61      POINTER(c_ubyte)
62  ]
63  native_generate_p256_key.restype = c_int
64  pub_key = (c_ubyte * _ECDH_KEY_LEN).from_buffer(bytearray(_ECDH_KEY_LEN))
65  pub_key_ptr = POINTER(c_ubyte)(pub_key)
66  priv_key = POINTER(c_ubyte)()
67  priv_key_len = c_uint(0)
68  res = native_generate_p256_key(
69      byref(priv_key), byref(priv_key_len), pub_key_ptr)
70  if res != 0:
71    raise RuntimeError('Failed to generate EC key')
72  private_key = bytes(bytearray(priv_key[:priv_key_len.value]))
73  public_key = bytes(bytearray(pub_key[0:_ECDH_KEY_LEN]))
74  return [private_key, public_key]
75
76
77def compute_p256_shared_secret(private_key, device_public_key):
78  """Computes a shared secret between the script and the device.
79
80  Args:
81    private_key: the script's private key.
82    device_public_key: the device's public key.
83
84  Raises:
85    RuntimeError: Computing the shared secret fails.
86
87  Returns:
88    The shared secret.
89  """
90  ec_helper = _ec_helper_native()
91  shared_secret_compute = ec_helper.shared_secret_compute
92  shared_secret_compute.argtypes = [
93      POINTER(c_ubyte), c_uint,
94      POINTER(c_ubyte),
95      POINTER(c_ubyte)
96  ]
97  shared_secret_compute.restype = c_int
98  shared_secret = (c_ubyte * 32).from_buffer(bytearray(32))
99  shared_secret_ptr = POINTER(c_ubyte)(shared_secret)
100  device_public_key_ptr = POINTER(c_ubyte)(
101      create_string_buffer(device_public_key))
102  private_key_ptr = POINTER(c_ubyte)(create_string_buffer(private_key))
103  res = shared_secret_compute(private_key_ptr,
104                              len(private_key), device_public_key_ptr,
105                              shared_secret_ptr)
106  if res != 0:
107    raise RuntimeError('Failed to compute P256 shared secret')
108  return bytes(bytearray(shared_secret[0:32]))
109