Skip to content
Permalink
Browse files

Add IETF XChaCha20Poly1305 (GH #727, PR #795)

  • Loading branch information...
noloader committed Feb 6, 2019
1 parent 0661536 commit 76b47204dfc40b0e285ce64a978daccaf3db3b2f
Showing with 279 additions and 1 deletion.
  1. +27 −0 TestVectors/chacha20poly1305.txt
  2. +1 −1 chacha.cpp
  3. +101 −0 chachapoly.cpp
  4. +149 −0 chachapoly.h
  5. +1 −0 regtest3.cpp
@@ -56,3 +56,30 @@ Ciphertext: 64 a0 86 15 75 86 1a f4 60 f0 62 c7 9b e6 43 bd \
a6 ad 5c b4 02 2b 02 70 9b
MAC: ee ad 9d 67 89 0c bb 22 39 23 36 fe a1 85 1f 38
Test: Encrypt

AlgorithmType: AuthenticatedSymmetricCipher
Name: XChaCha20/Poly1305
Source: draft-arciszewski-xchacha (https://tools.ietf.org/html/draft-arciszewski-xchacha), Appendix A.1 example
Key: 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f \
90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f
IV: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f \
50 51 52 53 54 55 56 57
Header: 50 51 52 53 c0 c1 c2 c3 c4 c5 c6 c7
Plaintext: 4c 61 64 69 65 73 20 61 6e 64 20 47 65 6e 74 6c \
65 6d 65 6e 20 6f 66 20 74 68 65 20 63 6c 61 73 \
73 20 6f 66 20 27 39 39 3a 20 49 66 20 49 20 63 \
6f 75 6c 64 20 6f 66 66 65 72 20 79 6f 75 20 6f \
6e 6c 79 20 6f 6e 65 20 74 69 70 20 66 6f 72 20 \
74 68 65 20 66 75 74 75 72 65 2c 20 73 75 6e 73 \
63 72 65 65 6e 20 77 6f 75 6c 64 20 62 65 20 69 \
74 2e
Ciphertext: bd 6d 17 9d 3e 83 d4 3b 95 76 57 94 93 c0 e9 39 \
57 2a 17 00 25 2b fa cc be d2 90 2c 21 39 6c bb \
73 1c 7f 1b 0b 4a a6 44 0b f3 a8 2f 4e da 7e 39 \
ae 64 c6 70 8c 54 c2 16 cb 96 b7 2e 12 13 b4 52 \
2f 8c 9b a4 0d b5 d9 45 b1 1b 69 b9 82 c1 bb 9e \
3f 3f ac 2b c3 69 48 8f 76 b2 38 35 65 d3 ff f9 \
21 f9 66 4c 97 63 7d a9 76 88 12 f6 15 c6 8b 13 \
b5 2e
MAC: c0 87 59 24 c1 c7 98 79 47 de af d8 78 0a cf 49
Test: Encrypt
@@ -558,7 +558,7 @@ void XChaCha20_Policy::SeekToIteration(lword iterationCount)
{
// Should we throw here??? XChaCha does not have a block
// counter, so I'm not sure how to seek on it.
CRYPTOPP_ASSERT(0);
CRYPTOPP_ASSERT(0); CRYPTOPP_UNUSED(iterationCount);
}

unsigned int XChaCha20_Policy::GetAlignment() const
@@ -8,6 +8,8 @@

NAMESPACE_BEGIN(CryptoPP)

////////////////////////////// IETF ChaChaTLS //////////////////////////////

// RekeyCipherAndMac is heavier-weight than we like. The Authenc framework was
// predicated on BlockCiphers, where the key and key schedule could be
// calculated independent of the IV being used. However, the ChaCha and
@@ -46,6 +48,7 @@ void ChaCha20Poly1305_Base::SetKeyWithoutResync(const byte *userKey, size_t user
// an IV, which results in an exception. In this case the user will need
// to call Resynchronize to key ChaCha and Poly1305.
// RekeyCipherAndMac(userKey, userKeyLength, params);
CRYPTOPP_UNUSED(params);
}

void ChaCha20Poly1305_Base::Resync(const byte *iv, size_t len)
@@ -103,4 +106,102 @@ bool ChaCha20Poly1305_Base::DecryptAndVerify(byte *message, const byte *mac, siz
return TruncatedVerify(mac, macLength);
}

////////////////////////////// IETF XChaCha20 draft //////////////////////////////

// RekeyCipherAndMac is heavier-weight than we like. The Authenc framework was
// predicated on BlockCiphers, where the key and key schedule could be
// calculated independent of the IV being used. However, the ChaCha and
// ChaCha20Poly1305 construction combines key setup and IV. That is, both are
// needed to key or rekey the cipher. Even a simple Resync() requires us to
// regenerate the initial state for both ChaCha20 and Poly1305.
void XChaCha20Poly1305_Base::RekeyCipherAndMac(const byte *userKey, size_t keylength, const NameValuePairs &params)
{
// Derive MAC key
AlgorithmParameters block0 = MakeParameters("InitialBlock", (word64)0, true);
AccessSymmetricCipher().SetKey(userKey, keylength, CombinedNameValuePairs(params, block0));

// Only the first 256-bits are used to key the MAC
SecByteBlock derived(NULLPTR, 32);
AccessSymmetricCipher().ProcessString(derived, derived.size());

// Key the Poly1305 MAC
AccessMAC().SetKey(derived, derived.size(), params);

// Key the ChaCha20 cipher
AlgorithmParameters block1 = MakeParameters("InitialBlock", (word64)1, true);
AccessSymmetricCipher().SetKey(userKey, keylength, CombinedNameValuePairs(params, block1));
}

void XChaCha20Poly1305_Base::SetKeyWithoutResync(const byte *userKey, size_t userKeyLength, const NameValuePairs &params)
{
CRYPTOPP_ASSERT(userKey && userKeyLength == 32);
m_userKey.Assign(userKey, userKeyLength);

// XChaCha20/Poly1305 initial state depends on both the key and IV. The
// IV may or may not be present during the call to SetKeyWithoutResync.
// If the IV is present, the framework will call SetKeyWithoutResync
// followed by Resynchronize which calls Resync. In this case we defer
// calculating the initial state until the call to Resynchronize.
// If the IV is not present, it avoids calling ChaCha's SetKey without
// an IV, which results in an exception. In this case the user will need
// to call Resynchronize to key ChaCha and Poly1305.
// RekeyCipherAndMac(userKey, userKeyLength, params);
CRYPTOPP_UNUSED(params);
}

void XChaCha20Poly1305_Base::Resync(const byte *iv, size_t len)
{
CRYPTOPP_ASSERT(iv && len == 24);
RekeyCipherAndMac(m_userKey, m_userKey.SizeInBytes(),
MakeParameters(Name::IV(), ConstByteArrayParameter(iv,len)));
}

size_t XChaCha20Poly1305_Base::AuthenticateBlocks(const byte *data, size_t len)
{
AccessMAC().Update(data, len);
return 0;
}

void XChaCha20Poly1305_Base::AuthenticateLastHeaderBlock()
{
// Pad to a multiple of 16 or 0
const byte zero[16] = {0};
size_t pad = (16 - (m_totalHeaderLength % 16)) % 16;
AccessMAC().Update(zero, pad);
}

void XChaCha20Poly1305_Base::AuthenticateLastConfidentialBlock()
{
// Pad to a multiple of 16 or 0
const byte zero[16] = {0};
size_t pad = (16 - (m_totalMessageLength % 16)) % 16;
AccessMAC().Update(zero, pad);
}

void XChaCha20Poly1305_Base::AuthenticateLastFooterBlock(byte *mac, size_t macSize)
{
CRYPTOPP_ALIGN_DATA(8) byte length[2*sizeof(word64)];
PutWord(true, LITTLE_ENDIAN_ORDER, length+0, m_totalHeaderLength);
PutWord(true, LITTLE_ENDIAN_ORDER, length+8, m_totalMessageLength);
AccessMAC().Update(length, sizeof(length));
AccessMAC().TruncatedFinal(mac, macSize);
m_state = State_KeySet;
}

void XChaCha20Poly1305_Base::EncryptAndAuthenticate(byte *ciphertext, byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *message, size_t messageLength)
{
Resynchronize(iv, ivLength);
Update(aad, aadLength);
ProcessString(ciphertext, message, messageLength);
TruncatedFinal(mac, macSize);
}

bool XChaCha20Poly1305_Base::DecryptAndVerify(byte *message, const byte *mac, size_t macLength, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *ciphertext, size_t ciphertextLength)
{
Resynchronize(iv, ivLength);
Update(aad, aadLength);
ProcessString(message, ciphertext, ciphertextLength);
return TruncatedVerify(mac, macLength);
}

NAMESPACE_END
@@ -21,6 +21,8 @@

NAMESPACE_BEGIN(CryptoPP)

////////////////////////////// IETF ChaChaTLS //////////////////////////////

/// \brief ChaCha20Poly1305 cipher base implementation
/// \details Base implementation of the AuthenticatedSymmetricCipher interface
/// \since Crypto++ 8.1
@@ -166,6 +168,153 @@ struct ChaCha20Poly1305 : public AuthenticatedSymmetricCipherDocumentation
typedef ChaCha20Poly1305_Final<false> Decryption;
};

////////////////////////////// IETF XChaCha20 draft //////////////////////////////

/// \brief XChaCha20Poly1305 cipher base implementation
/// \details Base implementation of the AuthenticatedSymmetricCipher interface
/// \since Crypto++ 8.1
class XChaCha20Poly1305_Base : public AuthenticatedSymmetricCipherBase
{
public:
virtual ~XChaCha20Poly1305_Base() {}

virtual const MessageAuthenticationCode & GetMAC() const = 0;
virtual MessageAuthenticationCode & AccessMAC() = 0;

public:
// AuthenticatedSymmetricCipher
std::string AlgorithmName() const
{return std::string("XChaCha20/Poly1305");}
std::string AlgorithmProvider() const
{return GetSymmetricCipher().AlgorithmProvider();}
size_t MinKeyLength() const
{return 32;}
size_t MaxKeyLength() const
{return 32;}
size_t DefaultKeyLength() const
{return 32;}
size_t GetValidKeyLength(size_t n) const
{CRYPTOPP_UNUSED(n); return 32;}
bool IsValidKeyLength(size_t n) const
{return n==32;}
unsigned int OptimalDataAlignment() const
{return GetSymmetricCipher().OptimalDataAlignment();}
IV_Requirement IVRequirement() const
{return UNIQUE_IV;}
unsigned int IVSize() const
{return 24;}
unsigned int MinIVLength() const
{return 24;}
unsigned int MaxIVLength() const
{return 24;}
unsigned int DigestSize() const
{return 16;}
lword MaxHeaderLength() const
{return LWORD_MAX;} // 2^64-1 bytes
lword MaxMessageLength() const
{return W64LIT(274877906880);} // 2^38-1 blocks
lword MaxFooterLength() const
{return 0;}

/// \brief Encrypts and calculates a MAC in one call
/// \param ciphertext the encryption buffer
/// \param mac the mac buffer
/// \param macSize the size of the MAC buffer, in bytes
/// \param iv the iv buffer
/// \param ivLength the size of the IV buffer, in bytes
/// \param aad the AAD buffer
/// \param aadLength the size of the AAD buffer, in bytes
/// \param message the message buffer
/// \param messageLength the size of the messagetext buffer, in bytes
/// \details EncryptAndAuthenticate() encrypts and generates the MAC in one call. The function
/// truncates the MAC if <tt>macSize < TagSize()</tt>.
virtual void EncryptAndAuthenticate(byte *ciphertext, byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *message, size_t messageLength);

/// \brief Decrypts and verifies a MAC in one call
/// \param message the decryption buffer
/// \param mac the mac buffer
/// \param macSize the size of the MAC buffer, in bytes
/// \param iv the iv buffer
/// \param ivLength the size of the IV buffer, in bytes
/// \param aad the AAD buffer
/// \param aadLength the size of the AAD buffer, in bytes
/// \param ciphertext the cipher buffer
/// \param ciphertextLength the size of the ciphertext buffer, in bytes
/// \return true if the MAC is valid and the decoding succeeded, false otherwise
/// \details DecryptAndVerify() decrypts and verifies the MAC in one call.
/// <tt>message</tt> is a decryption buffer and should be at least as large as the ciphertext buffer.
/// \details The function returns true iff MAC is valid. DecryptAndVerify() assumes the MAC
/// is truncated if <tt>macLength < TagSize()</tt>.
virtual bool DecryptAndVerify(byte *message, const byte *mac, size_t macSize, const byte *iv, int ivLength, const byte *aad, size_t aadLength, const byte *ciphertext, size_t ciphertextLength);

protected:
// AuthenticatedSymmetricCipherBase
bool AuthenticationIsOnPlaintext() const {return false;}
unsigned int AuthenticationBlockSize() const {return 1;}
void SetKeyWithoutResync(const byte *userKey, size_t keylength, const NameValuePairs &params);
void Resync(const byte *iv, size_t len);
size_t AuthenticateBlocks(const byte *data, size_t len);
void AuthenticateLastHeaderBlock();
void AuthenticateLastConfidentialBlock();
void AuthenticateLastFooterBlock(byte *mac, size_t macSize);

protected:
// See comments in chachapoly.cpp
void RekeyCipherAndMac(const byte *userKey, size_t userKeyLength, const NameValuePairs &params);

SecByteBlock m_userKey;
};

/// \brief XChaCha20Poly1305 cipher final implementation
/// \tparam T_IsEncryption flag indicating cipher direction
/// \details XChaCha20Poly1305 is an authenticated encryption scheme that combines
/// XChaCha20 and Poly1305-TLS. The scheme is defined in RFC 8439, section 2.8,
/// AEAD_CHACHA20_POLY1305 construction, and uses the IETF versions of ChaCha
/// and Poly1305.
/// \sa <A HREF="http://tools.ietf.org/html/rfc8439">RFC 8439, ChaCha20 and Poly1305
/// for IETF Protocols</A>.
/// \since Crypto++ 8.1
template <bool T_IsEncryption>
class XChaCha20Poly1305_Final : public XChaCha20Poly1305_Base
{
public:
static std::string StaticAlgorithmName()
{return std::string("XChaCha20/Poly1305");}

protected:
const SymmetricCipher & GetSymmetricCipher()
{return const_cast<XChaCha20Poly1305_Final *>(this)->AccessSymmetricCipher();}
SymmetricCipher & AccessSymmetricCipher()
{return m_cipher;}
bool IsForwardTransformation() const
{return T_IsEncryption;}

const MessageAuthenticationCode & GetMAC() const
{return const_cast<XChaCha20Poly1305_Final *>(this)->AccessMAC();}
MessageAuthenticationCode & AccessMAC()
{return m_mac;}

private:
XChaCha20::Encryption m_cipher;
Poly1305TLS m_mac;
};

/// \brief XChaCha20/Poly1305-TLS AEAD scheme
/// \details XChaCha20Poly1305 is an authenticated encryption scheme that combines
/// XChaCha20 and Poly1305-TLS. The scheme is defined in RFC 8439, section 2.8,
/// AEAD_XCHACHA20_POLY1305 construction, and uses the IETF versions of ChaCha
/// and Poly1305.
/// \sa <A HREF="http://tools.ietf.org/html/rfc8439">RFC 8439, ChaCha20 and Poly1305
/// for IETF Protocols</A>.
/// \since Crypto++ 8.1
struct XChaCha20Poly1305 : public AuthenticatedSymmetricCipherDocumentation
{
/// \brief XChaCha20Poly1305 encryption
typedef XChaCha20Poly1305_Final<true> Encryption;
/// \brief XChaCha20Poly1305 decryption
typedef XChaCha20Poly1305_Final<false> Decryption;
};

NAMESPACE_END

#endif // CRYPTOPP_CHACHA_POLY1305_H
@@ -72,6 +72,7 @@ void RegisterFactories4()
RegisterAuthenticatedSymmetricCipherDefaultFactories<GCM<AES> >();
RegisterAuthenticatedSymmetricCipherDefaultFactories<EAX<AES> >();
RegisterAuthenticatedSymmetricCipherDefaultFactories<ChaCha20Poly1305>();
RegisterAuthenticatedSymmetricCipherDefaultFactories<XChaCha20Poly1305>();

RegisterSymmetricCipherDefaultFactories<CBC_Mode<ARIA> >(); // For test vectors
RegisterSymmetricCipherDefaultFactories<CTR_Mode<ARIA> >();

0 comments on commit 76b4720

Please sign in to comment.
You can’t perform that action at this time.