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 binascii 8import os 9 10import pytest 11 12from cryptography.exceptions import _Reasons 13from cryptography.hazmat.primitives import serialization 14from cryptography.hazmat.primitives.asymmetric.x448 import ( 15 X448PrivateKey, 16 X448PublicKey, 17) 18 19from ...utils import ( 20 load_nist_vectors, 21 load_vectors_from_file, 22 raises_unsupported_algorithm, 23) 24 25 26@pytest.mark.supported( 27 only_if=lambda backend: not backend.x448_supported(), 28 skip_message="Requires OpenSSL without X448 support", 29) 30def test_x448_unsupported(backend): 31 with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM): 32 X448PublicKey.from_public_bytes(b"0" * 56) 33 34 with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM): 35 X448PrivateKey.from_private_bytes(b"0" * 56) 36 37 with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM): 38 X448PrivateKey.generate() 39 40 41@pytest.mark.supported( 42 only_if=lambda backend: backend.x448_supported(), 43 skip_message="Requires OpenSSL with X448 support", 44) 45class TestX448Exchange(object): 46 @pytest.mark.parametrize( 47 "vector", 48 load_vectors_from_file( 49 os.path.join("asymmetric", "X448", "rfc7748.txt"), 50 load_nist_vectors, 51 ), 52 ) 53 def test_rfc7748(self, vector, backend): 54 private = binascii.unhexlify(vector["input_scalar"]) 55 public = binascii.unhexlify(vector["input_u"]) 56 shared_key = binascii.unhexlify(vector["output_u"]) 57 private_key = X448PrivateKey.from_private_bytes(private) 58 public_key = X448PublicKey.from_public_bytes(public) 59 computed_shared_key = private_key.exchange(public_key) 60 assert computed_shared_key == shared_key 61 62 def test_rfc7748_1000_iteration(self, backend): 63 old_private = private = public = binascii.unhexlify( 64 b"05000000000000000000000000000000000000000000000000000000" 65 b"00000000000000000000000000000000000000000000000000000000" 66 ) 67 shared_key = binascii.unhexlify( 68 b"aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4" 69 b"af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38" 70 ) 71 private_key = X448PrivateKey.from_private_bytes(private) 72 public_key = X448PublicKey.from_public_bytes(public) 73 for _ in range(1000): 74 computed_shared_key = private_key.exchange(public_key) 75 private_key = X448PrivateKey.from_private_bytes( 76 computed_shared_key 77 ) 78 public_key = X448PublicKey.from_public_bytes(old_private) 79 old_private = computed_shared_key 80 81 assert computed_shared_key == shared_key 82 83 # These vectors are also from RFC 7748 84 # https://tools.ietf.org/html/rfc7748#section-6.2 85 @pytest.mark.parametrize( 86 ("private_bytes", "public_bytes"), 87 [ 88 ( 89 binascii.unhexlify( 90 b"9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28d" 91 b"d9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b" 92 ), 93 binascii.unhexlify( 94 b"9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c" 95 b"22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0" 96 ), 97 ), 98 ( 99 binascii.unhexlify( 100 b"1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d" 101 b"6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d" 102 ), 103 binascii.unhexlify( 104 b"3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b430" 105 b"27d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609" 106 ), 107 ), 108 ], 109 ) 110 def test_pub_priv_bytes_raw(self, private_bytes, public_bytes, backend): 111 private_key = X448PrivateKey.from_private_bytes(private_bytes) 112 assert ( 113 private_key.private_bytes( 114 serialization.Encoding.Raw, 115 serialization.PrivateFormat.Raw, 116 serialization.NoEncryption(), 117 ) 118 == private_bytes 119 ) 120 assert ( 121 private_key.public_key().public_bytes( 122 serialization.Encoding.Raw, serialization.PublicFormat.Raw 123 ) 124 == public_bytes 125 ) 126 public_key = X448PublicKey.from_public_bytes(public_bytes) 127 assert ( 128 public_key.public_bytes( 129 serialization.Encoding.Raw, serialization.PublicFormat.Raw 130 ) 131 == public_bytes 132 ) 133 134 @pytest.mark.parametrize( 135 ("encoding", "fmt", "encryption", "passwd", "load_func"), 136 [ 137 ( 138 serialization.Encoding.PEM, 139 serialization.PrivateFormat.PKCS8, 140 serialization.BestAvailableEncryption(b"password"), 141 b"password", 142 serialization.load_pem_private_key, 143 ), 144 ( 145 serialization.Encoding.DER, 146 serialization.PrivateFormat.PKCS8, 147 serialization.BestAvailableEncryption(b"password"), 148 b"password", 149 serialization.load_der_private_key, 150 ), 151 ( 152 serialization.Encoding.PEM, 153 serialization.PrivateFormat.PKCS8, 154 serialization.NoEncryption(), 155 None, 156 serialization.load_pem_private_key, 157 ), 158 ( 159 serialization.Encoding.DER, 160 serialization.PrivateFormat.PKCS8, 161 serialization.NoEncryption(), 162 None, 163 serialization.load_der_private_key, 164 ), 165 ], 166 ) 167 def test_round_trip_private_serialization( 168 self, encoding, fmt, encryption, passwd, load_func, backend 169 ): 170 key = X448PrivateKey.generate() 171 serialized = key.private_bytes(encoding, fmt, encryption) 172 loaded_key = load_func(serialized, passwd, backend) 173 assert isinstance(loaded_key, X448PrivateKey) 174 175 def test_generate(self, backend): 176 key = X448PrivateKey.generate() 177 assert key 178 assert key.public_key() 179 180 def test_invalid_type_exchange(self, backend): 181 key = X448PrivateKey.generate() 182 with pytest.raises(TypeError): 183 key.exchange(object()) 184 185 def test_invalid_length_from_public_bytes(self, backend): 186 with pytest.raises(ValueError): 187 X448PublicKey.from_public_bytes(b"a" * 55) 188 189 with pytest.raises(ValueError): 190 X448PublicKey.from_public_bytes(b"a" * 57) 191 192 def test_invalid_length_from_private_bytes(self, backend): 193 with pytest.raises(ValueError): 194 X448PrivateKey.from_private_bytes(b"a" * 55) 195 196 with pytest.raises(ValueError): 197 X448PrivateKey.from_private_bytes(b"a" * 57) 198 199 def test_invalid_private_bytes(self, backend): 200 key = X448PrivateKey.generate() 201 with pytest.raises(ValueError): 202 key.private_bytes( 203 serialization.Encoding.Raw, 204 serialization.PrivateFormat.Raw, 205 None, 206 ) 207 208 with pytest.raises(ValueError): 209 key.private_bytes( 210 serialization.Encoding.Raw, 211 serialization.PrivateFormat.PKCS8, 212 None, 213 ) 214 215 with pytest.raises(ValueError): 216 key.private_bytes( 217 serialization.Encoding.PEM, 218 serialization.PrivateFormat.Raw, 219 serialization.NoEncryption(), 220 ) 221 222 def test_invalid_public_bytes(self, backend): 223 key = X448PrivateKey.generate().public_key() 224 with pytest.raises(ValueError): 225 key.public_bytes( 226 serialization.Encoding.Raw, 227 serialization.PublicFormat.SubjectPublicKeyInfo, 228 ) 229 230 with pytest.raises(ValueError): 231 key.public_bytes( 232 serialization.Encoding.PEM, serialization.PublicFormat.PKCS1 233 ) 234 235 with pytest.raises(ValueError): 236 key.public_bytes( 237 serialization.Encoding.PEM, serialization.PublicFormat.Raw 238 ) 239 240 def test_buffer_protocol(self, backend): 241 private_bytes = binascii.unhexlify( 242 b"9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28d" 243 b"d9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b" 244 ) 245 key = X448PrivateKey.from_private_bytes(bytearray(private_bytes)) 246 assert ( 247 key.private_bytes( 248 serialization.Encoding.Raw, 249 serialization.PrivateFormat.Raw, 250 serialization.NoEncryption(), 251 ) 252 == private_bytes 253 ) 254