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 7import warnings 8 9from cryptography import utils 10from cryptography.hazmat.backends.openssl.utils import _evp_pkey_derive 11from cryptography.hazmat.primitives import serialization 12from cryptography.hazmat.primitives.asymmetric.x25519 import ( 13 X25519PrivateKey, X25519PublicKey 14) 15 16 17_X25519_KEY_SIZE = 32 18 19 20@utils.register_interface(X25519PublicKey) 21class _X25519PublicKey(object): 22 def __init__(self, backend, evp_pkey): 23 self._backend = backend 24 self._evp_pkey = evp_pkey 25 26 def public_bytes(self, encoding=None, format=None): 27 if encoding is None or format is None: 28 if encoding is not None or format is not None: 29 raise ValueError("Both encoding and format are required") 30 else: 31 warnings.warn( 32 "public_bytes now requires encoding and format arguments. " 33 "Support for calling without arguments will be removed in " 34 "cryptography 2.7", 35 utils.DeprecatedIn25, 36 ) 37 encoding = serialization.Encoding.Raw 38 format = serialization.PublicFormat.Raw 39 if ( 40 encoding is serialization.Encoding.Raw or 41 format is serialization.PublicFormat.Raw 42 ): 43 if ( 44 encoding is not serialization.Encoding.Raw or 45 format is not serialization.PublicFormat.Raw 46 ): 47 raise ValueError( 48 "When using Raw both encoding and format must be Raw" 49 ) 50 51 return self._raw_public_bytes() 52 53 if ( 54 encoding in serialization._PEM_DER and 55 format is not serialization.PublicFormat.SubjectPublicKeyInfo 56 ): 57 raise ValueError( 58 "format must be SubjectPublicKeyInfo when encoding is PEM or " 59 "DER" 60 ) 61 62 return self._backend._public_key_bytes( 63 encoding, format, self, self._evp_pkey, None 64 ) 65 66 def _raw_public_bytes(self): 67 ucharpp = self._backend._ffi.new("unsigned char **") 68 res = self._backend._lib.EVP_PKEY_get1_tls_encodedpoint( 69 self._evp_pkey, ucharpp 70 ) 71 self._backend.openssl_assert(res == 32) 72 self._backend.openssl_assert(ucharpp[0] != self._backend._ffi.NULL) 73 data = self._backend._ffi.gc( 74 ucharpp[0], self._backend._lib.OPENSSL_free 75 ) 76 return self._backend._ffi.buffer(data, res)[:] 77 78 79@utils.register_interface(X25519PrivateKey) 80class _X25519PrivateKey(object): 81 def __init__(self, backend, evp_pkey): 82 self._backend = backend 83 self._evp_pkey = evp_pkey 84 85 def public_key(self): 86 bio = self._backend._create_mem_bio_gc() 87 res = self._backend._lib.i2d_PUBKEY_bio(bio, self._evp_pkey) 88 self._backend.openssl_assert(res == 1) 89 evp_pkey = self._backend._lib.d2i_PUBKEY_bio( 90 bio, self._backend._ffi.NULL 91 ) 92 self._backend.openssl_assert(evp_pkey != self._backend._ffi.NULL) 93 evp_pkey = self._backend._ffi.gc( 94 evp_pkey, self._backend._lib.EVP_PKEY_free 95 ) 96 return _X25519PublicKey(self._backend, evp_pkey) 97 98 def exchange(self, peer_public_key): 99 if not isinstance(peer_public_key, X25519PublicKey): 100 raise TypeError("peer_public_key must be X25519PublicKey.") 101 102 return _evp_pkey_derive( 103 self._backend, self._evp_pkey, peer_public_key 104 ) 105 106 def private_bytes(self, encoding, format, encryption_algorithm): 107 if ( 108 encoding is serialization.Encoding.Raw or 109 format is serialization.PublicFormat.Raw 110 ): 111 if ( 112 format is not serialization.PrivateFormat.Raw or 113 encoding is not serialization.Encoding.Raw or not 114 isinstance(encryption_algorithm, serialization.NoEncryption) 115 ): 116 raise ValueError( 117 "When using Raw both encoding and format must be Raw " 118 "and encryption_algorithm must be NoEncryption" 119 ) 120 121 return self._raw_private_bytes() 122 123 if ( 124 encoding in serialization._PEM_DER and 125 format is not serialization.PrivateFormat.PKCS8 126 ): 127 raise ValueError( 128 "format must be PKCS8 when encoding is PEM or DER" 129 ) 130 131 return self._backend._private_key_bytes( 132 encoding, format, encryption_algorithm, self._evp_pkey, None 133 ) 134 135 def _raw_private_bytes(self): 136 # When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 we can 137 # switch this to EVP_PKEY_new_raw_private_key 138 # The trick we use here is serializing to a PKCS8 key and just 139 # using the last 32 bytes, which is the key itself. 140 bio = self._backend._create_mem_bio_gc() 141 res = self._backend._lib.i2d_PKCS8PrivateKey_bio( 142 bio, self._evp_pkey, 143 self._backend._ffi.NULL, self._backend._ffi.NULL, 144 0, self._backend._ffi.NULL, self._backend._ffi.NULL 145 ) 146 self._backend.openssl_assert(res == 1) 147 pkcs8 = self._backend._read_mem_bio(bio) 148 self._backend.openssl_assert(len(pkcs8) == 48) 149 return pkcs8[-_X25519_KEY_SIZE:] 150