Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/proto/kerberos/crypto/des_cbc_md5.rb
Views: 11766
# -*- coding: binary -*-12require 'rex/text'34module Rex5module Proto6module Kerberos7module Crypto8class DesCbcMd5 < BlockCipherBase9include Rex::Proto::Kerberos::Crypto::Asn1Utils1011HASH_LENGTH = 1612BLOCK_SIZE = 813PADDING_SIZE = 814MAC_SIZE = 161516# Derive an encryption key based on a password and salt for the given cipher type17#18# @param password [String] The password to use as the basis for key generation19# @param salt [String] A salt (usually based on domain and username)20# @param params [String] Unused for this encryption type21# @return [String] The derived key22def string_to_key(password, salt, params: nil)23raise Rex::Proto::Kerberos::Model::Error::KerberosError, 'Params not supported for DES' unless params == nil24reverse_this_block = false25tempstring = [0,0,0,0,0,0,0,0]2627utf8_encoded = (password + salt).encode('UTF-8').bytes.pack('C*')2829data = pad_with_zeroes(utf8_encoded, PADDING_SIZE)30data_as_blocks = data.unpack('C*')3132data_as_blocks.each_slice(BLOCK_SIZE) do |block|33result = []34block.each do |byte|35# Ignore the Most Significant Bit of each byte36result.append(byte & 0x7F)37end3839if reverse_this_block40reversed = []41result.reverse.each do |byte|42d = byte.digits(2)43d = d + [0] * (7 - d.length)44reversed.append(d.join('').to_i(2))45end4647result = reversed48end4950reverse_this_block = (not reverse_this_block)5152tempstring = xor_bytes(tempstring,result)53end5455paritied = addparity(tempstring)56tempkey = paritied.pack('C*')5758if _is_weak_des_key(tempkey)59paritied[7] = paritied[7] ^ 0xF060tempkey = paritied.pack('C*')61end6263cipher = OpenSSL::Cipher.new('des-cbc')64cipher.encrypt65cipher.padding = 066cipher.key = tempkey67cipher.iv = tempkey6869encrypted = cipher.update(data) + cipher.final70checksumkey = encrypted7172checksumkey = encrypted[-8,8]73paritied = fixparity(checksumkey.unpack('C*'))74checksumkey = paritied.pack('C*')75if _is_weak_des_key(checksumkey)76paritied[7] = paritied[7] ^ 0xF077checksumkey = paritied.pack('C*')78end7980checksumkey81end8283# Decrypts the cipher using DES-CBC-MD5 schema84#85# @param ciphertext [String] the data to decrypt86# @param key [String] the key to decrypt87# @param msg_type [Integer] ignored for this algorithm88# @return [String] the decrypted cipher89# @raise [Rex::Proto::Kerberos::Model::Error::KerberosError] if decryption doesn't succeed90def decrypt(ciphertext, key, msg_type)91raise Rex::Proto::Kerberos::Model::Error::KerberosError, 'Ciphertext too short' unless ciphertext && ciphertext.length > BLOCK_SIZE + HASH_LENGTH92raise Rex::Proto::Kerberos::Model::Error::KerberosError, 'Ciphertext is not a multiple of block length' unless ciphertext.length % BLOCK_SIZE == 0939495cipher = OpenSSL::Cipher.new('des-cbc')96if key.length != cipher.key_len97raise Rex::Proto::Kerberos::Model::Error::KerberosError, "Decryption key length must be #{cipher.key_len} for des-cbc"98end99cipher.decrypt100cipher.padding = 0101cipher.key = key102decrypted = cipher.update(ciphertext)103104confounder = decrypted[0, BLOCK_SIZE]105checksum = decrypted[BLOCK_SIZE, HASH_LENGTH]106plaintext = decrypted[BLOCK_SIZE + HASH_LENGTH, decrypted.length]107hashed_data = confounder + "\x00" * HASH_LENGTH + plaintext108109hash_fn = OpenSSL::Digest.new('MD5')110111if hash_fn.digest(hashed_data) != checksum112raise Rex::Proto::Kerberos::Model::Error::KerberosError, 'HMAC integrity error'113end114115plaintext116end117118def decrypt_asn1(ciphertext, key, msg_type)119result = decrypt(ciphertext, key, msg_type)120padding_removed = truncate_nulls_after_asn1(result)121end122123# Encrypts the cipher using DES-CBC-MD5 schema124#125# @param plaintext [String] the data to encrypt126# @param key [String] the key to encrypt127# @param msg_type [Integer] ignored for this algorithm128# @param confounder [String] Optionally force the confounder to a specific value129# @return [String] the encrypted data130def encrypt(plaintext, key, msg_type, confounder: nil)131confounder = Rex::Text::rand_text(BLOCK_SIZE) if confounder == nil132padded_data = pad_with_zeroes(plaintext, PADDING_SIZE)133hashed_data = confounder + "\x00" * HASH_LENGTH + padded_data134hash_fn = OpenSSL::Digest.new('MD5')135checksum = hash_fn.digest(hashed_data)136137raise Rex::Proto::Kerberos::Model::Error::KerberosError, 'Invalid checksum size' unless checksum.length == HASH_LENGTH138139plaintext = confounder + checksum + padded_data140141cipher = OpenSSL::Cipher.new('des-cbc')142if key.length != cipher.key_len143raise Rex::Proto::Kerberos::Model::Error::KerberosError, "Encryption key length must be #{cipher.key_len} for des-cbc"144end145cipher.encrypt146cipher.padding = 0147cipher.key = key148encrypted = cipher.update(plaintext) + cipher.final149150encrypted151end152153#154# The number of bytes in the encrypted plaintext that precede the actual plaintext155#156def header_byte_count157BLOCK_SIZE158end159160#161# The number of bytes in the encrypted plaintext that follow the actual plaintext162#163def trailing_byte_count164MAC_SIZE165end166167private168169def fixparity(deskey)170temp = []171deskey.each do |byte|172bits = byte.digits(2).reverse173bits.pop # Ignore the last bit, since it's a parity bit174add_at_end = (bits.count(1) + 1) % 2175bits.append(add_at_end)176parity_fixed = bits.join('').to_i(2)177temp.append(parity_fixed)178end179180temp181end182183def addparity(bytes)184temp = []185bytes.each do |byte|186bits = byte.digits(2).reverse187to_add = (bits.count(1) + 1) % 2188result = (byte << 1) + to_add189temp.append(result & 0xFF)190end191192temp193end194195def xor_bytes(l1,l2)196result = []197l1.zip(l2).each do |b1,b2|198if b1 != nil && b2 != nil199result.append((b1^b2)&0b01111111)200end201end202203result204end205end206end207end208end209end210211212