-1

I've written a code in php (as a service) to decrypt passwords sent over a protocol. The protocol demands the password to be "Mac then Encrypt"ed (MtE) using AES256, then encoded using base-64.

The message structure is as described in this comment on php.net.

base46encoded (iv + ecrypted(mac + password))

The process was easy using php

public static function getPassword($password, $key, $mac_algorithm = 'sha1',
                                       $enc_algorithm = MCRYPT_RIJNDAEL_256, $enc_mode = MCRYPT_MODE_CBC)
    {
        // truncating pre-shared key to 32 bytes.
        $key = substr($key, 0, 32);

        // decoding the message (being a password) from base64
        $password = base64_decode($password);

        // getting the iv size based on algorithm and encryption mode
        $iv_size = mcrypt_get_iv_size($enc_algorithm, $enc_mode);

        // extracting iv from message header (normally the first 32 byte) for decryption
        $iv_dec = substr($password, 0, $iv_size);

        // getting the encrypted message after the header (after the first 32 byte)
        $password = substr($password, $iv_size);

        // decrypting message using the pre-shared key and extracted iv
        $password = mcrypt_decrypt($enc_algorithm, $key, $password, $enc_mode, $iv_dec);

        // getting block size for hash algorithm in bytes (sha1 block size is 160 bit)
        $mac_block_size = ceil(static::getMacAlgoBlockSize($mac_algorithm)/8);

        // extracting the mac from the header of decrypted message
        $mac_dec = substr($password, 0, $mac_block_size);

        // extracting the valuable message
        $password = substr($password, $mac_block_size);

        // eliminate extra null terminators padded as the result of enc/decryption the following if and the next statement are check clauses for unpack function
        $password = unpack('Z*', $password);
        if (!isset($password[1]))
        {
            return false;
        }

        // obtaining the pure intended message (being the password) from the unpack result
        $password = $password[1];

        // regenerating the mac to control the authenticity and correctness of transmission
        $mac = hash_hmac($mac_algorithm, $password, $key, true);

        // see if transmitted mac (mac_dec) and the generated mac are the same and the data is valid
        if($mac_dec == $mac)
        {
            return $password;
        }
        else
        {
            return false;
        }
    }

Now the problem is, the application is developed around this protocol in iOS, and tried AESCrypt and CCCrypt, but the decryption results are different (gibberish).

We used standard CCHmac, Base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn and This SO answer for CCCrypt.

Community
  • 1
  • 1
Tala
  • 909
  • 10
  • 29
  • Sorry but I am getting lost in the php (it has been a long while) and the reuse of variable names for different things: $password is the input Base64 password, the binary password, the result of decryption and then an element of itself. It would also help me if I understood what "dec" meant in the variable names. Variable naming is very important to understanding code. – zaph Feb 21 '15 at 13:18
  • @Zaph I added comments on php. Thank you so much for your inputs and expertise on this matter. – Tala Feb 21 '15 at 14:58
  • 1
    AES used a 128-bit (16-byte) iv, the same as the block size. Common Crypto supports AES so comment "extracting iv from message header (normally the first 32 byte)" is incorrect, check the iv size in and block size in the ph code. Rijndael can have one of several block sizes: 128, 192 and 256 bit block sizes. Basically do not use Rijndael with 192 or 256 block sizes because it i=has not been as well studied and potentially less secure because of that. – zaph Feb 21 '15 at 22:47
  • @Zaph I know this level of security is somehow absurd in the application in design, but it is quoted from NSA in [this article](http://en.wikipedia.org/wiki/Advanced_Encryption_Standard#Security) that 192 and 256 is tested by NASA and 128 can't be used for documents up to TOP SECRET level of confidentiality, though 192 and 256 bit block sizes is recommended for those documents. I will use Rijndael 128, but it would be nice that Common Crypto would have supported more. I very much appreciate you effort. If you post that as an answer I will mark it as answer. Thanks a lot! – Tala Feb 22 '15 at 11:57
  • 1
    You misunderstand the document, it states: "TOP SECRET information will require use of either the 192 or 256 key lengths." **key lengths**, not **block sizes**, they are two different things. AES supports 128, 192 and 256 bit key sizes with a 128-bit block size. Unless you are a domain expert with 10K+ hours of experience in cryptographic security do not design your own security. Domain experts do not rely on Wikipedia. – zaph Feb 22 '15 at 12:31
  • 1
    The first thing to understand is the meaning of these constants: MCRYPT_RIJNDAEL_128 MCRYPT_RIJNDAEL_192 MCRYPT_RIJNDAEL_256 The three choices specify the **block-size** to be used with Rijndael encryption **not the key size** (i.e. strength) of the encryption. – zaph Feb 22 '15 at 12:56
  • @Zaph I see know. Thanks for clearing this up for me. – Tala Feb 23 '15 at 13:36

1 Answers1

1

The mcrypt_generic_init function initializes the cipher by specifying both the key and the IV. The length of the key determines whether we're doing 128-bit, 192-bit, or 256-bit encryption.

So use MCRYPT_RIJNDAEL_128 with a 256-bit (32-byte) key to be compatible with AES256.

zaph
  • 111,848
  • 21
  • 189
  • 228