Announcement

Collapse
No announcement yet.

Using IV for Key in AES

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Using IV for Key in AES

    At first this may seem a strange question. If a "secret" IV and a NULL or "not secret" key is used for AES encryption/decryption, will the encrypted output still be secure?

    I'm playing around with Windows Bcrypt.dll functions for a message server application. The server uses AES to encrypt IP network sessions with thousands of remote clients. Each client session has a separate AES "key" which changes periodically. AES-CBC with 256-bit keys and padding will be used. Messages to be encrypted will always be unique (CS random fields and other changing data in the message) so I assume that all encrypted messages will also be unique.

    What I would like to do is call BCryptGenerateSymetricKey once with a null or application-defined value to create a key object. I would then make multiple calls to BCryptEncrypt, using the session key as the IV input to BCryptEncrypt. The server application could be tracking more than 100K simultaneous sessions (each with a unique key) and encrypting/decrypting up to 50K messages per second. I'm thinking that there should be a performance improvement doing this versus creating and destroying a key object for each message.

    Thoughts and suggestions are always welcome and appreciated.

  • #2
    https://crackstation.net/hashing-security.htm

    You might consider wrapping the packet with a SHA-256 HMAC so you know a man-in-the-middle didn't replace it.
    The clients would also need to know the hmac password,which might not work for you, but you would know the packet hasn't been modified.
    Some systems keep an encrypted counter in the packet so a replay would will not work because its counter would be less than the current counter.

    The IV is supposed to be random and unpredictable or information is leaked in the first block.
    I generate a new hash key with each transmit and it looks something like this: random(IV)+ hmac256-sha(salt+hash key + encrypted data)
    The IV and salt are stripped off. The length of the packet is also in the encrypted data and a counter is optional.
    How you get 100K simultaneous sessions makes my program look like a snail since a thread for each new session maxes out at about 200.
    You must be doing something without threads.
    https://www.tesla.com/roadster

    Comment


    • #3
      Key exchange is handled at the start of each session. It's very similar to the DTLS specification and, currently, supports 2048-bit RSA and SHA-256 HMAC as well as methods to mitigate DoS and replay attacks. Once the authentication and key exchange is finished, it's all AES - again, very similar to DTLS. While the key/IV values are reused for the variable life of the session key (automated key changes), there is CS random and other variable data in the encrypted payload header of each message.

      UDP is used for transport, so many sessions can be supported by a single socket. Session status is handled by a kind of state machine using an in-memory database. There are housekeeping threads but only one message processing thread is required. This is, essentially, a secure message switch in an IoT environment. There could probably be a performance improvement with parallel threads handling the message processing - something that I'll tinker with later.

      Comment


      • #4
        Thanks for the explanation
        Studying DTLS, which I never heard of . Very interesting.
        Just read Steve Gibson on the subject in a previous security now https://www.grc.com/sn/sn-380.pdf
        Since I don't use TLS, I should add a handshake and maybe iterate the hash in my include file..
        Sounds like you aren't using TLS.


        Using IV for Key in AES (not sure?) I hash the key within the encrypted data so it wouldn't work for me.
        If the IV was encrypted, it would require knowing where the data begins since the encrypted first blocks would increase in length.

        I don't use a handshake because clients must know data password and hmac password. I'll work on that.

        https://crypto.stackexchange.com/que...iv-and-a-nonce
        In any case, the IV never needs to be kept secret — if it did, it would be a key, not an IV.
        Indeed, in most cases, keeping the IV secret would not be practical even if you wanted to, since the
        recipient needs to know it in order to decrypt the data (or verify the hash, etc.).


        https://www.tesla.com/roadster

        Comment


        • #5
          DTLS is based on TLS but is designed for UDP (vs TLS/TCP). My project is focused on networks supporting very low-latency transport of short, secure messages that, typically, fit into a single IP packet. Read this as IoT. So, DTLS became the model for my transport protocol - which explicitly includes security rules.

          In any case, the IV never needs to be kept secret — if it did, it would be a key, not an IV.
          Indeed, in most cases, keeping the IV secret would not be practical even if you wanted to, since the
          recipient needs to know it in order to decrypt the data (or verify the hash, etc.).
          In fact, I am looking to use a cryptographically-strong secret key as the IV value when calling BCryptEncrypt/Decrypt. Additionally, the message to be encrypted has at least one field with a CS nonce. Together, I believe that this should provide adequate security of the message data. That is what I'm hoping to verify in this thread.

          Comment


          • #6
            I use these for IV's and hashes which I'm sure you have something similar optimized for multiple calls.

            Code:
            FUNCTION GetRandomString(TheLength AS DWORD) AS STRING
              LOCAL hRand AS DWORD      'from code by David Roberts
              LOCAL sBuf  AS STRING
              BCryptOpenAlgorithmProvider(hRand, $$BCRYPT_RNG_ALGORITHM, $$NUL, 0)
              sBuf = STRING$(TheLength,0)
              BCryptGenRandom(hRand, STRPTR(sBuf), TheLength, 0)
              BCryptCloseAlgorithmProvider(hRand, 0)
              FUNCTION = sBuf
            END FUNCTION
            '-------------------------------------------------------------------------------------------------------------
            FUNCTION SHA256HMAC( BYVAL sText AS STRING, BYVAL pbSecret AS STRING ) AS STRING ' Binary
            ' sText is message to be hashed  code by David Roberts
            ' pbSecret is secret HMAC key
            
              LOCAL hAlg, phHash AS DWORD
              LOCAL sBinHMAC AS STRING * 32 ' 32 for SHA256
            
              BCryptOpenAlgorithmProvider( hAlg, "SHA256"$$, $$NUL, %BCRYPT_ALG_HANDLE_HMAC_FLAG ) ' We need hAlg
              BCryptCreateHash( hAlg, phHash, BYVAL %Null, 0, STRPTR( pbSecret ), LEN( pbSecret ), 0 ) ' We need pbHash
              BCryptHashData( phHash, STRPTR( sText ), LEN( sText ), 0 )
              BCryptFinishHash( phHash, VARPTR( sBinHMAC ), 32, 0 ) '  32 for SHA256, We need sBinHMAC
              BCryptDestroyHash( phHash )
              BCryptCloseAlgorithmProvider( hAlg, 0 )
              FUNCTION = sBinHMAC
            
            END FUNCTION
            In fact, I am looking to use a cryptographically-strong secret key as the IV value when calling BCryptEncrypt/Decrypt. Additionally, the message to be encrypted has at least one field with a CS nonce. Together, I believe that this should provide adequate security of the message data. That is what I'm hoping to verify in this thread.
            https://www.tesla.com/roadster

            Comment


            • #7
              I have similar functions in the CNG library that I'm working on. They haven't been exhaustively tested but should be OK. Since much of the data that I work with is not STRING data, I use pointers to buffers - which also natively fits well with the BCrypt functions. Note that I create algorithm providers and a hash object the first time that a function is called, and then reuse the provider/hash handles for subsequent calls. This seems to provide a substantial improvement in performance without (I hope) a security or stability penalty. Since the host application is supporting message relay rates of at least 50K/second, I look for every ounce of speed to keep latency down.

              Code:
              '==================================================================================================
              ' cngSHA256 - Generate an SHA256 hash value from an input buffer.
              '
              '==================================================================================================
              FUNCTION cngSHA256 ( _
                  pbIn    AS LONG, _  'IN: Pointer to buffer with data to hash
                  inSize  AS LONG, _  'IN: Length of data to hash
                  pbOut   AS LONG _   'IN: Pointer to 32-byte buffer to write hash to
                  ) AS LONG           'RET: Err val or zero for success.
              
                '-- Threaded values are used instead of STATIC values to minimize problems in multi-threaded
                '   apps.
                THREADED thHash256Provider  AS LONG ' Persistent handle for the BCrypt hash provider.
                THREADED thHash256Object    AS LONG ' Persistent handle for hash object
              
                '-- Test threaded BCrypt hash provider handle. If no handle then open an algorithm provider for
                '   random generation and initialize the provider. These two BCrypt functions should never fail.
                IF ISFALSE(thHash256Provider) THEN
                  BCryptOpenAlgorithmProvider(thHash256Provider, $$BCRYPT_SHA256_ALGORITHM, $$NUL, 0)
                  BCryptCreateHash ( _
                      thHash256Provider, _  ' This will always be valid (above)
                      thHash256Object, _    ' This is generated now using default values (below)
                      %NULL, _              ' Let Windows allocate hash object memory (prob 286 bytes long)
                      0, _ '                ' Zero length since Windows handles allocating memory
                      %NULL, _              ' Any secrets must be part of the data to be hashed
                      0, _                  ' No secret so no length
                      0 _                   ' No flags used since they would only apply to WIN10
                      )
                END IF
              
                '-- Hash input data. The only reason that this should fail is if the function args are
                '   bad (null buffer pointer or length). Will NOT catch bad memory locations - GPF!
                IF BCryptHashData ( _
                    thHash256Object, _
                    pbIn, _               ' Pointer to buffer with data to hash
                    inSize, _             ' Length of data in the buffer to hash
                    0 _                   ' No flags
                    ) THEN
                  FUNCTION = %CNG_BADINPUT
                  EXIT FUNCTION
                END IF
              
                '-- Copy hash to output buffer. The only reason that this should fail is if the ptr to the
                '   output buffer is NULL. Note the output buffer MUST BE AT LEAST 32 bytes (256 bits) long.
                '   If the pointer or buffer size are incorrect, you will probably GPF.
                IF BCryptFinishHash ( _
                    thHash256Object, _
                    pbOut, _              ' Pointer to buffer to write to
                    32, _                 ' Only need 256 bits to write to
                    0 _                   ' No flags
                    ) THEN
                  FUNCTION = %CNG_BADINPUT
                END IF
              
              END FUNCTION  ' cngSHA256
              
              
              '==================================================================================================
              ' cngRand - Fill a buffer with cryptographically strong random data.
              '
              '==================================================================================================
              FUNCTION cngRand ( _
                  BYVAL pBuff AS LONG, _  'IN: Ptr to buffer to hold random data
                  BYVAL dLen  AS LONG _   'IN: Length of random data to write to buffer
                  ) AS LONG               'RET: Err val or zero if no errors
              
                THREADED tnRndProvHandle  AS LONG ' Threaded CNG prov handle shared between cngRAND functions.
              
                '-- Test threaded BCrypt RNG provider handle. If no handle then open an algorithm provider for
                '   random generation. There should never be errros here.
                IF ISFALSE(tnRndProvHandle) THEN
                  BCryptOpenAlgorithmProvider(tnRndProvHandle, $$BCRYPT_RNG_ALGORITHM, $$NUL, 0)
                END IF
              
                '-- The only possible error will be invalid buffer pointer or length values so only one
                '   possible error value.
                IF BCryptGenRandom (tnRndProvHandle, pBuff, dLen, %NULL) THEN
                  FUNCTION = %CNG_BADINPUT
                END IF
              
              END FUNCTION  ' cngRand
              
              
              '==================================================================================================
              ' cngRand32 - Returns a cryptogrphically strong 32-bit value.
              '
              '==================================================================================================
              FUNCTION cngRand32 () AS DWORD        'RET: Random 32-bit value
              
                THREADED hRndProvHandle  AS LONG   ' Threaded CNG prov handle shared between cngRAND functions.
                LOCAL dwTemp             AS DWORD  ' 32-bit buffer to hold random var that will be returned
              
                '-- Test threaded BCrypt RNG provider handle. If no handle then open an algorithm provider for
                '   random generation.
                IF ISFALSE(tnRndProvHandle) THEN
                  BCryptOpenAlgorithmProvider(tnRndProvHandle, $$BCRYPT_RNG_ALGORITHM, $$NUL, 0)
                END IF
              
                '-- Generate the random 32-bit value and return it. There should never be BCrypt library errors so
                '   we don't need to bother checking.
                BCryptGenRandom (tnRndProvHandle, VARPTR(dwTemp), 4, 0)
                FUNCTION = dwTemp
              
              END FUNCTION  ' cngRand32

              Comment


              • #8
                ref - that quote that the IV does not need to be kept secret

                TOTAL BS !

                The IV is a secondary key variable. The IV makes the first block different even if the data is the same, which makes the second block different, and so on. An example of the first (few) block(s) being the same is a log-on sequence <-LOGON >-username <-PASSWORD >-password. With a known IV and likely guessed data gives away the generated key of the first block, making calculating following block easier, which can then be used for decryption by the "third man". Even if all the data is different every time, it can not be counted on to prevent it's own decryption.

                Whether you agree with that or not: the encrypter needs to know, the decrypter may need to know (by cryoto mode) but can know, no one else needs to know. Giving away anything extra increases the risk.

                ((in fact for log-on sequences, consider prepending salt to the data. salt could be 4 bytes of random nonce. At decryption it is discarded. It's only purpose was to make the data different. (of course you most write the code at both ends)))

                Cheers,
                Dale

                Comment


                • #9
                  The IV makes the first block different even if the data is the same, which makes the second block different, and so on.
                  This seems correct. But, what if the first block in the clear text is ALWAYS different? I believe that many messaging protocols do this by including CS random nonce fields and other continuously-changing fields (e.g. sequence counters) in the first block of every message - typically a message header followed by the message "payload" data. This would seem to accomplish the same goal as the IV. And, I'm not sure how two network-connected devices would synchronize IV values. They do securely exchange shared secret keys in an initial handshake using asymmetric encryption such as RSA.

                  I'm pretty sure that I would never send an actual password value in any message - RSA or AES. Instead, it makes more sense to use HMAC with a strong hash of known random data and the password. Only a receiver that already knows the password would be able to create the same hash value. This is what Mike indicated in post #2 in this thread. It seems to be a fairly common practice.

                  Comment


                  • #10
                    Even if the first block is always different, the IV (Initialization Vector) "starts" (or Initiates) the encryption "chain"
                    This would seem to accomplish the same goal as the IV
                    But the data (payload) is what is being protected, don't depend on it to protect the key that is protecting it. In this case send the IV in the same async encryption (RSA) message as the AES key.

                    The username and password login was mentioned as a worse case for why you need to keep the IV secret. The hash method works when you code (program) both ends (or at manufacturer has published the interface). But if you're protecting a long distance console connection to a router, you encrypt for long haul, decrypt, then to console port. AGAIN JUST AN EXAMPLE.
                    Dale

                    Comment


                    • #11
                      Prefixed plaintext with IV and it works great to not have to pass the IV to decrypt.
                      Now it appears any value passed for the IV will work.
                      1) What should be passed to bcryptencrypt for the IV? STRING$(16,0) or can that be something different to make decrypting harder?

                      2) Send hmac + encrypted data or is there a way to also do that better?

                      ntStatus = BCryptEnCrypt( hKey, STRPTR(sText), LEN(sText), BYVAL %Null, VARPTR(IV(1)), %BlockSize, _
                      BYVAL %Null, 0, LenOutText, %BCRYPT_BLOCK_PADDING )


                      There is never an error passing anything as an IV when decrypting.
                      It appears this is the same if passing the wrong IV in clear text the way I had it before.

                      ntStatus = BCryptDecrypt( hKey, STRPTR(sText), LEN(sText), BYVAL %Null, VARPTR(IV(1)), _
                      %BlockSize, BYVAL %Null, 0, LenOutText, %BCRYPT_BLOCK_PADDING ) ' We want LenOutText
                      https://www.tesla.com/roadster

                      Comment


                      • #12
                        Yes, the IV can be anything block sized. (all zeros or all ones (the bits) probably a bad idea). Just make it random, use only once, and protect it when passing to other end. Protect either with the key RSA message (Jerry already had that message anyway), or make it block zero (prepended to block 1) like you did.

                        Cheers,
                        Dale

                        Comment

                        Working...
                        X