kex_ecdh_nist.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. """
  2. Ephemeral Elliptic Curve Diffie-Hellman (ECDH) key exchange
  3. RFC 5656, Section 4
  4. """
  5. from hashlib import sha256, sha384, sha512
  6. from paramiko.message import Message
  7. from paramiko.py3compat import byte_chr, long
  8. from paramiko.ssh_exception import SSHException
  9. from cryptography.hazmat.backends import default_backend
  10. from cryptography.hazmat.primitives.asymmetric import ec
  11. from cryptography.hazmat.primitives import serialization
  12. from binascii import hexlify
  13. _MSG_KEXECDH_INIT, _MSG_KEXECDH_REPLY = range(30, 32)
  14. c_MSG_KEXECDH_INIT, c_MSG_KEXECDH_REPLY = [byte_chr(c) for c in range(30, 32)]
  15. class KexNistp256:
  16. name = "ecdh-sha2-nistp256"
  17. hash_algo = sha256
  18. curve = ec.SECP256R1()
  19. def __init__(self, transport):
  20. self.transport = transport
  21. # private key, client public and server public keys
  22. self.P = long(0)
  23. self.Q_C = None
  24. self.Q_S = None
  25. def start_kex(self):
  26. self._generate_key_pair()
  27. if self.transport.server_mode:
  28. self.transport._expect_packet(_MSG_KEXECDH_INIT)
  29. return
  30. m = Message()
  31. m.add_byte(c_MSG_KEXECDH_INIT)
  32. # SEC1: V2.0 2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion
  33. m.add_string(
  34. self.Q_C.public_bytes(
  35. serialization.Encoding.X962,
  36. serialization.PublicFormat.UncompressedPoint,
  37. )
  38. )
  39. self.transport._send_message(m)
  40. self.transport._expect_packet(_MSG_KEXECDH_REPLY)
  41. def parse_next(self, ptype, m):
  42. if self.transport.server_mode and (ptype == _MSG_KEXECDH_INIT):
  43. return self._parse_kexecdh_init(m)
  44. elif not self.transport.server_mode and (ptype == _MSG_KEXECDH_REPLY):
  45. return self._parse_kexecdh_reply(m)
  46. raise SSHException(
  47. "KexECDH asked to handle packet type {:d}".format(ptype)
  48. )
  49. def _generate_key_pair(self):
  50. self.P = ec.generate_private_key(self.curve, default_backend())
  51. if self.transport.server_mode:
  52. self.Q_S = self.P.public_key()
  53. return
  54. self.Q_C = self.P.public_key()
  55. def _parse_kexecdh_init(self, m):
  56. Q_C_bytes = m.get_string()
  57. self.Q_C = ec.EllipticCurvePublicKey.from_encoded_point(
  58. self.curve, Q_C_bytes
  59. )
  60. K_S = self.transport.get_server_key().asbytes()
  61. K = self.P.exchange(ec.ECDH(), self.Q_C)
  62. K = long(hexlify(K), 16)
  63. # compute exchange hash
  64. hm = Message()
  65. hm.add(
  66. self.transport.remote_version,
  67. self.transport.local_version,
  68. self.transport.remote_kex_init,
  69. self.transport.local_kex_init,
  70. )
  71. hm.add_string(K_S)
  72. hm.add_string(Q_C_bytes)
  73. # SEC1: V2.0 2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion
  74. hm.add_string(
  75. self.Q_S.public_bytes(
  76. serialization.Encoding.X962,
  77. serialization.PublicFormat.UncompressedPoint,
  78. )
  79. )
  80. hm.add_mpint(long(K))
  81. H = self.hash_algo(hm.asbytes()).digest()
  82. self.transport._set_K_H(K, H)
  83. sig = self.transport.get_server_key().sign_ssh_data(
  84. H, self.transport.host_key_type
  85. )
  86. # construct reply
  87. m = Message()
  88. m.add_byte(c_MSG_KEXECDH_REPLY)
  89. m.add_string(K_S)
  90. m.add_string(
  91. self.Q_S.public_bytes(
  92. serialization.Encoding.X962,
  93. serialization.PublicFormat.UncompressedPoint,
  94. )
  95. )
  96. m.add_string(sig)
  97. self.transport._send_message(m)
  98. self.transport._activate_outbound()
  99. def _parse_kexecdh_reply(self, m):
  100. K_S = m.get_string()
  101. Q_S_bytes = m.get_string()
  102. self.Q_S = ec.EllipticCurvePublicKey.from_encoded_point(
  103. self.curve, Q_S_bytes
  104. )
  105. sig = m.get_binary()
  106. K = self.P.exchange(ec.ECDH(), self.Q_S)
  107. K = long(hexlify(K), 16)
  108. # compute exchange hash and verify signature
  109. hm = Message()
  110. hm.add(
  111. self.transport.local_version,
  112. self.transport.remote_version,
  113. self.transport.local_kex_init,
  114. self.transport.remote_kex_init,
  115. )
  116. hm.add_string(K_S)
  117. # SEC1: V2.0 2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion
  118. hm.add_string(
  119. self.Q_C.public_bytes(
  120. serialization.Encoding.X962,
  121. serialization.PublicFormat.UncompressedPoint,
  122. )
  123. )
  124. hm.add_string(Q_S_bytes)
  125. hm.add_mpint(K)
  126. self.transport._set_K_H(K, self.hash_algo(hm.asbytes()).digest())
  127. self.transport._verify_key(K_S, sig)
  128. self.transport._activate_outbound()
  129. class KexNistp384(KexNistp256):
  130. name = "ecdh-sha2-nistp384"
  131. hash_algo = sha384
  132. curve = ec.SECP384R1()
  133. class KexNistp521(KexNistp256):
  134. name = "ecdh-sha2-nistp521"
  135. hash_algo = sha512
  136. curve = ec.SECP521R1()