slight update
This commit is contained in:
135
env/lib/python3.12/site-packages/Crypto/PublicKey/_openssh.py
vendored
Normal file
135
env/lib/python3.12/site-packages/Crypto/PublicKey/_openssh.py
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
# ===================================================================
|
||||
#
|
||||
# Copyright (c) 2019, Helder Eijs <helderijs@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
# ===================================================================
|
||||
|
||||
import struct
|
||||
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Hash import SHA512
|
||||
from Crypto.Protocol.KDF import _bcrypt_hash
|
||||
from Crypto.Util.strxor import strxor
|
||||
from Crypto.Util.py3compat import tostr, bchr, bord
|
||||
|
||||
|
||||
def read_int4(data):
|
||||
if len(data) < 4:
|
||||
raise ValueError("Insufficient data")
|
||||
value = struct.unpack(">I", data[:4])[0]
|
||||
return value, data[4:]
|
||||
|
||||
|
||||
def read_bytes(data):
|
||||
size, data = read_int4(data)
|
||||
if len(data) < size:
|
||||
raise ValueError("Insufficient data (V)")
|
||||
return data[:size], data[size:]
|
||||
|
||||
|
||||
def read_string(data):
|
||||
s, d = read_bytes(data)
|
||||
return tostr(s), d
|
||||
|
||||
|
||||
def check_padding(pad):
|
||||
for v, x in enumerate(pad):
|
||||
if bord(x) != ((v + 1) & 0xFF):
|
||||
raise ValueError("Incorrect padding")
|
||||
|
||||
|
||||
def import_openssh_private_generic(data, password):
|
||||
# https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD
|
||||
# https://github.com/openssh/openssh-portable/blob/master/sshkey.c
|
||||
# https://coolaj86.com/articles/the-openssh-private-key-format/
|
||||
# https://coolaj86.com/articles/the-ssh-public-key-format/
|
||||
|
||||
if not data.startswith(b'openssh-key-v1\x00'):
|
||||
raise ValueError("Incorrect magic value")
|
||||
data = data[15:]
|
||||
|
||||
ciphername, data = read_string(data)
|
||||
kdfname, data = read_string(data)
|
||||
kdfoptions, data = read_bytes(data)
|
||||
number_of_keys, data = read_int4(data)
|
||||
|
||||
if number_of_keys != 1:
|
||||
raise ValueError("We only handle 1 key at a time")
|
||||
|
||||
_, data = read_string(data) # Public key
|
||||
encrypted, data = read_bytes(data)
|
||||
if data:
|
||||
raise ValueError("Too much data")
|
||||
|
||||
if len(encrypted) % 8 != 0:
|
||||
raise ValueError("Incorrect payload length")
|
||||
|
||||
# Decrypt if necessary
|
||||
if ciphername == 'none':
|
||||
decrypted = encrypted
|
||||
else:
|
||||
if (ciphername, kdfname) != ('aes256-ctr', 'bcrypt'):
|
||||
raise ValueError("Unsupported encryption scheme %s/%s" % (ciphername, kdfname))
|
||||
|
||||
salt, kdfoptions = read_bytes(kdfoptions)
|
||||
iterations, kdfoptions = read_int4(kdfoptions)
|
||||
|
||||
if len(salt) != 16:
|
||||
raise ValueError("Incorrect salt length")
|
||||
if kdfoptions:
|
||||
raise ValueError("Too much data in kdfoptions")
|
||||
|
||||
pwd_sha512 = SHA512.new(password).digest()
|
||||
# We need 32+16 = 48 bytes, therefore 2 bcrypt outputs are sufficient
|
||||
stripes = []
|
||||
constant = b"OxychromaticBlowfishSwatDynamite"
|
||||
for count in range(1, 3):
|
||||
salt_sha512 = SHA512.new(salt + struct.pack(">I", count)).digest()
|
||||
out_le = _bcrypt_hash(pwd_sha512, 6, salt_sha512, constant, False)
|
||||
out = struct.pack("<IIIIIIII", *struct.unpack(">IIIIIIII", out_le))
|
||||
acc = bytearray(out)
|
||||
for _ in range(1, iterations):
|
||||
out_le = _bcrypt_hash(pwd_sha512, 6, SHA512.new(out).digest(), constant, False)
|
||||
out = struct.pack("<IIIIIIII", *struct.unpack(">IIIIIIII", out_le))
|
||||
strxor(acc, out, output=acc)
|
||||
stripes.append(acc[:24])
|
||||
|
||||
result = b"".join([bchr(a)+bchr(b) for (a, b) in zip(*stripes)])
|
||||
|
||||
cipher = AES.new(result[:32],
|
||||
AES.MODE_CTR,
|
||||
nonce=b"",
|
||||
initial_value=result[32:32+16])
|
||||
decrypted = cipher.decrypt(encrypted)
|
||||
|
||||
checkint1, decrypted = read_int4(decrypted)
|
||||
checkint2, decrypted = read_int4(decrypted)
|
||||
if checkint1 != checkint2:
|
||||
raise ValueError("Incorrect checksum")
|
||||
ssh_name, decrypted = read_string(decrypted)
|
||||
|
||||
return ssh_name, decrypted
|
Reference in New Issue
Block a user