Class: Rex::Proto::Kerberos::Crypto::AesBlockCipherBase

Inherits:
BlockCipherBase show all
Includes:
GssNewEncryptionType, Utils
Defined in:
lib/rex/proto/kerberos/crypto/aes_block_cipher_base.rb

Overview

Base class for RFC3962 AES encryption classes

Direct Known Subclasses

Aes128CtsSha1, Aes256CtsSha1

Constant Summary collapse

BLOCK_SIZE =
16
PADDING_SIZE =
1
MAC_SIZE =
12
HASH_FUNCTION =
'SHA1'

Constants included from GssNewEncryptionType

GssNewEncryptionType::GSS_ACCEPTOR_SUBKEY, GssNewEncryptionType::GSS_HEADER_LEN, GssNewEncryptionType::GSS_SEALED, GssNewEncryptionType::GSS_SENT_BY_ACCEPTOR, GssNewEncryptionType::TOK_ID_GSS_WRAP

Instance Method Summary collapse

Methods included from GssNewEncryptionType

#gss_unwrap, #gss_wrap

Methods included from Utils

#xor_bytes, #xor_strings

Methods inherited from BlockCipherBase

#add_ones_complement, #checksum, #decrypt, #encrypt, #gss_unwrap, #gss_wrap, #rotate_right

Instance Method Details

#decrypt_basic(ciphertext, key) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/rex/proto/kerberos/crypto/aes_block_cipher_base.rb', line 55

def decrypt_basic(ciphertext, key)
  raise Rex::Proto::Kerberos::Model::Error::KerberosError, 'Ciphertext too short' if ciphertext.length < BLOCK_SIZE

  cipher = OpenSSL::Cipher.new(self.class::DECRYPT_CIPHER_NAME)
  cipher.decrypt
  cipher.key = key
  cipher.padding = 0

  if ciphertext.length == BLOCK_SIZE
    return cipher.update(ciphertext) + cipher.final
  end

  # Split the ciphertext into blocks.  The last block may be partial.
  block_chunks = ciphertext.unpack('C*').each_slice(BLOCK_SIZE).to_a
  last_block_length = block_chunks[-1].length

  # CBC-decrypt all but the last two blocks.
  prev_chunk = [0] * BLOCK_SIZE
  plaintext_arr = []
  block_chunks.slice(0..-3).each do |chunk|
    decrypted = cipher.update(chunk.pack('C*')) + cipher.final
    decrypted_arr = decrypted.unpack('C*')
    plaintext_arr += xor_bytes(decrypted_arr, prev_chunk)
    prev_chunk = chunk
  end

  # Decrypt the second-to-last cipher block.  The left side of
  # the decrypted block will be the final block of plaintext
  # xor'd with the final partial cipher block; the right side
  # will be the omitted bytes of ciphertext from the final
  # block.
  decrypted = cipher.update(block_chunks[-2].pack('C*')) + cipher.final
  decrypted_arr = decrypted.unpack('C*')
  last_plaintext_arr = xor_bytes(decrypted_arr[0, last_block_length], block_chunks[-1])
  omitted_arr = decrypted_arr[last_block_length, decrypted.length]

  # Decrypt the final cipher block plus the omitted bytes to get
  # the second-to-last plaintext block.

  decrypted = cipher.update((block_chunks[-1] + omitted_arr).pack('C*'))
  decrypted_arr = decrypted.unpack('C*')
  plaintext_arr += xor_bytes(decrypted_arr, prev_chunk)
  (plaintext_arr + last_plaintext_arr).pack('C*')
end

#encrypt_basic(plaintext, key) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/rex/proto/kerberos/crypto/aes_block_cipher_base.rb', line 34

def encrypt_basic(plaintext, key)
  raise Rex::Proto::Kerberos::Model::Error::KerberosError, 'Ciphertext too short' if plaintext.length < BLOCK_SIZE

  cipher = OpenSSL::Cipher.new(self.class::ENCRYPT_CIPHER_NAME)
  cipher.encrypt
  cipher.key = key
  cipher.padding = 0

  padded = pad_with_zeroes(plaintext, BLOCK_SIZE)
  ciphertext = cipher.update(padded) + cipher.final
  if plaintext.length > BLOCK_SIZE
    # Swap the last two ciphertext blocks and truncate the
    # final block to match the plaintext length.
    last_block_length = plaintext.length % BLOCK_SIZE
    last_block_length = BLOCK_SIZE if last_block_length == 0
    ciphertext = ciphertext[0, ciphertext.length - 32] + ciphertext[-BLOCK_SIZE, BLOCK_SIZE] + ciphertext[-32, last_block_length]
  end

  ciphertext
end

#header_byte_countObject

The number of bytes in the encrypted plaintext that precede the actual plaintext



103
104
105
# File 'lib/rex/proto/kerberos/crypto/aes_block_cipher_base.rb', line 103

def header_byte_count
  BLOCK_SIZE
end

#string_to_key(password, salt, params: nil) ⇒ String

Derive an encryption key based on a password and salt for the given cipher type

Parameters:

  • password (String)

    The password to use as the basis for key generation

  • salt (String)

    A salt (usually based on domain and username)

  • params (String) (defaults to: nil)

    When unpacked, the number of iterations used during key generation

Returns:

  • (String)

    The derived key



26
27
28
29
30
31
32
# File 'lib/rex/proto/kerberos/crypto/aes_block_cipher_base.rb', line 26

def string_to_key(password, salt, params: nil)
  params = "\x00\x00\x10\x00" if params == nil
  iterations = params.unpack('N')[0]
  seed = OpenSSL::KDF.pbkdf2_hmac(password, salt: salt, iterations: iterations, length: self.class::SEED_SIZE, hash: HASH_FUNCTION)
  tkey = random_to_key(seed)
  derive(tkey, 'kerberos'.encode('utf-8'))
end

#trailing_byte_countObject

The number of bytes in the encrypted plaintext that follow the actual plaintext



110
111
112
# File 'lib/rex/proto/kerberos/crypto/aes_block_cipher_base.rb', line 110

def trailing_byte_count
  MAC_SIZE
end