Path: blob/a-new-beginning/SharedDependencies/Sources/cryptopp/chacha.cpp
2 views
// chacha.cpp - written and placed in the public domain by Jeffrey Walton.1// Based on Wei Dai's Salsa20, Botan's SSE2 implementation,2// and Bernstein's reference ChaCha family implementation at3// http://cr.yp.to/chacha.html.45#include "pch.h"6#include "config.h"7#include "chacha.h"8#include "argnames.h"9#include "misc.h"10#include "cpu.h"1112// Internal compiler error in GCC 3.3 and below13#if defined(__GNUC__) && (__GNUC__ < 4)14# undef CRYPTOPP_SSE2_INTRIN_AVAILABLE15#endif1617NAMESPACE_BEGIN(CryptoPP)1819#if (CRYPTOPP_ARM_NEON_AVAILABLE)20extern void ChaCha_OperateKeystream_NEON(const word32 *state, const byte* input, byte *output, unsigned int rounds);21#endif2223#if (CRYPTOPP_AVX2_AVAILABLE)24extern void ChaCha_OperateKeystream_AVX2(const word32 *state, const byte* input, byte *output, unsigned int rounds);25#endif26#if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)27extern void ChaCha_OperateKeystream_SSE2(const word32 *state, const byte* input, byte *output, unsigned int rounds);28#endif2930#if (CRYPTOPP_ALTIVEC_AVAILABLE)31extern void ChaCha_OperateKeystream_ALTIVEC(const word32 *state, const byte* input, byte *output, unsigned int rounds);32#endif3334#if defined(CRYPTOPP_DEBUG) && !defined(CRYPTOPP_DOXYGEN_PROCESSING)35void ChaCha_TestInstantiations()36{37ChaCha::Encryption x;38ChaChaTLS::Encryption y;39XChaCha20::Encryption z;40}41#endif4243NAMESPACE_END // CryptoPP4445////////////////////////////// ChaCha Core //////////////////////////////4647#define CHACHA_QUARTER_ROUND(a,b,c,d) \48a += b; d ^= a; d = rotlConstant<16,word32>(d); \49c += d; b ^= c; b = rotlConstant<12,word32>(b); \50a += b; d ^= a; d = rotlConstant<8,word32>(d); \51c += d; b ^= c; b = rotlConstant<7,word32>(b);5253#define CHACHA_OUTPUT(x){\54CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 0, x0 + state[0]);\55CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 1, x1 + state[1]);\56CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 2, x2 + state[2]);\57CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 3, x3 + state[3]);\58CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 4, x4 + state[4]);\59CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 5, x5 + state[5]);\60CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 6, x6 + state[6]);\61CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 7, x7 + state[7]);\62CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 8, x8 + state[8]);\63CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 9, x9 + state[9]);\64CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 10, x10 + state[10]);\65CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 11, x11 + state[11]);\66CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 12, x12 + state[12]);\67CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 13, x13 + state[13]);\68CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 14, x14 + state[14]);\69CRYPTOPP_KEYSTREAM_OUTPUT_WORD(x, LITTLE_ENDIAN_ORDER, 15, x15 + state[15]);}7071ANONYMOUS_NAMESPACE_BEGIN7273// Hacks... Bring in all symbols, and supply74// the stuff the templates normally provide.75using namespace CryptoPP;76typedef word32 WordType;77enum {BYTES_PER_ITERATION=64};7879// MultiBlockSafe detects a condition that can arise in the SIMD80// implementations where we overflow one of the 32-bit state words during81// addition in an intermediate result. Preconditions for the issue include82// a user seeks to around 2^32 blocks (256 GB of data) for ChaCha; or a83// user specifies an arbitrarily large initial counter block for ChaChaTLS.84// Also see https://github.com/weidai11/cryptopp/issues/732.85inline bool MultiBlockSafe(unsigned int ctrLow, unsigned int blocks)86{87return 0xffffffff - ctrLow > blocks;88}8990// OperateKeystream always produces a key stream. The key stream is written91// to output. Optionally a message may be supplied to xor with the key stream.92// The message is input, and output = output ^ input.93void ChaCha_OperateKeystream(KeystreamOperation operation,94word32 state[16], word32& ctrLow, word32& ctrHigh, word32 rounds,95byte *output, const byte *input, size_t iterationCount)96{97do98{99#if (CRYPTOPP_AVX2_AVAILABLE)100if (HasAVX2())101{102while (iterationCount >= 8 && MultiBlockSafe(state[12], 8))103{104const bool xorInput = (operation & EnumToInt(INPUT_NULL)) != EnumToInt(INPUT_NULL);105ChaCha_OperateKeystream_AVX2(state, xorInput ? input : NULLPTR, output, rounds);106107// MultiBlockSafe avoids overflow on the counter words108state[12] += 8;109110input += (!!xorInput) * 8 * BYTES_PER_ITERATION;111output += 8 * BYTES_PER_ITERATION;112iterationCount -= 8;113}114}115#endif116117#if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)118if (HasSSE2())119{120while (iterationCount >= 4 && MultiBlockSafe(state[12], 4))121{122const bool xorInput = (operation & EnumToInt(INPUT_NULL)) != EnumToInt(INPUT_NULL);123ChaCha_OperateKeystream_SSE2(state, xorInput ? input : NULLPTR, output, rounds);124125// MultiBlockSafe avoids overflow on the counter words126state[12] += 4;127128input += (!!xorInput)*4*BYTES_PER_ITERATION;129output += 4*BYTES_PER_ITERATION;130iterationCount -= 4;131}132}133#endif134135#if (CRYPTOPP_ARM_NEON_AVAILABLE)136if (HasNEON())137{138while (iterationCount >= 4 && MultiBlockSafe(state[12], 4))139{140const bool xorInput = (operation & EnumToInt(INPUT_NULL)) != EnumToInt(INPUT_NULL);141ChaCha_OperateKeystream_NEON(state, xorInput ? input : NULLPTR, output, rounds);142143// MultiBlockSafe avoids overflow on the counter words144state[12] += 4;145146input += (!!xorInput)*4*BYTES_PER_ITERATION;147output += 4*BYTES_PER_ITERATION;148iterationCount -= 4;149}150}151#endif152153#if (CRYPTOPP_ALTIVEC_AVAILABLE)154if (HasAltivec())155{156while (iterationCount >= 4 && MultiBlockSafe(state[12], 4))157{158const bool xorInput = (operation & EnumToInt(INPUT_NULL)) != EnumToInt(INPUT_NULL);159ChaCha_OperateKeystream_ALTIVEC(state, xorInput ? input : NULLPTR, output, rounds);160161// MultiBlockSafe avoids overflow on the counter words162state[12] += 4;163164input += (!!xorInput)*4*BYTES_PER_ITERATION;165output += 4*BYTES_PER_ITERATION;166iterationCount -= 4;167}168}169#endif170171if (iterationCount)172{173word32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;174175x0 = state[0]; x1 = state[1]; x2 = state[2]; x3 = state[3];176x4 = state[4]; x5 = state[5]; x6 = state[6]; x7 = state[7];177x8 = state[8]; x9 = state[9]; x10 = state[10]; x11 = state[11];178x12 = state[12]; x13 = state[13]; x14 = state[14]; x15 = state[15];179180for (int i = static_cast<int>(rounds); i > 0; i -= 2)181{182CHACHA_QUARTER_ROUND(x0, x4, x8, x12);183CHACHA_QUARTER_ROUND(x1, x5, x9, x13);184CHACHA_QUARTER_ROUND(x2, x6, x10, x14);185CHACHA_QUARTER_ROUND(x3, x7, x11, x15);186187CHACHA_QUARTER_ROUND(x0, x5, x10, x15);188CHACHA_QUARTER_ROUND(x1, x6, x11, x12);189CHACHA_QUARTER_ROUND(x2, x7, x8, x13);190CHACHA_QUARTER_ROUND(x3, x4, x9, x14);191}192193CRYPTOPP_KEYSTREAM_OUTPUT_SWITCH(CHACHA_OUTPUT, BYTES_PER_ITERATION);194195// This is state[12] and state[13] from ChaCha. In the case of196// ChaChaTLS ctrHigh is a reference to a discard value.197if (++ctrLow == 0)198ctrHigh++;199}200201// We may re-enter a SIMD keystream operation from here.202} while (iterationCount--);203}204205// XChaCha key derivation206void HChaCha_OperateKeystream(const word32 state[16], word32 output[8])207{208word32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;209210x0 = state[0]; x1 = state[1]; x2 = state[2]; x3 = state[3];211x4 = state[4]; x5 = state[5]; x6 = state[6]; x7 = state[7];212x8 = state[8]; x9 = state[9]; x10 = state[10]; x11 = state[11];213x12 = state[12]; x13 = state[13]; x14 = state[14]; x15 = state[15];214215for (int i = 20; i > 0; i -= 2)216{217CHACHA_QUARTER_ROUND(x0, x4, x8, x12);218CHACHA_QUARTER_ROUND(x1, x5, x9, x13);219CHACHA_QUARTER_ROUND(x2, x6, x10, x14);220CHACHA_QUARTER_ROUND(x3, x7, x11, x15);221222CHACHA_QUARTER_ROUND(x0, x5, x10, x15);223CHACHA_QUARTER_ROUND(x1, x6, x11, x12);224CHACHA_QUARTER_ROUND(x2, x7, x8, x13);225CHACHA_QUARTER_ROUND(x3, x4, x9, x14);226}227228output[0] = x0; output[1] = x1;229output[2] = x2; output[3] = x3;230output[4] = x12; output[5] = x13;231output[6] = x14; output[7] = x15;232}233234std::string ChaCha_AlgorithmProvider()235{236#if (CRYPTOPP_AVX2_AVAILABLE)237if (HasAVX2())238return "AVX2";239else240#endif241#if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)242if (HasSSE2())243return "SSE2";244else245#endif246#if (CRYPTOPP_ARM_NEON_AVAILABLE)247if (HasNEON())248return "NEON";249else250#endif251#if (CRYPTOPP_ALTIVEC_AVAILABLE)252if (HasAltivec())253return "Altivec";254else255#endif256return "C++";257}258259unsigned int ChaCha_GetAlignment()260{261#if (CRYPTOPP_AVX2_AVAILABLE)262if (HasAVX2())263return 16;264else265#endif266#if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)267if (HasSSE2())268return 16;269else270#endif271#if (CRYPTOPP_ALTIVEC_AVAILABLE)272if (HasAltivec())273return 16;274else275#endif276return GetAlignmentOf<word32>();277}278279unsigned int ChaCha_GetOptimalBlockSize()280{281#if (CRYPTOPP_AVX2_AVAILABLE)282if (HasAVX2())283return 8 * BYTES_PER_ITERATION;284else285#endif286#if (CRYPTOPP_SSE2_INTRIN_AVAILABLE)287if (HasSSE2())288return 4*BYTES_PER_ITERATION;289else290#endif291#if (CRYPTOPP_ARM_NEON_AVAILABLE)292if (HasNEON())293return 4*BYTES_PER_ITERATION;294else295#endif296#if (CRYPTOPP_ALTIVEC_AVAILABLE)297if (HasAltivec())298return 4*BYTES_PER_ITERATION;299else300#endif301return BYTES_PER_ITERATION;302}303304ANONYMOUS_NAMESPACE_END305306NAMESPACE_BEGIN(CryptoPP)307308////////////////////////////// Bernstein ChaCha //////////////////////////////309310std::string ChaCha_Policy::AlgorithmName() const311{312return std::string("ChaCha")+IntToString(m_rounds);313}314315std::string ChaCha_Policy::AlgorithmProvider() const316{317return ChaCha_AlgorithmProvider();318}319320void ChaCha_Policy::CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length)321{322CRYPTOPP_ASSERT(key); CRYPTOPP_ASSERT(length == 16 || length == 32);323CRYPTOPP_UNUSED(key); CRYPTOPP_UNUSED(length);324325// Use previous rounds as the default value326int rounds = params.GetIntValueWithDefault(Name::Rounds(), m_rounds);327if (rounds != 20 && rounds != 12 && rounds != 8)328throw InvalidRounds(ChaCha::StaticAlgorithmName(), rounds);329330// Latch a good value331m_rounds = rounds;332333// "expand 16-byte k" or "expand 32-byte k"334m_state[0] = 0x61707865;335m_state[1] = (length == 16) ? 0x3120646e : 0x3320646e;336m_state[2] = (length == 16) ? 0x79622d36 : 0x79622d32;337m_state[3] = 0x6b206574;338339GetBlock<word32, LittleEndian> get1(key);340get1(m_state[4])(m_state[5])(m_state[6])(m_state[7]);341342GetBlock<word32, LittleEndian> get2(key + ((length == 32) ? 16 : 0));343get2(m_state[8])(m_state[9])(m_state[10])(m_state[11]);344}345346void ChaCha_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length)347{348CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);349CRYPTOPP_ASSERT(length==8); CRYPTOPP_UNUSED(length);350351GetBlock<word32, LittleEndian> get(IV);352m_state[12] = m_state[13] = 0;353get(m_state[14])(m_state[15]);354}355356void ChaCha_Policy::SeekToIteration(lword iterationCount)357{358m_state[12] = (word32)iterationCount; // low word359m_state[13] = (word32)SafeRightShift<32>(iterationCount);360}361362unsigned int ChaCha_Policy::GetAlignment() const363{364return ChaCha_GetAlignment();365}366367unsigned int ChaCha_Policy::GetOptimalBlockSize() const368{369return ChaCha_GetOptimalBlockSize();370}371372void ChaCha_Policy::OperateKeystream(KeystreamOperation operation,373byte *output, const byte *input, size_t iterationCount)374{375ChaCha_OperateKeystream(operation, m_state, m_state[12], m_state[13],376m_rounds, output, input, iterationCount);377}378379////////////////////////////// IETF ChaChaTLS //////////////////////////////380381std::string ChaChaTLS_Policy::AlgorithmName() const382{383return std::string("ChaChaTLS");384}385386std::string ChaChaTLS_Policy::AlgorithmProvider() const387{388return ChaCha_AlgorithmProvider();389}390391void ChaChaTLS_Policy::CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length)392{393CRYPTOPP_ASSERT(key); CRYPTOPP_ASSERT(length == 32);394CRYPTOPP_UNUSED(length);395396// ChaChaTLS is always 20 rounds. Fetch Rounds() to avoid a spurious failure.397int rounds = params.GetIntValueWithDefault(Name::Rounds(), ROUNDS);398if (rounds != 20)399throw InvalidRounds(ChaChaTLS::StaticAlgorithmName(), rounds);400401// RFC 8439 test vectors use an initial block counter. However, the counter402// can be an arbitrary value per RFC 8439 Section 2.4. We stash the counter403// away in state[16] and use it for a Resynchronize() operation. I think404// the initial counter is used more like a Tweak when non-0, and it should405// be provided in Resynchronize() (light-weight re-keying). However,406// Resynchronize() does not have an overload that allows us to pass it into407// the function, so we have to use the heavier-weight SetKey to change it.408word64 block;409if (params.GetValue("InitialBlock", block))410m_counter = static_cast<word32>(block);411else412m_counter = 0;413414// State words are defined in RFC 8439, Section 2.3. Key is 32-bytes.415GetBlock<word32, LittleEndian> get(key);416get(m_state[KEY+0])(m_state[KEY+1])(m_state[KEY+2])(m_state[KEY+3])417(m_state[KEY+4])(m_state[KEY+5])(m_state[KEY+6])(m_state[KEY+7]);418}419420void ChaChaTLS_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *IV, size_t length)421{422CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);423CRYPTOPP_ASSERT(length==12);424425// State words are defined in RFC 8439, Section 2.3.426m_state[0] = 0x61707865; m_state[1] = 0x3320646e;427m_state[2] = 0x79622d32; m_state[3] = 0x6b206574;428429// Copy saved key into state430std::memcpy(m_state+4, m_state+KEY, 8*sizeof(word32));431432// State words are defined in RFC 8439, Section 2.3433GetBlock<word32, LittleEndian> get(IV);434m_state[12] = m_counter;435get(m_state[13])(m_state[14])(m_state[15]);436}437438void ChaChaTLS_Policy::SeekToIteration(lword iterationCount)439{440// Should we throw here??? If the initial block counter is441// large then we can wrap and process more data as long as442// data processed in the security context does not exceed443// 2^32 blocks or approximately 256 GB of data.444CRYPTOPP_ASSERT(iterationCount <= (std::numeric_limits<word32>::max)());445m_state[12] = (word32)iterationCount; // low word446}447448unsigned int ChaChaTLS_Policy::GetAlignment() const449{450return ChaCha_GetAlignment();451}452453unsigned int ChaChaTLS_Policy::GetOptimalBlockSize() const454{455return ChaCha_GetOptimalBlockSize();456}457458void ChaChaTLS_Policy::OperateKeystream(KeystreamOperation operation,459byte *output, const byte *input, size_t iterationCount)460{461word32 discard=0;462ChaCha_OperateKeystream(operation, m_state, m_state[12], discard,463ROUNDS, output, input, iterationCount);464465// If this fires it means ChaCha_OperateKeystream generated a counter466// block carry that was discarded. The problem is, the RFC does not467// specify what should happen when the counter block wraps. All we can468// do is inform the user that something bad may happen because we don't469// know what we should do.470// Also see https://github.com/weidai11/cryptopp/issues/790 and471// https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU472// CRYPTOPP_ASSERT(discard==0);473}474475////////////////////////////// IETF XChaCha20 //////////////////////////////476477std::string XChaCha20_Policy::AlgorithmName() const478{479return std::string("XChaCha20");480}481482std::string XChaCha20_Policy::AlgorithmProvider() const483{484return ChaCha_AlgorithmProvider();485}486487void XChaCha20_Policy::CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length)488{489CRYPTOPP_ASSERT(key); CRYPTOPP_ASSERT(length == 32);490CRYPTOPP_UNUSED(length);491492// Use previous rounds as the default value493int rounds = params.GetIntValueWithDefault(Name::Rounds(), m_rounds);494if (rounds != 20 && rounds != 12)495throw InvalidRounds(ChaCha::StaticAlgorithmName(), rounds);496497// Latch a good value498m_rounds = rounds;499500word64 block;501if (params.GetValue("InitialBlock", block))502m_counter = static_cast<word32>(block);503else504m_counter = 1;505506// Stash key away for use in CipherResynchronize507GetBlock<word32, LittleEndian> get(key);508get(m_state[KEY+0])(m_state[KEY+1])(m_state[KEY+2])(m_state[KEY+3])509(m_state[KEY+4])(m_state[KEY+5])(m_state[KEY+6])(m_state[KEY+7]);510}511512void XChaCha20_Policy::CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length)513{514CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);515CRYPTOPP_ASSERT(length==24);516517// HChaCha derivation518m_state[0] = 0x61707865; m_state[1] = 0x3320646e;519m_state[2] = 0x79622d32; m_state[3] = 0x6b206574;520521// Copy saved key into state522std::memcpy(m_state+4, m_state+KEY, 8*sizeof(word32));523524GetBlock<word32, LittleEndian> get(iv);525get(m_state[12])(m_state[13])(m_state[14])(m_state[15]);526527// Operate the keystream without adding state back in.528// This function also gathers the key words into a529// contiguous 8-word block.530HChaCha_OperateKeystream(m_state, m_state+4);531532// XChaCha state533m_state[0] = 0x61707865; m_state[1] = 0x3320646e;534m_state[2] = 0x79622d32; m_state[3] = 0x6b206574;535536// Setup new IV537m_state[12] = m_counter;538m_state[13] = 0;539m_state[14] = GetWord<word32>(false, LITTLE_ENDIAN_ORDER, iv+16);540m_state[15] = GetWord<word32>(false, LITTLE_ENDIAN_ORDER, iv+20);541}542543void XChaCha20_Policy::SeekToIteration(lword iterationCount)544{545// Should we throw here??? XChaCha does not have a block546// counter, so I'm not sure how to seek on it.547CRYPTOPP_ASSERT(0); CRYPTOPP_UNUSED(iterationCount);548}549550unsigned int XChaCha20_Policy::GetAlignment() const551{552return ChaCha_GetAlignment();553}554555unsigned int XChaCha20_Policy::GetOptimalBlockSize() const556{557return ChaCha_GetOptimalBlockSize();558}559560void XChaCha20_Policy::OperateKeystream(KeystreamOperation operation,561byte *output, const byte *input, size_t iterationCount)562{563ChaCha_OperateKeystream(operation, m_state, m_state[12], m_state[13],564m_rounds, output, input, iterationCount);565}566567NAMESPACE_END568569570