blob: 5c177212e3a295c1d74fbdf83de657734d5e9db8 [file] [log] [blame]
#
# 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]))