Sergey Pastor / 1

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers chacha.c Source File

chacha.c

Go to the documentation of this file.
00001 /**
00002  * @file chacha.c
00003  * @brief ChaCha encryption algorithm
00004  *
00005  * @section License
00006  *
00007  * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
00008  *
00009  * This file is part of CycloneCrypto Open.
00010  *
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License
00013  * as published by the Free Software Foundation; either version 2
00014  * of the License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software Foundation,
00023  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024  *
00025  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00026  * @version 1.7.6
00027  **/
00028 
00029 //Switch to the appropriate trace level
00030 #define TRACE_LEVEL CRYPTO_TRACE_LEVEL
00031 
00032 //Dependencies
00033 #include "crypto.h"
00034 #include "chacha.h"
00035 
00036 //Check crypto library configuration
00037 #if (CHACHA_SUPPORT == ENABLED)
00038 
00039 //ChaCha quarter-round function
00040 #define CHACHA_QUARTER_ROUND(a, b, c, d) \
00041 { \
00042    a += b; d ^= a; d = ROL32(d, 16); \
00043    c += d; b ^= c; b = ROL32(b, 12); \
00044    a += b; d ^= a; d = ROL32(d, 8); \
00045    c += d; b ^= c; b = ROL32(b, 7); \
00046 }
00047 
00048 
00049 /**
00050  * @brief Initialize ChaCha context using the supplied key and nonce
00051  * @param[in] context Pointer to the ChaCha context to initialize
00052  * @param[in] nr Number of rounds to be applied (8, 12 or 20)
00053  * @param[in] key Pointer to the key
00054  * @param[in] keyLength Length of the key, in bytes (16 or 32)
00055  * @param[in] nonce Pointer to the nonce
00056  * @param[in] nonceLength Length of the nonce, in bytes (8 or 12)
00057  * @return Error code
00058  **/
00059 
00060 error_t chachaInit(ChachaContext *context, uint_t nr, const uint8_t *key,
00061    size_t keyLength, const uint8_t *nonce, size_t nonceLength)
00062 {
00063    uint32_t *w;
00064 
00065    //The number of rounds must be 8, 12 or 20
00066    if(nr != 8 && nr != 12 && nr != 20)
00067       return ERROR_INVALID_PARAMETER;
00068 
00069    //Save the number of rounds to be applied
00070    context->nr = nr;
00071 
00072    //Point to the state
00073    w = context->state;
00074 
00075    //Check the length of the key
00076    if(keyLength == 16)
00077    {
00078       //The first four input words are constants
00079       w[0] = 0x61707865;
00080       w[1] = 0x3120646E;
00081       w[2] = 0x79622D36;
00082       w[3] = 0x6B206574;
00083 
00084       //Input words 4 through 7 are taken from the 128-bit key, by reading
00085       //the bytes in little-endian order, in 4-byte chunks
00086       w[4] = LOAD32LE(key);
00087       w[5] = LOAD32LE(key + 4);
00088       w[6] = LOAD32LE(key + 8);
00089       w[7] = LOAD32LE(key + 12);
00090 
00091       //Input words 8 through 11 are taken from the 128-bit key, again by
00092       //reading the bytes in little-endian order, in 4-byte chunks
00093       w[8] = LOAD32LE(key);
00094       w[9] = LOAD32LE(key + 4);
00095       w[10] = LOAD32LE(key + 8);
00096       w[11] = LOAD32LE(key + 12);
00097    }
00098    else if(keyLength == 32)
00099    {
00100       //The first four input words are constants
00101       w[0] = 0x61707865;
00102       w[1] = 0x3320646E;
00103       w[2] = 0x79622D32;
00104       w[3] = 0x6B206574;
00105 
00106       //Input words 4 through 11 are taken from the 256-bit key, by reading
00107       //the bytes in little-endian order, in 4-byte chunks
00108       w[4] = LOAD32LE(key);
00109       w[5] = LOAD32LE(key + 4);
00110       w[6] = LOAD32LE(key + 8);
00111       w[7] = LOAD32LE(key + 12);
00112       w[8] = LOAD32LE(key + 16);
00113       w[9] = LOAD32LE(key + 20);
00114       w[10] = LOAD32LE(key + 24);
00115       w[11] = LOAD32LE(key + 28);
00116    }
00117    else
00118    {
00119       //Invalid key length
00120       return ERROR_INVALID_PARAMETER;
00121    }
00122 
00123    //Check the length of the nonce
00124    if(nonceLength == 8)
00125    {
00126       //Input words 12 and 13 are a block counter, with word 12
00127       //overflowing into word 13
00128       w[12] = 0;
00129       w[13] = 0;
00130 
00131       //Input words 14 and 15 are taken from an 64-bit nonce, by reading
00132       //the bytes in little-endian order, in 4-byte chunks
00133       w[14] = LOAD32LE(nonce);
00134       w[15] = LOAD32LE(nonce + 4);
00135    }
00136    else if(nonceLength == 12)
00137    {
00138       //Input word 12 is a block counter
00139       w[12] = 0;
00140 
00141       //Input words 13 to 15 are taken from an 96-bit nonce, by reading
00142       //the bytes in little-endian order, in 4-byte chunks
00143       w[13] = LOAD32LE(nonce);
00144       w[14] = LOAD32LE(nonce + 4);
00145       w[15] = LOAD32LE(nonce + 8);
00146    }
00147    else
00148    {
00149       //Invalid nonce length
00150       return ERROR_INVALID_PARAMETER;
00151    }
00152 
00153    //The keystream block is empty
00154    context->pos = 0;
00155 
00156    //No error to report
00157    return NO_ERROR;
00158 }
00159 
00160 
00161 /**
00162  * @brief Encrypt/decrypt data with the ChaCha algorithm
00163  * @param[in] context Pointer to the ChaCha context
00164  * @param[in] input Pointer to the data to encrypt/decrypt (optional)
00165  * @param[in] output Pointer to the resulting data (optional)
00166  * @param[in] length Number of bytes to be processed
00167  **/
00168 
00169 void chachaCipher(ChachaContext *context, const uint8_t *input,
00170    uint8_t *output, size_t length)
00171 {
00172    uint_t i;
00173    uint_t n;
00174    uint8_t *k;
00175 
00176    //Encryption loop
00177    while(length > 0)
00178    {
00179       //Check whether a new keystream block must be generated
00180       if(context->pos == 0 || context->pos >= 64)
00181       {
00182          //ChaCha successively calls the ChaCha block function, with the same key
00183          //and nonce, and with successively increasing block counter parameters
00184          chachaProcessBlock(context);
00185 
00186          //Increment block counter
00187          context->state[12]++;
00188 
00189          //Propagate the carry if necessary
00190          if(context->state[12] == 0)
00191             context->state[13]++;
00192 
00193          //Rewind to the beginning of the keystream block
00194          context->pos = 0;
00195       }
00196 
00197       //Compute the number of bytes to encrypt/decrypt at a time
00198       n = MIN(length, 64 - context->pos);
00199 
00200       //Valid output pointer?
00201       if(output != NULL)
00202       {
00203          //Point to the keystream
00204          k = (uint8_t *) context->block + context->pos;
00205 
00206          //Valid input pointer?
00207          if(input != NULL)
00208          {
00209             //XOR the input data with the keystream
00210             for(i = 0; i < n; i++)
00211                output[i] = input[i] ^ k[i];
00212 
00213             //Advance input pointer
00214             input += n;
00215          }
00216          else
00217          {
00218             //Output the keystream
00219             for(i = 0; i < n; i++)
00220                output[i] = k[i];
00221          }
00222 
00223          //Advance output pointer
00224          output += n;
00225       }
00226 
00227       //Current position in the keystream block
00228       context->pos += n;
00229       //Remaining bytes to process
00230       length -= n;
00231    }
00232 }
00233 
00234 
00235 /**
00236  * @brief Generate a keystream block
00237  * @param[in] context Pointer to the ChaCha context
00238  **/
00239 
00240 void chachaProcessBlock(ChachaContext *context)
00241 {
00242    uint_t i;
00243    uint32_t *w;
00244 
00245    //Point to the working state
00246    w = (uint32_t *) context->block;
00247 
00248    //Copy the state to the working state
00249    for(i = 0; i < 16; i++)
00250       w[i] = context->state[i];
00251 
00252    //ChaCha runs 8, 12 or 20 rounds, alternating between column rounds
00253    //and diagonal rounds
00254    for(i = 0; i < context->nr; i += 2)
00255    {
00256       //The column rounds apply the quarter-round function to the four
00257       //columns, from left to right
00258       CHACHA_QUARTER_ROUND(w[0], w[4], w[8], w[12]);
00259       CHACHA_QUARTER_ROUND(w[1], w[5], w[9], w[13]);
00260       CHACHA_QUARTER_ROUND(w[2], w[6], w[10], w[14]);
00261       CHACHA_QUARTER_ROUND(w[3], w[7], w[11], w[15]);
00262 
00263       //The diagonal rounds apply the quarter-round function to the top-left,
00264       //bottom-right diagonal, followed by the pattern shifted one place to
00265       //the right, for three more quarter-rounds
00266       CHACHA_QUARTER_ROUND(w[0], w[5], w[10], w[15]);
00267       CHACHA_QUARTER_ROUND(w[1], w[6], w[11], w[12]);
00268       CHACHA_QUARTER_ROUND(w[2], w[7], w[8], w[13]);
00269       CHACHA_QUARTER_ROUND(w[3], w[4], w[9], w[14]);
00270    }
00271 
00272    //Add the original input words to the output words
00273    for(i = 0; i < 16; i++)
00274       w[i] += context->state[i];
00275 
00276    //Serialize the result by sequencing the words one-by-one in
00277    //little-endian order
00278    for(i = 0; i < 16; i++)
00279       w[i] = htole32(w[i]);
00280 }
00281 
00282 #endif
00283