summaryrefslogtreecommitdiff
path: root/chacha.cpp
diff options
context:
space:
mode:
authorJeffrey Walton <noloader@gmail.com>2019-02-06 01:03:28 -0500
committerGitHub <noreply@github.com>2019-02-06 01:03:28 -0500
commit26c83877ef30c81054746721f23f8df91ef44337 (patch)
tree1b732c240595f494a52e0e7cb03bac5c7f138178 /chacha.cpp
parent48531785b74d2f6728282937a105595467b94cc7 (diff)
downloadcryptopp-git-26c83877ef30c81054746721f23f8df91ef44337.tar.gz
Add IETF XChaCha (GH #727, PR #794)
Diffstat (limited to 'chacha.cpp')
-rw-r--r--chacha.cpp141
1 files changed, 129 insertions, 12 deletions
diff --git a/chacha.cpp b/chacha.cpp
index fcea81ec..437aeff7 100644
--- a/chacha.cpp
+++ b/chacha.cpp
@@ -39,6 +39,7 @@ void ChaCha_TestInstantiations()
{
ChaCha::Encryption x;
ChaChaTLS::Encryption y;
+ XChaCha20::Encryption z;
}
#endif
@@ -218,9 +219,35 @@ void ChaCha_OperateKeystream(KeystreamOperation operation,
// We may re-enter a SIMD keystream operation from here.
} while (iterationCount--);
+}
+
+// XChaCha key derivation
+void HChaCha_OperateKeystream(const word32 state[16], word32 output[8])
+{
+ word32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
+
+ x0 = state[0]; x1 = state[1]; x2 = state[2]; x3 = state[3];
+ x4 = state[4]; x5 = state[5]; x6 = state[6]; x7 = state[7];
+ x8 = state[8]; x9 = state[9]; x10 = state[10]; x11 = state[11];
+ x12 = state[12]; x13 = state[13]; x14 = state[14]; x15 = state[15];
- #undef CHACHA_QUARTER_ROUND
- #undef CHACHA_OUTPUT
+ for (int i = 20; i > 0; i -= 2)
+ {
+ CHACHA_QUARTER_ROUND(x0, x4, x8, x12);
+ CHACHA_QUARTER_ROUND(x1, x5, x9, x13);
+ CHACHA_QUARTER_ROUND(x2, x6, x10, x14);
+ CHACHA_QUARTER_ROUND(x3, x7, x11, x15);
+
+ CHACHA_QUARTER_ROUND(x0, x5, x10, x15);
+ CHACHA_QUARTER_ROUND(x1, x6, x11, x12);
+ CHACHA_QUARTER_ROUND(x2, x7, x8, x13);
+ CHACHA_QUARTER_ROUND(x3, x4, x9, x14);
+ }
+
+ output[0] = x0; output[1] = x1;
+ output[2] = x2; output[3] = x3;
+ output[4] = x12; output[5] = x13;
+ output[6] = x14; output[7] = x15;
}
std::string ChaCha_AlgorithmProvider()
@@ -398,19 +425,14 @@ void ChaChaTLS_Policy::CipherSetKey(const NameValuePairs &params, const byte *ke
// the function, so we have to use the heavier-weight SetKey to change it.
word64 block;
if (params.GetValue("InitialBlock", block))
- m_state[16] = static_cast<word32>(block);
+ m_state[CTR] = static_cast<word32>(block);
else
- m_state[16] = 0;
-
- // State words are defined in RFC 8439, Section 2.3.
- m_state[0] = 0x61707865;
- m_state[1] = 0x3320646e;
- m_state[2] = 0x79622d32;
- m_state[3] = 0x6b206574;
+ m_state[CTR] = 0;
// State words are defined in RFC 8439, Section 2.3. Key is 32-bytes.
GetBlock<word32, LittleEndian> get(key);
- get(m_state[4])(m_state[5])(m_state[6])(m_state[7])(m_state[8])(m_state[9])(m_state[10])(m_state[11]);
+ get(m_state[KEY+0])(m_state[KEY+1])(m_state[KEY+2])(m_state[KEY+3])
+ (m_state[KEY+4])(m_state[KEY+5])(m_state[KEY+6])(m_state[KEY+7]);
}
void ChaChaTLS_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length)
@@ -418,9 +440,16 @@ void ChaChaTLS_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *IV
CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
CRYPTOPP_ASSERT(length==12);
+ // State words are defined in RFC 8439, Section 2.3.
+ m_state[0] = 0x61707865; m_state[1] = 0x3320646e;
+ m_state[2] = 0x79622d32; m_state[3] = 0x6b206574;
+
+ // Copy saved key into state
+ std::memcpy(m_state+4, m_state+KEY, 8*sizeof(word32));
+
// State words are defined in RFC 8439, Section 2.3
GetBlock<word32, LittleEndian> get(IV);
- m_state[12] = m_state[16];
+ m_state[12] = m_state[CTR];
get(m_state[13])(m_state[14])(m_state[15]);
}
@@ -461,4 +490,92 @@ void ChaChaTLS_Policy::OperateKeystream(KeystreamOperation operation,
CRYPTOPP_ASSERT(discard==0);
}
+////////////////////////////// IETF XChaCha20 //////////////////////////////
+
+std::string XChaCha20_Policy::AlgorithmName() const
+{
+ return std::string("XChaCha20");
+}
+
+std::string XChaCha20_Policy::AlgorithmProvider() const
+{
+ return ChaCha_AlgorithmProvider();
+}
+
+void XChaCha20_Policy::CipherSetKey(const NameValuePairs &params, const byte *key, size_t length)
+{
+ CRYPTOPP_ASSERT(key); CRYPTOPP_ASSERT(length == 32);
+
+ // XChaCha20 is always 20 rounds. Fetch Rounds() to avoid a spurious failure.
+ int rounds = params.GetIntValueWithDefault(Name::Rounds(), ROUNDS);
+ if (rounds != 20)
+ throw InvalidRounds(XChaCha20::StaticAlgorithmName(), rounds);
+
+ word64 block;
+ if (params.GetValue("InitialBlock", block))
+ m_state[CTR] = static_cast<word32>(block);
+ else
+ m_state[CTR] = 1;
+
+ // Stash key away for use in CipherResynchronize
+ GetBlock<word32, LittleEndian> get(key);
+ get(m_state[KEY+0])(m_state[KEY+1])(m_state[KEY+2])(m_state[KEY+3])
+ (m_state[KEY+4])(m_state[KEY+5])(m_state[KEY+6])(m_state[KEY+7]);
+}
+
+void XChaCha20_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length)
+{
+ CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
+ CRYPTOPP_ASSERT(length==24);
+
+ // HChaCha derivation
+ m_state[0] = 0x61707865; m_state[1] = 0x3320646e;
+ m_state[2] = 0x79622d32; m_state[3] = 0x6b206574;
+
+ // Copy saved key into state
+ std::memcpy(m_state+4, m_state+KEY, 8*sizeof(word32));
+
+ GetBlock<word32, LittleEndian> get(iv);
+ get(m_state[12])(m_state[13])(m_state[14])(m_state[15]);
+
+ // Operate the keystream without adding state back in.
+ // This function also gathers the key words into a
+ // contiguous 8-word block.
+ HChaCha_OperateKeystream(m_state, m_state+4);
+
+ // XChaCha state
+ m_state[0] = 0x61707865; m_state[1] = 0x3320646e;
+ m_state[2] = 0x79622d32; m_state[3] = 0x6b206574;
+
+ // Setup new IV
+ m_state[12] = m_state[CTR];
+ m_state[13] = 0;
+ m_state[14] = GetWord<word32>(false, LITTLE_ENDIAN_ORDER, iv+16);
+ m_state[15] = GetWord<word32>(false, LITTLE_ENDIAN_ORDER, iv+20);
+}
+
+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);
+}
+
+unsigned int XChaCha20_Policy::GetAlignment() const
+{
+ return ChaCha_GetAlignment();
+}
+
+unsigned int XChaCha20_Policy::GetOptimalBlockSize() const
+{
+ return ChaCha_GetOptimalBlockSize();
+}
+
+void XChaCha20_Policy::OperateKeystream(KeystreamOperation operation,
+ byte *output, const byte *input, size_t iterationCount)
+{
+ ChaCha_OperateKeystream(operation, m_state, m_state[12], m_state[13],
+ ROUNDS, output, input, iterationCount);
+}
+
NAMESPACE_END