0

I'm implimenting my own ssl/tls library for learning purpose. These days when I seed encrypted message to server, I will a receive "bad mac" alert. Today I use this question's record to debug my code.

This is where the strange thing happend. I use his server_random, client_random and master-secret to generate client_write_key and client_write_iv. Then I get the same output as his. However, when I use the client_wrtie_key and client_write_iv to decrypt the "GET / HTTP/1.0\n" message, I get this output:

Q▒W▒            ▒7▒3▒▒▒

This is so different from the correct message! My whole debug output is

C:/Users/ayanamists/.babun/cygwin/home/ayanamists/my_ssl_in_ruby/encrypt_handler/aes_gcm_handler.rb:87:in `final': OpenSSL::Cipher::CipherError
        from C:/Users/ayanamists/.babun/cygwin/home/ayanamists/my_ssl_in_ruby/encrypt_handler/aes_gcm_handler.rb:87:in `recv_decrypt'
        from decrypt.rb:72:in `<main>'
client write key is
 4b 11 9d fb fc 93 0a be 13 00 30 bd 53 c3 bf 78
nonce is
 20 29 ca e2 c9 1d e0 05 e2 ae 50 a8
what to be decrypt:
 a5 7a be e5 5c 18 36 67 b1 36 34 3f ee f4 a3 87 cb 7c f8 30 30
the tag is
 a4 7e 23 0a f2 68 37 8c 4f 33 c8 b5 ba b3 d2 6d
the additional data is
 00 00 00 00 00 00 00 01 17 03 03 00 00 15
decrypter is
 OpenSSL::Cipher::AES
Q▒W▒            ▒7▒3▒▒▒
output is
 bd 8c e8 87 b7 ab c6 f7 eb 31 fd cb 65 4c d4 a9 16 ae 1b ca da
the correct is
 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 30 0a

You can see that my key and nonce have no different with his key and nonce, but why the result is wrong? The code is:

require_relative 'encrypt_message_handler'
require 'pp'

class AES_CGM_Handler
    include EncryptMessageHandler
    attr_accessor :send_cipher, :recv_cipher, :send_implicit, :recv_implicit,
    :send_seq_num, :recv_seq_num
    def initialize(server_random, client_random, certificate = '', length = 0, 
        usage = 'client', version_major = 0x03, version_minor = 0x03)
        if block_given?
            @master = yield
            @version = [0x03, 0x03]
        else
            super(server_random, client_random, certificate)
        end
        # the nonce of AES_GCM is defined by:
        # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        # +  0  1  2  3 | 0  1  2  3  4  5  6  7    +
        # +     salt    |     nonce_explicit        +
        # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        # salt is server_write_iv or client_write_iv, so you need 4 * 2
        # and length need to /8(bit->byte) and * 2(both server and client), so it's length/4
        key_block = (length/4 + 4 * 2).tls_prf(@master, "key expansion", server_random + client_random)
        arr = key_block.unpack "a#{length/8}a#{length/8}a4a4"
        client_write_key = arr[0]
        server_write_key = arr[1]
        client_write_iv = arr[2]
        server_write_iv = arr[3]

        @send_cipher = OpenSSL::Cipher::AES.new(length, :GCM).encrypt
        @recv_cipher = OpenSSL::Cipher::AES.new(length, :GCM).decrypt
        if usage == 'client'
            @send_cipher.key = client_write_key
            @send_implicit = client_write_iv
            @recv_cipher.key = server_write_key
            @recv_implicit = server_write_iv
            puts "server write key is #{server_write_key.to_hex}"
        elsif usage == 'server'
            @send_cipher.key = server_write_key
            @send_implicit = server_write_iv
            @recv_cipher.key = client_write_key
            @recv_implicit = client_write_iv
            puts "client write key is\n #{client_write_key.to_hex}"
        else
            raise "AES_GCM_HANDLER: BAD_ARGUMENT"
        end

        @send_seq_num = 0
        @recv_seq_num = 0
    end

    def send_encrypt(type = 22, seqence = '')
        nonce_explicit = OpenSSL::Random.random_bytes(8)
        nonce = @send_implicit + nonce_explicit
        @send_cipher.iv = nonce
        length = seqence.length
        #the handle of seq_num may be wrong
        @send_cipher.auth_data = [0, @send_seq_num,
            type, @version[0], @version[1], 0 ,length].pack("NNCCCCn")
        encrypt = @send_cipher.update(seqence) + @send_cipher.final
        encrypt = encrypt + @send_cipher.auth_tag
        return encrypt
    end

    def recv_decrypt(type = 22, sequence = '', seq_num = 0)
        if seq_num != 0
            @recv_seq_num = seq_num
        end
        template = 'a8a*'
        arr = sequence.unpack(template)
        nonce_explicit = arr[0]
        length = sequence.length - 8 - 16
        sequence = arr[1]
        encrypted = sequence[0, sequence.length - 16]
        @recv_cipher.auth_tag = sequence[sequence.length - 16, sequence.length]
        @recv_cipher.iv = @recv_implicit + nonce_explicit
        puts "nonce is\n #{(@recv_implicit + nonce_explicit).to_hex}"
        puts "what to be decrypt: \n #{encrypted.to_hex}" 
        puts "the tag is \n #{sequence[sequence.length - 16, sequence.length].to_hex}"
        @recv_cipher.auth_data =
            [0, @recv_seq_num, type ,@version[0], @version[1], 0 ,length].pack("NNCCCCn")
        puts "the additional data is\n #{([0, @recv_seq_num, type ,@version[0], @version[1], 0 ,length].pack("NNCCCCn")).to_hex }"
        puts "decrypter is\n #{@recv_cipher.class}"
        puts @recv_cipher.update(encrypted)
        puts "output is\n #{@recv_cipher.update(encrypted).to_hex}"
        puts "the correct is\n #{"GET / HTTP/1.0\n".to_hex}"
        decrypt = @recv_cipher.update(encrypted) + @recv_cipher.final
        return decrypt
    end
end

Please help me to solve this problem, I'll be very grateful!

chenxi li
  • 1
  • 1

1 Answers1

0

I have solved this question eventually! If anyone want to use this test vector, do not forget to add "789c" as zlib compress

chenxi li
  • 1
  • 1