/* Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of LibCat nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CAT_AUTHENTICATED_ENCRYPTION_HPP #define CAT_AUTHENTICATED_ENCRYPTION_HPP #include #include #include namespace cat { /* Tunnel Authenticated Encryption "Calico" protocol: Run after the Key Agreement protocol completes. Uses a 1024-bit anti-replay sliding window, suitable for Internet file transfer over UDP. Cipher: 12-round ChaCha with 256-bit or 384-bit keys KDF: Key derivation function (Skein) MAC: 64-bit truncated HMAC-MD5 IV: Initialization vector incrementing by 1 each time c2sMKey = KDF(k) { "upstream-MAC" } s2cMKey = KDF(k) { "downstream-MAC" } c2sEKey = KDF(k) { "upstream-ENC" } s2cEKey = KDF(k) { "downstream-ENC" } c2sIV = KDF(k) { "upstream-IV" } s2cIV = KDF(k) { "downstream-IV" } To transmit a message, the client calculates a MAC with the c2sMKey of the IV concatenated with the plaintext message and then appends the 8-byte MAC and low 3 bytes of the IV to the message, which is encrypted using the c2sEKey and the IV. c2s Encrypt(c2sEKey) { message || MAC(c2sMKey) { full-iv-us||message } } || Obfuscated { trunc-iv-us } encrypted { MESSAGE(X) MAC(8by) } IV(3by) = 11 bytes overhead at end of packet To transmit a message, the server calculates a MAC with the s2cMKey of the IV concatenated with the plaintext message and then appends the 8-byte MAC and low 3 bytes of the IV to the message, which is encrypted using the s2cEKey and the IV. s2c Encrypt(s2cEKey) { message || MAC(s2cMKey) { full-iv-ds||message } } || Obfuscated { trunc-iv-ds } encrypted { MESSAGE(X) MAC(8by) } IV(3by) = 11 bytes overhead at end of packet The full 64-bit IVs are initialized to c2sIV and s2cIV, and the first one sent is IV+1. */ class KeyAgreementResponder; class KeyAgreementInitiator; // This class is NOT THREAD-SAFE. class CAT_EXPORT AuthenticatedEncryption { friend class KeyAgreementResponder; friend class KeyAgreementInitiator; bool _is_initiator, _accept_out_of_order; Skein key_hash; HMAC_MD5 local_mac_key, remote_mac_key; ChaChaKey local_cipher_key, remote_cipher_key; u64 local_iv, remote_iv; // 1024-bit anti-replay sliding window static const int BITMAP_BITS = 1024; static const int BITMAP_WORDS = BITMAP_BITS / 64; u64 iv_bitmap[BITMAP_WORDS]; public: CAT_INLINE AuthenticatedEncryption() {} CAT_INLINE ~AuthenticatedEncryption() {} // Tunnel overhead bytes static const int MAC_BYTES = 8; static const int IV_BYTES = 3; static const u32 OVERHEAD_BYTES = IV_BYTES + MAC_BYTES; // IV constants static const int IV_BITS = IV_BYTES * 8; static const u32 IV_MSB = (1 << IV_BITS); static const u32 IV_MASK = (IV_MSB - 1); static const u32 IV_FUZZ = 0xCA7DCA7D; protected: bool SetKey(int KeyBytes, Skein *key, bool is_initiator, const char *key_name); bool IsValidIV(u64 iv); void AcceptIV(u64 iv); public: // Generate a proof that the local host has the key bool GenerateProof(u8 *local_proof, int proof_bytes); // Validate a proof that the remote host has the key bool ValidateProof(const u8 *remote_proof, int proof_bytes); public: void AllowOutOfOrder(bool allowed = true) { _accept_out_of_order = allowed; } public: // Overhead is OVERHEAD_BYTES bytes at the end of the packet // Returns false if the message is invalid. Invalid messages should just be ignored as if they were never received // buf_bytes: Number of bytes in the buffer, including the overhead // If Decrypt() returns true, buf_bytes is set to the size of the decrypted message bool Decrypt(u8 *buffer, u32 &buf_bytes); // Overhead is OVERHEAD_BYTES bytes at the end of the packet // buffer_bytes: Number of bytes in the buffer; will return false if buffer size too small // msg_bytes: Number of bytes in the message, excluding the overhead // If Encrypt() returns true, msg_bytes is set to the size of the encrypted message bool Encrypt(u8 *buffer, u32 buffer_bytes, u32 &msg_bytes); }; } // namespace cat #endif // CAT_AUTHENTICATED_ENCRYPTION_HPP