コード例 #1
0
            /**
             * This is needlessly complicated due to the way BCrypt handles CBC mode. It assumes that you will only make one call to BCryptEncrypt and as a result
             * appends the padding to the output of every call. The simplest way around this is to have an extra 32 byte block sitting around. During EncryptBuffer calls
             * we don't use padding at all, we enforce that we only pass multiples of 32 bytes to BCryptEncrypt. Anything extra goes into either the next EncryptBuffer call
             * or is handled in the Finalize call.  On the very last call, we add the padding back. This is what the other Crypto APIs such as OpenSSL and CommonCrypto do under the hood anyways.
             */
            CryptoBuffer AES_CBC_Cipher_BCrypt::FillInOverflow(const CryptoBuffer& buffer)
            {
                static const size_t RESERVE_SIZE = BlockSizeBytes * 2;
                m_flags = 0;

                CryptoBuffer finalBuffer;

                if (m_blockOverflow.GetLength() > 0)
                {
                    finalBuffer = CryptoBuffer({ (ByteBuffer*)&m_blockOverflow, (ByteBuffer*)&buffer });
                    m_blockOverflow = CryptoBuffer();
                }
                else
                {
                    finalBuffer = buffer;
                }

                auto overflow = finalBuffer.GetLength() % RESERVE_SIZE;

                if (finalBuffer.GetLength() > RESERVE_SIZE)
                {
                    auto offset = overflow == 0 ? RESERVE_SIZE : overflow;
                    m_blockOverflow = CryptoBuffer(finalBuffer.GetUnderlyingData() + finalBuffer.GetLength() - offset, offset);
                    finalBuffer = CryptoBuffer(finalBuffer.GetUnderlyingData(), finalBuffer.GetLength() - offset);
                    return finalBuffer;
                }
                else
                {
                    m_blockOverflow = finalBuffer;
                    return CryptoBuffer();
                }
            }
コード例 #2
0
            /**
             * Since we have to assume these calls are being chained, and due to the way the windows
             * api works, we have to make sure we hold a final buffer until the end so we can tell
             * windows to compute the auth tag. Also, prior to the last call, we have to pass the data
             * in multiples of 16 byte blocks. So, here we keep a buffer of the % 16 + 16 bytes.
             * That gets saved until the end where we will decrypt the last buffer and compute the tag.
             */
            CryptoBuffer AES_GCM_Cipher_BCrypt::DecryptBuffer(const CryptoBuffer& toDecrypt)
            {
                CryptoBuffer workingBuffer;

                if (m_finalBuffer.GetLength() > 0)
                {
                    workingBuffer = CryptoBuffer({ (ByteBuffer*)&m_finalBuffer, (ByteBuffer*)&toDecrypt });
                    m_finalBuffer = CryptoBuffer();
                }
                else
                {
                    workingBuffer = toDecrypt;
                }

                if (workingBuffer.GetLength() > TagLengthBytes)
                {
                    auto offset = workingBuffer.GetLength() % TagLengthBytes;
                    m_finalBuffer = CryptoBuffer(workingBuffer.GetUnderlyingData() + workingBuffer.GetLength() - (TagLengthBytes + offset), TagLengthBytes + offset);
                    workingBuffer = CryptoBuffer(workingBuffer.GetUnderlyingData(), workingBuffer.GetLength() - (TagLengthBytes + offset));
                    return BCryptSymmetricCipher::DecryptBuffer(workingBuffer);
                }
                else
                {
                    m_finalBuffer = workingBuffer;
                    return CryptoBuffer();
                }
            }
コード例 #3
0
            CryptoBuffer BCryptSymmetricCipher::EncryptBuffer(const CryptoBuffer& unEncryptedData)
            {
                CheckInitEncryptor();

                if (m_failure)
                {
                    AWS_LOGSTREAM_FATAL(SYM_CIPHER_TAG, "Cipher not properly initialized for encryption. Aborting");
                    return CryptoBuffer();
                }

                if (unEncryptedData.GetLength() == 0)
                {
                    return CryptoBuffer();
                }

                size_t predictedWriteLengths = m_flags & BCRYPT_BLOCK_PADDING ? unEncryptedData.GetLength() + (GetBlockSizeBytes() - unEncryptedData.GetLength() % GetBlockSizeBytes())
                                                             : unEncryptedData.GetLength();

                ULONG lengthWritten = static_cast<ULONG>(predictedWriteLengths);
                CryptoBuffer encryptedText(static_cast<size_t>(predictedWriteLengths));

                PUCHAR iv = nullptr;
                ULONG ivSize = 0;

                if (m_authInfoPtr)
                {
                    iv = m_workingIv.GetUnderlyingData();
                    ivSize = static_cast<ULONG>(m_workingIv.GetLength());
                }

                //iv was set on the key itself, so we don't need to pass it here.
                NTSTATUS status = BCryptEncrypt(m_keyHandle, unEncryptedData.GetUnderlyingData(), (ULONG)unEncryptedData.GetLength(),
                    m_authInfoPtr, iv, ivSize, encryptedText.GetUnderlyingData(), (ULONG)encryptedText.GetLength(), &lengthWritten, m_flags);

                if (!NT_SUCCESS(status))
                {
                    m_failure = true;
                    AWS_LOGSTREAM_ERROR(SYM_CIPHER_TAG, "Failed to compute encrypted output with error code " << status);
                    return CryptoBuffer();
                }

                if (static_cast<size_t>(lengthWritten) < encryptedText.GetLength())
                {
                    return CryptoBuffer(encryptedText.GetUnderlyingData(), static_cast<size_t>(lengthWritten));
                }

                return encryptedText;
            }
