locked
CryptDecrypt Final Flag RRS feed

  • Question

  • I wrote TLS Client and Server programs that communicate between themselves just fine, but when using a browser to communicate with my Server program, I ran into a problem with the GET request. For the handshake I was using the following call:
    CryptDecrypt(priv->hCryptKey, 0, TRUE, 0, buf, &flen)
    But when it came time for the browser to send the GET request, I got an HMAC error and the request was unreadable. The only way I could get the Server program to decrypt the GET request properly was to change the Final flag to FALSE. I have a similar problem with the response, as the browser would return an HMAC error. Changing the Final flag to FALSE in the Encrypt routine however, causes the handshake to bomb out. I wrote a small routine to test this flag:

    ENCRYPT with True Final
    BC 90 42 4E 55 32 16 C1 10 18 D3 04 8C F2 58 3E
    B9 26 74 3D 2F 8C 97 33 82 F1 29 6D 67 A6 5A BA
    1A 08 4A BE 76 49 2B 66 B7 FE D0 C8 28 0D 3A 73
    DECRYPT with True Final
    This is a test of the encrypt/decrypt functions!
    ENCRYPT with False Final
    BC 90 42 4E 55 32 16 C1 10 18 D3 04 8C F2 58 3E
    B9 26 74 3D 2F 8C 97 33 82 F1 29 6D 67 A6 5A BA
    1A 08 4A BE 76 49 2B 66 B7 FE D0 C8 28 0D 3A 73
    DECRYPT with False Final
    (¦M„Ç_ÛB¡ ¹ÙF×:ÿ«Iw’˜ÅdÅù vV6jsdøÝôèë¸$ÌM­ø
    ENCRYPT with True Final
    88 4F CF 3B DB B4 6A 24 27 41 FB 51 A7 7D 3A 6C
    03 00 9A 94 04 D7 46 CE 5E E4 8C 53 AB 1C 8F 18
    24 23 C7 D2 93 A0 A7 75 A4 5B 63 F0 B7 05 EC 46
    DECRYPT with False Final
    `·ä®ïÅVy\0XûB=ÜšÁN{´“¿gÜN¸•±Ç]Yô‘Éêf}ÆÇQðf¥

    ENCRYPT with True Final
    BC 90 42 4E 55 32 16 C1 10 18 D3 04 8C F2 58 3E
    B9 26 74 3D 2F 8C 97 33 82 F1 29 6D 67 A6 5A BA
    1A 08 4A BE 76 49 2B 66 B7 FE D0 C8 28 0D 3A 73
    DECRYPT with True Final
    This is a test of the encrypt/decrypt functions!
    ENCRYPT with False Final
    BC 90 42 4E 55 32 16 C1 10 18 D3 04 8C F2 58 3E
    B9 26 74 3D 2F 8C 97 33 82 F1 29 6D 67 A6 5A BA
    1A 08 4A BE 76 49 2B 66 B7 FE D0 C8 28 0D 3A 73
    DECRYPT with False Final
    (¦M„Ç_ÛB¡ ¹ÙF×:ÿ«Iw’˜ÅdÅù vV6jsdøÝôèë¸$ÌM­ø
    NULL ENCRYPT with True Final
    ENCRYPT with True Final
    BC 90 42 4E 55 32 16 C1 10 18 D3 04 8C F2 58 3E
    B9 26 74 3D 2F 8C 97 33 82 F1 29 6D 67 A6 5A BA
    1A 08 4A BE 76 49 2B 66 B7 FE D0 C8 28 0D 3A 73
    DECRYPT with False Final
    This is a test of the encrypt/decrypt functions!

    but it is very obvious that I don't fully understand the impact of this flag. If someone can shed some light, it would be appreciated. I know that it is used to process large amounts of data, but why would a simple GET request need a False Final?

    J.A. Coutts

    Thursday, January 30, 2014 5:28 PM

Answers

  • After a lot of trial and error, I discovered that the programs wanted the Final flag to be True for some encryption/decryption,  but once the connection was made, it wanted the flag to be False as demonstrated by this test output.

    ------------------------------------

    ENCRYPT with FALSE Final

    9C 9D 58 49 55 28 11 93 18 56 C0

    DECRYPT with FALSE Final

    test string

    ENCRYPT with FALSE Final

    15 9A F5 0C 71 AC 72 72 3C 24 CB

    DECRYPT with FALSE Final

    test string

    ENCRYPT with FALSE Final

    86 38 92 F7 70 6E 67 FB 57 B1 1E

    DECRYPT with FALSE Final

    test string

    DECRYPT with TRUE Final

    ü\õ#t"Ê¿

    ------------------------------------

    Does anyone know where this is covered in the specs?
    • Marked as answer by allecnarf Thursday, February 13, 2014 3:53 PM
    Thursday, February 6, 2014 5:13 PM

All replies

  • Hi! I think the answer is very simpe! When you use FINAL flag as FALSE, cryptoprovider is waiting for another data to encrypt till you provide it with TRUE FINAL flag. And only after this it will perform next steps:

    - If the key is a block cipher key, the data is padded to a multiple of the block size of the cipher. If the data length equals the block size of the cipher, one additional block of padding is appended to the data. To find the block size of a cipher, use CryptGetKeyParam to get the KP_BLOCKLEN value of the key.
    - If the cipher is operating in a chaining mode, the next CryptEncrypt operation resets the cipher's feedback register to the KP_IV value of the key.
    - If the cipher is a stream cipher, the next CryptEncrypt resets the cipher to its initial state.

    Look Remarks here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa379924(v=vs.85).aspx

    Friday, January 31, 2014 6:19 AM
  • It is even simpler than that. Because I am using a stream cipher (RC4), the only item that is pertinent is the last one. But that still doesn't anwer the question of why the GET request only works with the flag set to False. The GET request is a simple single record (286 bytes) as evidenced by the blank line at the end. It should be regarded as the ONLY or LAST call to the CryptDecrypt function. There is nothing following it. I am obviously missing something here.

    J.A. Coutts

    Friday, January 31, 2014 5:19 PM
  • After a lot of trial and error, I discovered that the programs wanted the Final flag to be True for some encryption/decryption,  but once the connection was made, it wanted the flag to be False as demonstrated by this test output.

    ------------------------------------

    ENCRYPT with FALSE Final

    9C 9D 58 49 55 28 11 93 18 56 C0

    DECRYPT with FALSE Final

    test string

    ENCRYPT with FALSE Final

    15 9A F5 0C 71 AC 72 72 3C 24 CB

    DECRYPT with FALSE Final

    test string

    ENCRYPT with FALSE Final

    86 38 92 F7 70 6E 67 FB 57 B1 1E

    DECRYPT with FALSE Final

    test string

    DECRYPT with TRUE Final

    ü\õ#t"Ê¿

    ------------------------------------

    Does anyone know where this is covered in the specs?
    • Marked as answer by allecnarf Thursday, February 13, 2014 3:53 PM
    Thursday, February 6, 2014 5:13 PM
  • Can you post the source code of your test program?
    Thursday, February 6, 2014 6:48 PM
  • This is VB code, but you get the idea. CSP is RSA/SChannel. tDecrypt is the same as fDecrypt, but with a True Final flag.

    Private Sub cmdTest3_Click()<BR>
        Dim sResult As String<BR>
        Dim sEncrypted As String<BR>
        Dim strTest As String<BR>
        strTest = "test string" <BR>
        Call SecureSession.CreateKeys("password")<BR>
        Debug.Print hReadKey, hWriteKey<BR>
        sEncrypted = SecureSession.fEncrypt(hWriteKey, strTest)<BR>
        txtMessage.Text = "ENCRYPT with FALSE Final"<BR>
        Call DisplayHex("False Encrypt", sEncrypted, False)<BR>
        sResult = SecureSession.fDecrypt(hReadKey, sEncrypted)<BR>
        txtMessage.Text = txtMessage.Text & vbCrLf & "DECRYPT with FALSE Final" & vbCrLf & sResult<BR>
        sEncrypted = SecureSession.fEncrypt(hWriteKey, strTest)<BR>
        txtMessage.Text = txtMessage.Text & vbCrLf & "ENCRYPT with FALSE Final"<BR>
        Call DisplayHex("False Encrypt", sEncrypted, False)<BR>
        sResult = SecureSession.fDecrypt(hReadKey, sEncrypted)<BR>
        txtMessage.Text = txtMessage.Text & vbCrLf & "DECRYPT with FALSE Final" & vbCrLf & sResult<BR>
        sEncrypted = SecureSession.fEncrypt(hWriteKey, strTest)<BR>
        txtMessage.Text = txtMessage.Text & vbCrLf & "ENCRYPT with FALSE Final"<BR>
        Call DisplayHex("False Encrypt", sEncrypted, False)<BR>
        sResult = SecureSession.fDecrypt(hReadKey, sEncrypted)<BR>
        txtMessage.Text = txtMessage.Text & vbCrLf & "DECRYPT with FALSE Final" & vbCrLf & sResult & vbCrLf<BR>
        sResult = SecureSession.tDecrypt(hReadKey, sEncrypted)<BR>
        txtMessage.Text = txtMessage.Text & "DECRYPT with TRUE Final" & vbCrLf & sResult & vbCrLf<BR>
    End Sub<BR>

    Public Sub CreateKeys(sPassword As String)<BR>
        Const Routine As String = "CreateKeys"<BR>
        Dim hHash As Long<BR>
        'Create a hash object.<BR>
        If CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, hHash) = 0 Then<BR>
            RaiseEvent Error(Err.LastDllError, CCH, Routine)<BR>
            GoTo ReleaseHandles<BR>
        End If<BR>
        'Hash in the password data.<BR>
        If CryptHashData(hHash, sPassword, Len(sPassword), 0) = 0 Then<BR>
            RaiseEvent Error(Err.LastDllError, CHD, Routine)<BR>
            GoTo ReleaseHandles<BR>
        End If<BR>
        'Derive a session key from the hash object.<BR>
        If CryptDeriveKey(hCryptProv, ENCRYPT_ALGORITHM, hHash, 0, hWriteKey) = 0 Then<BR>
            RaiseEvent Error(Err.LastDllError, CDrK, Routine)<BR>
            GoTo ReleaseHandles<BR>
        End If<BR>
        If CryptDeriveKey(hCryptProv, ENCRYPT_ALGORITHM, hHash, 0, hReadKey) = 0 Then<BR>
            RaiseEvent Error(Err.LastDllError, CDrK, Routine)<BR>
            GoTo ReleaseHandles<BR>
        End If<BR>
    ReleaseHandles:<BR>
        'Destroy the hash object.<BR>
        CryptDestroyHash hHash<BR>
    End Sub<BR>

    Public Function fEncrypt(hKey As Long, sMsg As String) As String<BR>
        Const Routine As String = "Encrypt"<BR>
        Dim sBuffer As String<BR>
        sBuffer = sMsg<BR>
        Debug.Print sBuffer<BR>
        If CryptEncrypt(hKey, 0, False, 0, sBuffer, Len(sBuffer), Len(sBuffer)) = 0 Then<BR>
           RaiseEvent Error(Err.LastDllError, CE, Routine)<BR>
           GoTo ReleaseHandles<BR>
        End If<BR>
        fEncrypt = sBuffer<BR>
    ReleaseHandles:<BR>
    End Function<BR>

    Public Function fDecrypt(hKey As Long, sMsg As String) As String<BR>
        Const Routine As String = "Decrypt"<BR>
        Dim sBuffer As String<BR>
        Dim lLen As Long<BR>
        sBuffer = sMsg<BR>
        lLen = Len(sBuffer)<BR>
        'Decrypt data<BR>
        If CryptDecrypt(hKey, 0, False, 0, sBuffer, lLen) = 0 Then<BR>
           RaiseEvent Error(Err.LastDllError, CAQ, Routine)<BR>
           GoTo ReleaseHandles<BR>
        End If<BR>
        Debug.Print lLen<BR>
        fDecrypt = sBuffer<BR>
    ReleaseHandles:<BR>
    End Function<BR>

    Friday, February 7, 2014 4:11 PM
  • This section of your test program decrypts the same piece of ciphertext twice with the same key:

        sResult = SecureSession.fDecrypt(hReadKey, sEncrypted)
        txtMessage.Text = txtMessage.Text & vbCrLf & "DECRYPT with FALSE Final" & vbCrLf & sResult & vbCrLf
        sResult = SecureSession.tDecrypt(hReadKey, sEncrypted)
        txtMessage.Text = txtMessage.Text & "DECRYPT with TRUE Final" & vbCrLf & sResult & vbCrLf

    Each CryptDecrypt call changes the state of the key. At the beginning of the second call, the decryption state thus does not match what CryptEncrypt used, and you get garbage out. If you used CryptDuplicateKey before this section and changed the second call to use the duplicate decryption key, I believe you would get the correct plaintext from both calls.  After these calls, the two decryption keys would of course have different states, because Final=TRUE would have restored the initial state in one of them.

    Wednesday, February 12, 2014 7:01 PM
  • I knew there was something that I did not understand. With a little digging I found out what stateful encryption is, but where in the standards does it call for stateful encryption? Is it in the RSA standard, or the TLS standard? And what is the state? Is it a simple counter?

    J.A. Coutts

    Wednesday, February 12, 2014 11:29 PM
  • You wrote you're using RC4.  The Wikipedia article on RC4 says its "secret internal state" consists of two 8-bit counters and a permutation of 256 bytes.  So it's 258 bytes in total, not a simple counter.

    Thursday, February 13, 2014 12:55 PM