1# This file is dual licensed under the terms of the Apache License, Version 2# 2.0, and the BSD License. See the LICENSE file in the root of this repository 3# for complete details. 4 5from __future__ import absolute_import, division, print_function 6 7from cryptography import utils 8from cryptography.hazmat.backends.openssl.utils import _evp_pkey_derive 9from cryptography.hazmat.primitives import serialization 10from cryptography.hazmat.primitives.asymmetric.x25519 import ( 11 X25519PrivateKey, 12 X25519PublicKey, 13) 14 15 16_X25519_KEY_SIZE = 32 17 18 19@utils.register_interface(X25519PublicKey) 20class _X25519PublicKey(object): 21 def __init__(self, backend, evp_pkey): 22 self._backend = backend 23 self._evp_pkey = evp_pkey 24 25 def public_bytes(self, encoding, format): 26 if ( 27 encoding is serialization.Encoding.Raw 28 or format is serialization.PublicFormat.Raw 29 ): 30 if ( 31 encoding is not serialization.Encoding.Raw 32 or format is not serialization.PublicFormat.Raw 33 ): 34 raise ValueError( 35 "When using Raw both encoding and format must be Raw" 36 ) 37 38 return self._raw_public_bytes() 39 40 return self._backend._public_key_bytes( 41 encoding, format, self, self._evp_pkey, None 42 ) 43 44 def _raw_public_bytes(self): 45 ucharpp = self._backend._ffi.new("unsigned char **") 46 res = self._backend._lib.EVP_PKEY_get1_tls_encodedpoint( 47 self._evp_pkey, ucharpp 48 ) 49 self._backend.openssl_assert(res == 32) 50 self._backend.openssl_assert(ucharpp[0] != self._backend._ffi.NULL) 51 data = self._backend._ffi.gc( 52 ucharpp[0], self._backend._lib.OPENSSL_free 53 ) 54 return self._backend._ffi.buffer(data, res)[:] 55 56 57@utils.register_interface(X25519PrivateKey) 58class _X25519PrivateKey(object): 59 def __init__(self, backend, evp_pkey): 60 self._backend = backend 61 self._evp_pkey = evp_pkey 62 63 def public_key(self): 64 bio = self._backend._create_mem_bio_gc() 65 res = self._backend._lib.i2d_PUBKEY_bio(bio, self._evp_pkey) 66 self._backend.openssl_assert(res == 1) 67 evp_pkey = self._backend._lib.d2i_PUBKEY_bio( 68 bio, self._backend._ffi.NULL 69 ) 70 self._backend.openssl_assert(evp_pkey != self._backend._ffi.NULL) 71 evp_pkey = self._backend._ffi.gc( 72 evp_pkey, self._backend._lib.EVP_PKEY_free 73 ) 74 return _X25519PublicKey(self._backend, evp_pkey) 75 76 def exchange(self, peer_public_key): 77 if not isinstance(peer_public_key, X25519PublicKey): 78 raise TypeError("peer_public_key must be X25519PublicKey.") 79 80 return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key) 81 82 def private_bytes(self, encoding, format, encryption_algorithm): 83 if ( 84 encoding is serialization.Encoding.Raw 85 or format is serialization.PublicFormat.Raw 86 ): 87 if ( 88 format is not serialization.PrivateFormat.Raw 89 or encoding is not serialization.Encoding.Raw 90 or not isinstance( 91 encryption_algorithm, serialization.NoEncryption 92 ) 93 ): 94 raise ValueError( 95 "When using Raw both encoding and format must be Raw " 96 "and encryption_algorithm must be NoEncryption()" 97 ) 98 99 return self._raw_private_bytes() 100 101 return self._backend._private_key_bytes( 102 encoding, format, encryption_algorithm, self, self._evp_pkey, None 103 ) 104 105 def _raw_private_bytes(self): 106 # When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 we can 107 # switch this to EVP_PKEY_new_raw_private_key 108 # The trick we use here is serializing to a PKCS8 key and just 109 # using the last 32 bytes, which is the key itself. 110 bio = self._backend._create_mem_bio_gc() 111 res = self._backend._lib.i2d_PKCS8PrivateKey_bio( 112 bio, 113 self._evp_pkey, 114 self._backend._ffi.NULL, 115 self._backend._ffi.NULL, 116 0, 117 self._backend._ffi.NULL, 118 self._backend._ffi.NULL, 119 ) 120 self._backend.openssl_assert(res == 1) 121 pkcs8 = self._backend._read_mem_bio(bio) 122 self._backend.openssl_assert(len(pkcs8) == 48) 123 return pkcs8[-_X25519_KEY_SIZE:] 124