x25519.py 4.6 KB

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