PageRenderTime 56ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/Windows/Python3.8/WPy64-3830/WPy64-3830/python-3.8.3.amd64/Lib/site-packages/Crypto/PublicKey/_openssh.py

https://gitlab.com/abhi1tb/build
Python | 135 lines | 78 code | 22 blank | 35 comment | 15 complexity | 2d9d751d05097a416224695a2ec3b815 MD5 | raw file
  1. # ===================================================================
  2. #
  3. # Copyright (c) 2019, Helder Eijs <helderijs@gmail.com>
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions
  8. # are met:
  9. #
  10. # 1. Redistributions of source code must retain the above copyright
  11. # notice, this list of conditions and the following disclaimer.
  12. # 2. Redistributions in binary form must reproduce the above copyright
  13. # notice, this list of conditions and the following disclaimer in
  14. # the documentation and/or other materials provided with the
  15. # distribution.
  16. #
  17. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  25. # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  26. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  27. # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. # POSSIBILITY OF SUCH DAMAGE.
  29. # ===================================================================
  30. import struct
  31. from Crypto.Cipher import AES
  32. from Crypto.Hash import SHA512
  33. from Crypto.Protocol.KDF import _bcrypt_hash
  34. from Crypto.Util.strxor import strxor
  35. from Crypto.Util.py3compat import tostr, bchr, bord
  36. def read_int4(data):
  37. if len(data) < 4:
  38. raise ValueError("Insufficient data")
  39. value = struct.unpack(">I", data[:4])[0]
  40. return value, data[4:]
  41. def read_bytes(data):
  42. size, data = read_int4(data)
  43. if len(data) < size:
  44. raise ValueError("Insufficient data (V)")
  45. return data[:size], data[size:]
  46. def read_string(data):
  47. s, d = read_bytes(data)
  48. return tostr(s), d
  49. def check_padding(pad):
  50. for v, x in enumerate(pad):
  51. if bord(x) != ((v + 1) & 0xFF):
  52. raise ValueError("Incorrect padding")
  53. def import_openssh_private_generic(data, password):
  54. # https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD
  55. # https://github.com/openssh/openssh-portable/blob/master/sshkey.c
  56. # https://coolaj86.com/articles/the-openssh-private-key-format/
  57. # https://coolaj86.com/articles/the-ssh-public-key-format/
  58. if not data.startswith(b'openssh-key-v1\x00'):
  59. raise ValueError("Incorrect magic value")
  60. data = data[15:]
  61. ciphername, data = read_string(data)
  62. kdfname, data = read_string(data)
  63. kdfoptions, data = read_bytes(data)
  64. number_of_keys, data = read_int4(data)
  65. if number_of_keys != 1:
  66. raise ValueError("We only handle 1 key at a time")
  67. _, data = read_string(data) # Public key
  68. encrypted, data = read_bytes(data)
  69. if data:
  70. raise ValueError("Too much data")
  71. if len(encrypted) % 8 != 0:
  72. raise ValueError("Incorrect payload length")
  73. # Decrypt if necessary
  74. if ciphername == 'none':
  75. decrypted = encrypted
  76. else:
  77. if (ciphername, kdfname) != ('aes256-ctr', 'bcrypt'):
  78. raise ValueError("Unsupported encryption scheme %s/%s" % (ciphername, kdfname))
  79. salt, kdfoptions = read_bytes(kdfoptions)
  80. iterations, kdfoptions = read_int4(kdfoptions)
  81. if len(salt) != 16:
  82. raise ValueError("Incorrect salt length")
  83. if kdfoptions:
  84. raise ValueError("Too much data in kdfoptions")
  85. pwd_sha512 = SHA512.new(password).digest()
  86. # We need 32+16 = 48 bytes, therefore 2 bcrypt outputs are sufficient
  87. stripes = []
  88. constant = b"OxychromaticBlowfishSwatDynamite"
  89. for count in range(1, 3):
  90. salt_sha512 = SHA512.new(salt + struct.pack(">I", count)).digest()
  91. out_le = _bcrypt_hash(pwd_sha512, 6, salt_sha512, constant, False)
  92. out = struct.pack("<IIIIIIII", *struct.unpack(">IIIIIIII", out_le))
  93. acc = bytearray(out)
  94. for _ in range(1, iterations):
  95. out_le = _bcrypt_hash(pwd_sha512, 6, SHA512.new(out).digest(), constant, False)
  96. out = struct.pack("<IIIIIIII", *struct.unpack(">IIIIIIII", out_le))
  97. strxor(acc, out, output=acc)
  98. stripes.append(acc[:24])
  99. result = b"".join([bchr(a)+bchr(b) for (a, b) in zip(*stripes)])
  100. cipher = AES.new(result[:32],
  101. AES.MODE_CTR,
  102. nonce=b"",
  103. initial_value=result[32:32+16])
  104. decrypted = cipher.decrypt(encrypted)
  105. checkint1, decrypted = read_int4(decrypted)
  106. checkint2, decrypted = read_int4(decrypted)
  107. if checkint1 != checkint2:
  108. raise ValueError("Incorrect checksum")
  109. ssh_name, decrypted = read_string(decrypted)
  110. return ssh_name, decrypted