x25519.py 4.4 KB

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