Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/cryptography/hazmat/backends/openssl/x25519.py: 30%

53 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +0000

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 

5import typing 

6 

7from cryptography.hazmat.backends.openssl.utils import _evp_pkey_derive 

8from cryptography.hazmat.primitives import serialization 

9from cryptography.hazmat.primitives.asymmetric.x25519 import ( 

10 X25519PrivateKey, 

11 X25519PublicKey, 

12) 

13 

14if typing.TYPE_CHECKING: 

15 from cryptography.hazmat.backends.openssl.backend import Backend 

16 

17 

18_X25519_KEY_SIZE = 32 

19 

20 

21class _X25519PublicKey(X25519PublicKey): 

22 def __init__(self, backend: "Backend", evp_pkey): 

23 self._backend = backend 

24 self._evp_pkey = evp_pkey 

25 

26 def public_bytes( 

27 self, 

28 encoding: serialization.Encoding, 

29 format: serialization.PublicFormat, 

30 ) -> bytes: 

31 if ( 

32 encoding is serialization.Encoding.Raw 

33 or format is serialization.PublicFormat.Raw 

34 ): 

35 if ( 

36 encoding is not serialization.Encoding.Raw 

37 or format is not serialization.PublicFormat.Raw 

38 ): 

39 raise ValueError( 

40 "When using Raw both encoding and format must be Raw" 

41 ) 

42 

43 return self._raw_public_bytes() 

44 

45 return self._backend._public_key_bytes( 

46 encoding, format, self, self._evp_pkey, None 

47 ) 

48 

49 def _raw_public_bytes(self) -> bytes: 

50 ucharpp = self._backend._ffi.new("unsigned char **") 

51 res = self._backend._lib.EVP_PKEY_get1_tls_encodedpoint( 

52 self._evp_pkey, ucharpp 

53 ) 

54 self._backend.openssl_assert(res == 32) 

55 self._backend.openssl_assert(ucharpp[0] != self._backend._ffi.NULL) 

56 data = self._backend._ffi.gc( 

57 ucharpp[0], self._backend._lib.OPENSSL_free 

58 ) 

59 return self._backend._ffi.buffer(data, res)[:] 

60 

61 

62class _X25519PrivateKey(X25519PrivateKey): 

63 def __init__(self, backend: "Backend", evp_pkey): 

64 self._backend = backend 

65 self._evp_pkey = evp_pkey 

66 

67 def public_key(self) -> X25519PublicKey: 

68 bio = self._backend._create_mem_bio_gc() 

69 res = self._backend._lib.i2d_PUBKEY_bio(bio, self._evp_pkey) 

70 self._backend.openssl_assert(res == 1) 

71 evp_pkey = self._backend._lib.d2i_PUBKEY_bio( 

72 bio, self._backend._ffi.NULL 

73 ) 

74 self._backend.openssl_assert(evp_pkey != self._backend._ffi.NULL) 

75 evp_pkey = self._backend._ffi.gc( 

76 evp_pkey, self._backend._lib.EVP_PKEY_free 

77 ) 

78 return _X25519PublicKey(self._backend, evp_pkey) 

79 

80 def exchange(self, peer_public_key: X25519PublicKey) -> bytes: 

81 if not isinstance(peer_public_key, X25519PublicKey): 

82 raise TypeError("peer_public_key must be X25519PublicKey.") 

83 

84 return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key) 

85 

86 def private_bytes( 

87 self, 

88 encoding: serialization.Encoding, 

89 format: serialization.PrivateFormat, 

90 encryption_algorithm: serialization.KeySerializationEncryption, 

91 ) -> bytes: 

92 if ( 

93 encoding is serialization.Encoding.Raw 

94 or format is serialization.PublicFormat.Raw 

95 ): 

96 if ( 

97 format is not serialization.PrivateFormat.Raw 

98 or encoding is not serialization.Encoding.Raw 

99 or not isinstance( 

100 encryption_algorithm, serialization.NoEncryption 

101 ) 

102 ): 

103 raise ValueError( 

104 "When using Raw both encoding and format must be Raw " 

105 "and encryption_algorithm must be NoEncryption()" 

106 ) 

107 

108 return self._raw_private_bytes() 

109 

110 return self._backend._private_key_bytes( 

111 encoding, format, encryption_algorithm, self, self._evp_pkey, None 

112 ) 

113 

114 def _raw_private_bytes(self) -> bytes: 

115 # When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 we can 

116 # switch this to EVP_PKEY_new_raw_private_key 

117 # The trick we use here is serializing to a PKCS8 key and just 

118 # using the last 32 bytes, which is the key itself. 

119 bio = self._backend._create_mem_bio_gc() 

120 res = self._backend._lib.i2d_PKCS8PrivateKey_bio( 

121 bio, 

122 self._evp_pkey, 

123 self._backend._ffi.NULL, 

124 self._backend._ffi.NULL, 

125 0, 

126 self._backend._ffi.NULL, 

127 self._backend._ffi.NULL, 

128 ) 

129 self._backend.openssl_assert(res == 1) 

130 pkcs8 = self._backend._read_mem_bio(bio) 

131 self._backend.openssl_assert(len(pkcs8) == 48) 

132 return pkcs8[-_X25519_KEY_SIZE:]