x25519.py 4.5 KB

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