コード例 #4
0
            CryptoBuffer CommonCryptoCipher::DecryptBuffer(const CryptoBuffer& encryptedData)
            {
                if (m_failure)
                {
                    AWS_LOGSTREAM_FATAL(CC_LOG_TAG, "Cipher not properly initialized for decryption. Aborting");
                    return CryptoBuffer();
                }

                CheckInitDecryptor();
                size_t lengthWritten = encryptedData.GetLength() + (GetBlockSizeBytes() - 1);
                CryptoBuffer decryptedText(static_cast<size_t>(lengthWritten));

                CCStatus status = CCCryptorUpdate(m_cryptoHandle, encryptedData.GetUnderlyingData(), encryptedData.GetLength(),
                    decryptedText.GetUnderlyingData(), decryptedText.GetLength(), &lengthWritten);

                if (status != kCCSuccess)
                {
                    m_failure = true;
                    AWS_LOGSTREAM_ERROR(CC_LOG_TAG, "Decryption of buffer failed with status code: " << status);
                    return CryptoBuffer();
                }

                if (lengthWritten < decryptedText.GetLength())
                {
                    return CryptoBuffer(decryptedText.GetUnderlyingData(), static_cast<size_t>(lengthWritten));
                }

                return decryptedText;
            }
コード例 #5
0
            void AES_CTR_Cipher_BCrypt::IncrementCounter(CryptoBuffer& buffer)
            {
                assert(buffer.GetLength() == BlockSizeBytes);

                int32_t ctr = 0;
                for (size_t i = BlockSizeBytes - 5; i < BlockSizeBytes; ++i)
                {
                    ctr <<= 8;
                    ctr |= buffer[i];
                }

                ctr += 1;

                for (size_t i = BlockSizeBytes - 1; i > BlockSizeBytes - 5; --i)
                {
                    buffer[i] = ctr & 0x000000FF;
                    ctr >>= 8;
                }
            }
コード例 #6
0
            /**
            * Windows doesn't expose CTR mode. We can however, build it manually from ECB. Here, split each
            * buffer into 16 byte chunks, for each complete buffer encrypt the counter and xor it against the unencrypted
            * text. Save anything left over for the next run.
            */
            CryptoBuffer AES_CTR_Cipher_BCrypt::EncryptWithCtr(const CryptoBuffer& buffer)
            {
                size_t bytesWritten = 0;
                Aws::Vector<ByteBuffer*> finalBufferSet(0);

                CryptoBuffer bufferToEncrypt;

                if (m_blockOverflow.GetLength() > 0 && &m_blockOverflow != &buffer)
                {
                    bufferToEncrypt = CryptoBuffer({ (ByteBuffer*)&m_blockOverflow, (ByteBuffer*)&buffer });
                    m_blockOverflow = CryptoBuffer();
                }
                else
                {
                    bufferToEncrypt = buffer;
                }

                Aws::Utils::Array<CryptoBuffer> slicedBuffers;

                if (bufferToEncrypt.GetLength() > BlockSizeBytes)
                {
                    slicedBuffers = bufferToEncrypt.Slice(BlockSizeBytes);
                }
                else
                {
                    slicedBuffers = Aws::Utils::Array<CryptoBuffer>(1u);
                    slicedBuffers[0] = bufferToEncrypt;
                }

                finalBufferSet = Aws::Vector<ByteBuffer*>(slicedBuffers.GetLength());
                InitBuffersToNull(finalBufferSet);

                for (size_t i = 0; i < slicedBuffers.GetLength(); ++i)
                {
                    if (slicedBuffers[i].GetLength() == BlockSizeBytes || (m_blockOverflow.GetLength() > 0 && slicedBuffers.GetLength() == 1))
                    {
                        ULONG lengthWritten = static_cast<ULONG>(BlockSizeBytes);
                        CryptoBuffer encryptedText(BlockSizeBytes);

                        NTSTATUS status = BCryptEncrypt(m_keyHandle, m_workingIv.GetUnderlyingData(), (ULONG)m_workingIv.GetLength(),
                            nullptr, nullptr, 0, encryptedText.GetUnderlyingData(), (ULONG)encryptedText.GetLength(), &lengthWritten, m_flags);

                        if (!NT_SUCCESS(status))
                        {
                            m_failure = true;
                            AWS_LOGSTREAM_ERROR(CTR_LOG_TAG, "Failed to compute encrypted output with error code " << status);
                            CleanupBuffers(finalBufferSet);
                            return CryptoBuffer();
                        }

                        CryptoBuffer* newBuffer = Aws::New<CryptoBuffer>(CTR_LOG_TAG, BlockSizeBytes);
                        *newBuffer = slicedBuffers[i] ^ encryptedText;
                        finalBufferSet[i] = newBuffer;
                        IncrementCounter(m_workingIv);
                        bytesWritten += static_cast<size_t>(lengthWritten);
                    }
                    else
                    {
                        m_blockOverflow = slicedBuffers[i];
                        CryptoBuffer* newBuffer = Aws::New<CryptoBuffer>(CTR_LOG_TAG, 0);
                        finalBufferSet[i] = newBuffer;
                    }
                }

                CryptoBuffer returnBuffer(std::move(finalBufferSet));
                CleanupBuffers(finalBufferSet);

                return returnBuffer;
            }