| # |
| # Copyright 2017 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| """Methods to call native OpenSSL APIs to support P256 with compression. |
| |
| Since there are no Python crypto libraries that support P256 EC keys with |
| with X9.62 compression, this module uses the native OpenSSL APIs to support |
| key generation and deriving a shared secret using generated P256 EC keys. |
| """ |
| |
| from ctypes import byref |
| from ctypes import c_int |
| from ctypes import c_ubyte |
| from ctypes import c_uint |
| from ctypes import cdll |
| from ctypes import create_string_buffer |
| from ctypes import POINTER |
| from ctypes.util import find_library |
| |
| _ECDH_KEY_LEN = 33 |
| |
| |
| def _ec_helper_native(): |
| """Loads the ec_helper_native library. |
| |
| Returns: |
| The ctypes ec_helper_native library. |
| """ |
| cdll.LoadLibrary(find_library('crypto')) |
| cdll.LoadLibrary(find_library('ssl')) |
| return cdll.LoadLibrary('./ec_helper_native.so') |
| |
| |
| def generate_p256_key(): |
| """Geneates a new p256 key pair. |
| |
| Raises: |
| RuntimeError: Generating a new key fails. |
| |
| Returns: |
| A tuple containing the der-encoded private key and the X9.62 compressed |
| public key. |
| """ |
| ec_helper = _ec_helper_native() |
| native_generate_p256_key = ec_helper.generate_p256_key |
| native_generate_p256_key.argtypes = [ |
| POINTER(POINTER(c_ubyte)), |
| POINTER(c_uint), |
| POINTER(c_ubyte) |
| ] |
| native_generate_p256_key.restype = c_int |
| pub_key = (c_ubyte * _ECDH_KEY_LEN).from_buffer(bytearray(_ECDH_KEY_LEN)) |
| pub_key_ptr = POINTER(c_ubyte)(pub_key) |
| priv_key = POINTER(c_ubyte)() |
| priv_key_len = c_uint(0) |
| res = native_generate_p256_key( |
| byref(priv_key), byref(priv_key_len), pub_key_ptr) |
| if res != 0: |
| raise RuntimeError('Failed to generate EC key') |
| private_key = bytes(bytearray(priv_key[:priv_key_len.value])) |
| public_key = bytes(bytearray(pub_key[0:_ECDH_KEY_LEN])) |
| return [private_key, public_key] |
| |
| |
| def compute_p256_shared_secret(private_key, device_public_key): |
| """Computes a shared secret between the script and the device. |
| |
| Args: |
| private_key: the script's private key. |
| device_public_key: the device's public key. |
| |
| Raises: |
| RuntimeError: Computing the shared secret fails. |
| |
| Returns: |
| The shared secret. |
| """ |
| ec_helper = _ec_helper_native() |
| shared_secret_compute = ec_helper.shared_secret_compute |
| shared_secret_compute.argtypes = [ |
| POINTER(c_ubyte), c_uint, |
| POINTER(c_ubyte), |
| POINTER(c_ubyte) |
| ] |
| shared_secret_compute.restype = c_int |
| shared_secret = (c_ubyte * 32).from_buffer(bytearray(32)) |
| shared_secret_ptr = POINTER(c_ubyte)(shared_secret) |
| device_public_key_ptr = POINTER(c_ubyte)( |
| create_string_buffer(device_public_key)) |
| private_key_ptr = POINTER(c_ubyte)(create_string_buffer(private_key)) |
| res = shared_secret_compute(private_key_ptr, |
| len(private_key), device_public_key_ptr, |
| shared_secret_ptr) |
| if res != 0: |
| raise RuntimeError('Failed to compute P256 shared secret') |
| return bytes(bytearray(shared_secret[0:32])) |