metasploit-framework/lib/rex/proto/kerberos/crypto/des3_cbc_sha1.rb

100 lines
3.5 KiB
Ruby

# -*- coding: binary -*-
require 'rex/text'
module Rex
module Proto
module Kerberos
module Crypto
class Des3CbcSha1 < BlockCipherBase
include Rex::Proto::Kerberos::Crypto::Asn1Utils
SEED_SIZE = 21
BLOCK_SIZE = 8
PADDING_SIZE = 8
MAC_SIZE = 20
HASH_FUNCTION = 'SHA1'
# Derive an encryption key based on a password and salt for the given cipher type
#
# @param string [Stringl The password to use as the basis for key generation
# @param salt [String] A salt (usually based on domain and username)
# @param params [String] Unused for this encryption type
# @return [String] The derived key
def string_to_key(string, salt, params: nil)
raise Rex::Proto::Kerberos::Model::Error::KerberosError, 'Params not supported for DES' unless params == nil
utf8_encoded = (string + salt).encode('UTF-8').bytes
k = random_to_key(nfold(utf8_encoded, 21))
k = k.pack('C*')
result = derive(k, 'kerberos'.encode('UTF-8'))
result
end
def decrypt_asn1(ciphertext, key, msg_type)
result = decrypt(ciphertext, key, msg_type)
padding_removed = truncate_nulls_after_asn1(result)
end
private
# Decrypts the cipher using DES3-CBC-SHA1 schema
def decrypt_basic(ciphertext, key)
raise Rex::Proto::Kerberos::Model::Error::KerberosError, 'Ciphertext is not a multiple of block length' unless ciphertext.length % BLOCK_SIZE == 0
cipher = OpenSSL::Cipher.new('des-ede3-cbc')
if key.length != cipher.key_len
raise Rex::Proto::Kerberos::Model::Error::KerberosError, "Decryption key length must be #{cipher.key_len} for des-ede3-cbc"
end
cipher.decrypt
cipher.key = key
cipher.padding = 0
decrypted = cipher.update(ciphertext) + cipher.final
decrypted
end
# Encrypts the cipher using DES3-CBC-SHA1 schema
def encrypt_basic(plaintext, key)
cipher = OpenSSL::Cipher.new('des-ede3-cbc')
if key.length != cipher.key_len
raise Rex::Proto::Kerberos::Model::Error::KerberosError, "Encryption key length must be #{cipher.key_len} for des-ede3-cbc"
end
cipher.encrypt
cipher.key = key
cipher.padding = 0
encrypted = cipher.update(plaintext) + cipher.final
encrypted
end
def random_to_key(seed)
def expand(seed)
def parity(b)
# Return b with the low-order bit set to yield odd parity.
b &= ~1
b | (b.digits(2).count(1) + 1) % 2
end
raise Rex::Proto::Kerberos::Model::Error::KerberosError unless seed.length == 7
firstbytes = seed.map {|b| parity(b & ~1)}
tmp = 7.times.map { |i| (seed[i] & 1) << i+1 }
lastbyte = parity(tmp.sum)
keybytes = firstbytes + [lastbyte]
if _is_weak_des_key(keybytes)
keybytes[7] = keybytes[7] ^ 0xF0
end
keybytes
end
raise Rex::Proto::Kerberos::Model::Error::KerberosError unless seed.length == 21
subkeys = seed.each_slice(7).map { |slice| expand(slice) }
subkeys.flatten
end
end
end
end
end
end