mirror of
https://github.com/taigrr/arduinolibs
synced 2025-01-18 04:33:12 -08:00
Speed up GHASH with AVR assembly code
Also split the Galois operations off into a separate GF128 class.
This commit is contained in:
parent
2decb74161
commit
21ac06136a
@ -86,12 +86,15 @@ Ardunino Mega 2560 running at 16 MHz are similar:
|
|||||||
<tr><td colspan="5"> </td></tr>
|
<tr><td colspan="5"> </td></tr>
|
||||||
<tr><td>AEAD Algorithm</td><td align="right">Encryption (per byte)</td><td align="right">Decryption (per byte)</td><td>Key Setup</td><td>State Size (bytes)</td></tr>
|
<tr><td>AEAD Algorithm</td><td align="right">Encryption (per byte)</td><td align="right">Decryption (per byte)</td><td>Key Setup</td><td>State Size (bytes)</td></tr>
|
||||||
<tr><td>ChaChaPoly</td><td align="right">41.20us</td><td align="right">41.19us</td><td align="right">902.36us</td><td align="right">221</td></tr>
|
<tr><td>ChaChaPoly</td><td align="right">41.20us</td><td align="right">41.19us</td><td align="right">902.36us</td><td align="right">221</td></tr>
|
||||||
<tr><td>GCM<AES128></td><td align="right">183.25us</td><td align="right">182.80us</td><td align="right">1272.73us</td><td align="right">284</td></tr>
|
<tr><td>GCM<AES128></td><td align="right">109.71us</td><td align="right">109.26us</td><td align="right">1265.69us</td><td align="right">284</td></tr>
|
||||||
<tr><td>GCM<AES192></td><td align="right">189.92us</td><td align="right">189.47us</td><td align="right">1492.60us</td><td align="right">316</td></tr>
|
<tr><td>GCM<AES192></td><td align="right">116.38us</td><td align="right">115.92us</td><td align="right">1485.56us</td><td align="right">316</td></tr>
|
||||||
<tr><td>GCM<AES256></td><td align="right">196.59us</td><td align="right">196.13us</td><td align="right">1767.33us</td><td align="right">348</td></tr>
|
<tr><td>GCM<AES256></td><td align="right">123.04us</td><td align="right">122.59us</td><td align="right">1760.28us</td><td align="right">348</td></tr>
|
||||||
<tr><td>EAX<AES128></td><td align="right">71.14us</td><td align="right">71.14us</td><td align="right">1329.44us</td><td align="right">268</td></tr>
|
<tr><td>GCM<Speck> (256-bit key)</td><td align="right">87.78us</td><td align="right">87.32us</td><td align="right">714.41us</td><td align="right">378</td></tr>
|
||||||
<tr><td>EAX<Speck> (128-bit key)</td><td align="right">26.01us</td><td align="right">26.01us</td><td align="right">735.46us</td><td align="right">362</td></tr>
|
<tr><td>GCM<SpeckLowMemory> (256-bit key)</td><td align="right">114.30us</td><td align="right">113.84us</td><td align="right">1270.32us</td><td align="right">138</td></tr>
|
||||||
<tr><td>EAX<SpeckLowMemory> (128-bit key)</td><td align="right">75.08us</td><td align="right">75.07us</td><td align="right">1243.66us</td><td align="right">122</td></tr>
|
<tr><td>EAX<AES128></td><td align="right">71.14us</td><td align="right">71.14us</td><td align="right">1311.97us</td><td align="right">268</td></tr>
|
||||||
|
<tr><td>EAX<AES256></td><td align="right">97.80us</td><td align="right">97.80us</td><td align="right">1806.57us</td><td align="right">332</td></tr>
|
||||||
|
<tr><td>EAX<Speck> (256-bit key)</td><td align="right">27.27us</td><td align="right">27.26us</td><td align="right">760.74us</td><td align="right">362</td></tr>
|
||||||
|
<tr><td>EAX<SpeckLowMemory> (256-bit key)</td><td align="right">80.31us</td><td align="right">80.31us</td><td align="right">1316.60us</td><td align="right">122</td></tr>
|
||||||
<tr><td colspan="5"> </td></tr>
|
<tr><td colspan="5"> </td></tr>
|
||||||
<tr><td>Hash Algorithm</td><td align="right">Hashing (per byte)</td><td align="right">Finalization</td><td> </td><td>State Size (bytes)</td></tr>
|
<tr><td>Hash Algorithm</td><td align="right">Hashing (per byte)</td><td align="right">Finalization</td><td> </td><td>State Size (bytes)</td></tr>
|
||||||
<tr><td>SHA256</td><td align="right">43.85us</td><td align="right">2841.04us</td><td align="right"> </td><td align="right">107</td></tr>
|
<tr><td>SHA256</td><td align="right">43.85us</td><td align="right">2841.04us</td><td align="right"> </td><td align="right">107</td></tr>
|
||||||
@ -105,7 +108,7 @@ Ardunino Mega 2560 running at 16 MHz are similar:
|
|||||||
<tr><td>SHA256 (HMAC mode)</td><td align="right">43.85us</td><td align="right">8552.61us</td><td align="right">2836.49us</td><td align="right">107</td></tr>
|
<tr><td>SHA256 (HMAC mode)</td><td align="right">43.85us</td><td align="right">8552.61us</td><td align="right">2836.49us</td><td align="right">107</td></tr>
|
||||||
<tr><td>BLAKE2s (HMAC mode)</td><td align="right">20.65us</td><td align="right">4055.56us</td><td align="right">1350.00us</td><td align="right">107</td></tr>
|
<tr><td>BLAKE2s (HMAC mode)</td><td align="right">20.65us</td><td align="right">4055.56us</td><td align="right">1350.00us</td><td align="right">107</td></tr>
|
||||||
<tr><td>Poly1305</td><td align="right">26.26us</td><td align="right">489.11us</td><td align="right">17.06us</td><td align="right">53</td></tr>
|
<tr><td>Poly1305</td><td align="right">26.26us</td><td align="right">489.11us</td><td align="right">17.06us</td><td align="right">53</td></tr>
|
||||||
<tr><td>GHASH</td><td align="right">148.14us</td><td align="right">17.09us</td><td align="right">21.87us</td><td align="right">33</td></tr>
|
<tr><td>GHASH</td><td align="right">74.59us</td><td align="right">15.91us</td><td align="right">14.79us</td><td align="right">33</td></tr>
|
||||||
<tr><td colspan="5"> </td></tr>
|
<tr><td colspan="5"> </td></tr>
|
||||||
<tr><td>Public Key Operation</td><td align="right">Time (per operation)</td><td colspan="3">Comment</td></tr>
|
<tr><td>Public Key Operation</td><td align="right">Time (per operation)</td><td colspan="3">Comment</td></tr>
|
||||||
<tr><td>Curve25519::eval()</td><td align="right">3119ms</td><td colspan="3">Raw curve evaluation</td></tr>
|
<tr><td>Curve25519::eval()</td><td align="right">3119ms</td><td colspan="3">Raw curve evaluation</td></tr>
|
||||||
@ -141,12 +144,15 @@ All figures are for the Arduino Due running at 84 MHz:
|
|||||||
<tr><td colspan="5"> </td></tr>
|
<tr><td colspan="5"> </td></tr>
|
||||||
<tr><td>AEAD Algorithm</td><td align="right">Encryption (per byte)</td><td align="right">Decryption (per byte)</td><td>Key Setup</td><td>State Size (bytes)</td></tr>
|
<tr><td>AEAD Algorithm</td><td align="right">Encryption (per byte)</td><td align="right">Decryption (per byte)</td><td>Key Setup</td><td>State Size (bytes)</td></tr>
|
||||||
<tr><td>ChaChaPoly</td><td align="right">1.71us</td><td align="right">1.71us</td><td align="right">45.08us</td><td align="right">240</td></tr>
|
<tr><td>ChaChaPoly</td><td align="right">1.71us</td><td align="right">1.71us</td><td align="right">45.08us</td><td align="right">240</td></tr>
|
||||||
<tr><td>GCM<AES128></td><td align="right">10.29us</td><td align="right">10.29us</td><td align="right">223.82us</td><td align="right">312</td></tr>
|
<tr><td>GCM<AES128></td><td align="right">10.90us</td><td align="right">10.90us</td><td align="right">248.83us</td><td align="right">312</td></tr>
|
||||||
<tr><td>GCM<AES192></td><td align="right">11.50us</td><td align="right">11.51us</td><td align="right">265.62us</td><td align="right">344</td></tr>
|
<tr><td>GCM<AES192></td><td align="right">12.30us</td><td align="right">12.31us</td><td align="right">296.83us</td><td align="right">344</td></tr>
|
||||||
<tr><td>GCM<AES256></td><td align="right">12.67us</td><td align="right">12.67us</td><td align="right">313.06us</td><td align="right">376</td></tr>
|
<tr><td>GCM<AES256></td><td align="right">13.66us</td><td align="right">13.67us</td><td align="right">350.25us</td><td align="right">376</td></tr>
|
||||||
<tr><td>EAX<AES128></td><td align="right">12.29us</td><td align="right">12.29us</td><td align="right">236.47us</td><td align="right">280</td></tr>
|
<tr><td>GCM<Speck> (256-bit key)</td><td align="right">5.27us</td><td align="right">5.28us</td><td align="right">75.31us</td><td align="right">408</td></tr>
|
||||||
<tr><td>EAX<Speck> (128-bit key)</td><td align="right">2.65us</td><td align="right">2.65us</td><td align="right">79.46us</td><td align="right">384</td></tr>
|
<tr><td>GCM<SpeckLowMemory> (256-bit key)</td><td align="right">7.06us</td><td align="right">7.07us</td><td align="right">94.20us</td><td align="right">168</td></tr>
|
||||||
<tr><td>EAX<SpeckLowMemory> (128-bit key)</td><td align="right">6.29us</td><td align="right">6.29us</td><td align="right">106.60us</td><td align="right">144</td></tr>
|
<tr><td>EAX<AES128></td><td align="right">12.33us</td><td align="right">12.33us</td><td align="right">234.91us</td><td align="right">280</td></tr>
|
||||||
|
<tr><td>EAX<AES256></td><td align="right">16.99us</td><td align="right">16.99us</td><td align="right">322.92us</td><td align="right">344</td></tr>
|
||||||
|
<tr><td>EAX<Speck> (256-bit key)</td><td align="right">2.80us</td><td align="right">2.80us</td><td align="right">81.63us</td><td align="right">384</td></tr>
|
||||||
|
<tr><td>EAX<SpeckLowMemory> (256-bit key)</td><td align="right">6.69us</td><td align="right">6.69us</td><td align="right">110.91us</td><td align="right">144</td></tr>
|
||||||
<tr><td colspan="5"> </td></tr>
|
<tr><td colspan="5"> </td></tr>
|
||||||
<tr><td>Hash Algorithm</td><td align="right">Hashing (per byte)</td><td align="right">Finalization</td><td> </td><td>State Size (bytes)</td></tr>
|
<tr><td>Hash Algorithm</td><td align="right">Hashing (per byte)</td><td align="right">Finalization</td><td> </td><td>State Size (bytes)</td></tr>
|
||||||
<tr><td>SHA256</td><td align="right">1.15us</td><td align="right">76.60us</td><td align="right"> </td><td align="right">120</td></tr>
|
<tr><td>SHA256</td><td align="right">1.15us</td><td align="right">76.60us</td><td align="right"> </td><td align="right">120</td></tr>
|
||||||
@ -160,7 +166,7 @@ All figures are for the Arduino Due running at 84 MHz:
|
|||||||
<tr><td>SHA256 (HMAC mode)</td><td align="right">1.15us</td><td align="right">238.98us</td><td align="right">80.44us</td><td align="right">120</td></tr>
|
<tr><td>SHA256 (HMAC mode)</td><td align="right">1.15us</td><td align="right">238.98us</td><td align="right">80.44us</td><td align="right">120</td></tr>
|
||||||
<tr><td>BLAKE2s (HMAC mode)</td><td align="right">0.72us</td><td align="right">157.75us</td><td align="right">57.18us</td><td align="right">120</td></tr>
|
<tr><td>BLAKE2s (HMAC mode)</td><td align="right">0.72us</td><td align="right">157.75us</td><td align="right">57.18us</td><td align="right">120</td></tr>
|
||||||
<tr><td>Poly1305</td><td align="right">0.81us</td><td align="right">19.01us</td><td align="right">2.57us</td><td align="right">60</td></tr>
|
<tr><td>Poly1305</td><td align="right">0.81us</td><td align="right">19.01us</td><td align="right">2.57us</td><td align="right">60</td></tr>
|
||||||
<tr><td>GHASH</td><td align="right">4.37us</td><td align="right">1.50us</td><td align="right">4.37us</td><td align="right">36</td></tr>
|
<tr><td>GHASH</td><td align="right">4.47us</td><td align="right">1.52us</td><td align="right">2.60us</td><td align="right">36</td></tr>
|
||||||
<tr><td colspan="5"> </td></tr>
|
<tr><td colspan="5"> </td></tr>
|
||||||
<tr><td>Public Key Operation</td><td align="right">Time (per operation)</td><td colspan="3">Comment</td></tr>
|
<tr><td>Public Key Operation</td><td align="right">Time (per operation)</td><td colspan="3">Comment</td></tr>
|
||||||
<tr><td>Curve25519::eval()</td><td align="right">103ms</td><td colspan="3">Raw curve evaluation</td></tr>
|
<tr><td>Curve25519::eval()</td><td align="right">103ms</td><td colspan="3">Raw curve evaluation</td></tr>
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "EAX.h"
|
#include "EAX.h"
|
||||||
|
#include "GF128.h"
|
||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -145,19 +146,6 @@ void EAXCommon::clear()
|
|||||||
clean(state);
|
clean(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Doubles a 128-bit value in the GF(2^128) field.
|
|
||||||
static void gfDouble(uint8_t value[16])
|
|
||||||
{
|
|
||||||
uint16_t temp = 0;
|
|
||||||
for (uint8_t index = 16; index > 0; ) {
|
|
||||||
--index;
|
|
||||||
temp |= (((uint16_t)(value[index])) << 1);
|
|
||||||
value[index] = (uint8_t)temp;
|
|
||||||
temp >>= 8;
|
|
||||||
}
|
|
||||||
value[15] ^= (uint8_t)((-temp) & 0x87);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Initialises the first OMAC hashing context and creates the B value.
|
* \brief Initialises the first OMAC hashing context and creates the B value.
|
||||||
*
|
*
|
||||||
@ -175,7 +163,7 @@ void EAXCommon::omacInitFirst(uint8_t omac[16])
|
|||||||
// Generate the B value from the encrypted block of zeroes.
|
// Generate the B value from the encrypted block of zeroes.
|
||||||
// We will need this later when finalising the OMAC hashes.
|
// We will need this later when finalising the OMAC hashes.
|
||||||
memcpy(state.b, omac, 16);
|
memcpy(state.b, omac, 16);
|
||||||
gfDouble(state.b);
|
GF128::dblEAX(state.b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -230,17 +218,17 @@ void EAXCommon::omacFinal(uint8_t omac[16])
|
|||||||
// Apply padding if necessary.
|
// Apply padding if necessary.
|
||||||
if (state.authPosn != 16) {
|
if (state.authPosn != 16) {
|
||||||
// Need padding: XOR with P = 2 * B.
|
// Need padding: XOR with P = 2 * B.
|
||||||
uint8_t p[16];
|
uint32_t p[4];
|
||||||
memcpy(p, state.b, 16);
|
memcpy(p, state.b, 16);
|
||||||
gfDouble(p);
|
GF128::dblEAX(p);
|
||||||
omac[state.authPosn] ^= 0x80;
|
omac[state.authPosn] ^= 0x80;
|
||||||
for (uint8_t index = 0; index < 16; ++index)
|
for (uint8_t index = 0; index < 16; ++index)
|
||||||
omac[index] ^= p[index];
|
omac[index] ^= ((const uint8_t *)p)[index];
|
||||||
clean(p);
|
clean(p);
|
||||||
} else {
|
} else {
|
||||||
// No padding necessary: XOR with B.
|
// No padding necessary: XOR with B.
|
||||||
for (uint8_t index = 0; index < 16; ++index)
|
for (uint8_t index = 0; index < 16; ++index)
|
||||||
omac[index] ^= state.b[index];
|
omac[index] ^= ((const uint8_t *)(state.b))[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt the hash to get the final OMAC value.
|
// Encrypt the hash to get the final OMAC value.
|
||||||
|
@ -59,7 +59,7 @@ private:
|
|||||||
uint8_t stream[16];
|
uint8_t stream[16];
|
||||||
uint8_t tag[16];
|
uint8_t tag[16];
|
||||||
uint8_t hash[16];
|
uint8_t hash[16];
|
||||||
uint8_t b[16];
|
uint32_t b[4];
|
||||||
uint8_t encPosn;
|
uint8_t encPosn;
|
||||||
uint8_t authPosn;
|
uint8_t authPosn;
|
||||||
uint8_t authMode;
|
uint8_t authMode;
|
||||||
|
480
libraries/Crypto/GF128.cpp
Normal file
480
libraries/Crypto/GF128.cpp
Normal file
@ -0,0 +1,480 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "GF128.h"
|
||||||
|
#include "utility/EndianUtil.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class GF128 GF128.h <GF128.h>
|
||||||
|
* \brief Operations in the Galois field GF(2^128).
|
||||||
|
*
|
||||||
|
* This class contains helper functions for performing operations in
|
||||||
|
* the Galois field GF(2^128) which is used as the basis of GCM and GHASH.
|
||||||
|
* These functions are provided for use by other cryptographic protocols
|
||||||
|
* that make use of GF(2^128).
|
||||||
|
*
|
||||||
|
* Most of the functions in this class use the field, polynomial, and
|
||||||
|
* byte ordering conventions described in NIST SP 800-38D (GCM). The one
|
||||||
|
* exception is dblEAX() which uses the conventions of EAX mode instead.
|
||||||
|
*
|
||||||
|
* References: <a href="http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf">NIST SP 800-38D</a>
|
||||||
|
*
|
||||||
|
* \sa GCM, GHASH
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Initialize multiplication in the GF(2^128) field.
|
||||||
|
*
|
||||||
|
* \param H The hash state to be initialized.
|
||||||
|
* \param key Points to the 16 byte authentication key which is assumed
|
||||||
|
* to be in big-endian byte order.
|
||||||
|
*
|
||||||
|
* This function and the companion mul() are intended for use by other
|
||||||
|
* classes that need access to the raw GF(2^128) field multiplication of
|
||||||
|
* GHASH without the overhead of GHASH itself.
|
||||||
|
*
|
||||||
|
* \sa mul(), dbl()
|
||||||
|
*/
|
||||||
|
void GF128::mulInit(uint32_t H[4], const void *key)
|
||||||
|
{
|
||||||
|
#if defined(__AVR__)
|
||||||
|
// Copy the key into H but leave it in big endian order because
|
||||||
|
// we can correct for the byte order in mul() below.
|
||||||
|
memcpy(H, key, 16);
|
||||||
|
#else
|
||||||
|
// Copy the key into H and convert from big endian to host order.
|
||||||
|
memcpy(H, key, 16);
|
||||||
|
#if defined(CRYPTO_LITTLE_ENDIAN)
|
||||||
|
H[0] = be32toh(H[0]);
|
||||||
|
H[1] = be32toh(H[1]);
|
||||||
|
H[2] = be32toh(H[2]);
|
||||||
|
H[3] = be32toh(H[3]);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Perform a multiplication in the GF(2^128) field.
|
||||||
|
*
|
||||||
|
* \param Y The first value to multiply, and the result. This array is
|
||||||
|
* assumed to be in big-endian order on entry and exit.
|
||||||
|
* \param H The second value to multiply, which must have been initialized
|
||||||
|
* by the mulInit() function.
|
||||||
|
*
|
||||||
|
* This function and the companion mulInit() are intended for use by other
|
||||||
|
* classes that need access to the raw GF(2^128) field multiplication of
|
||||||
|
* GHASH without the overhead of GHASH itself.
|
||||||
|
*
|
||||||
|
* \sa mulInit(), dbl()
|
||||||
|
*/
|
||||||
|
void GF128::mul(uint32_t Y[4], const uint32_t H[4])
|
||||||
|
{
|
||||||
|
#if defined(__AVR__)
|
||||||
|
uint32_t Z[4] = {0, 0, 0, 0}; // Z = 0
|
||||||
|
uint32_t V0 = H[0]; // V = H
|
||||||
|
uint32_t V1 = H[1];
|
||||||
|
uint32_t V2 = H[2];
|
||||||
|
uint32_t V3 = H[3];
|
||||||
|
|
||||||
|
// Multiply Z by V for the set bits in Y, starting at the top.
|
||||||
|
// This is a very simple bit by bit version that may not be very
|
||||||
|
// fast but it should be resistant to cache timing attacks.
|
||||||
|
for (uint8_t posn = 0; posn < 16; ++posn) {
|
||||||
|
uint8_t value = ((const uint8_t *)Y)[posn];
|
||||||
|
for (uint8_t bit = 0; bit < 8; ++bit) {
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
// Extract the high bit of "value" and turn it into a mask.
|
||||||
|
"ldd r24,%8\n"
|
||||||
|
"lsl r24\n"
|
||||||
|
"std %8,r24\n"
|
||||||
|
"mov __tmp_reg__,__zero_reg__\n"
|
||||||
|
"sbc __tmp_reg__,__zero_reg__\n"
|
||||||
|
|
||||||
|
// XOR V with Z if the bit is 1.
|
||||||
|
"mov r24,%D0\n" // Z0 ^= (V0 & mask)
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%D4\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %D4,r25\n"
|
||||||
|
"mov r24,%C0\n"
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%C4\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %C4,r25\n"
|
||||||
|
"mov r24,%B0\n"
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%B4\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %B4,r25\n"
|
||||||
|
"mov r24,%A0\n"
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%A4\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %A4,r25\n"
|
||||||
|
"mov r24,%D1\n" // Z1 ^= (V1 & mask)
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%D5\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %D5,r25\n"
|
||||||
|
"mov r24,%C1\n"
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%C5\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %C5,r25\n"
|
||||||
|
"mov r24,%B1\n"
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%B5\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %B5,r25\n"
|
||||||
|
"mov r24,%A1\n"
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%A5\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %A5,r25\n"
|
||||||
|
"mov r24,%D2\n" // Z2 ^= (V2 & mask)
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%D6\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %D6,r25\n"
|
||||||
|
"mov r24,%C2\n"
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%C6\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %C6,r25\n"
|
||||||
|
"mov r24,%B2\n"
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%B6\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %B6,r25\n"
|
||||||
|
"mov r24,%A2\n"
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%A6\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %A6,r25\n"
|
||||||
|
"mov r24,%D3\n" // Z3 ^= (V3 & mask)
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%D7\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %D7,r25\n"
|
||||||
|
"mov r24,%C3\n"
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%C7\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %C7,r25\n"
|
||||||
|
"mov r24,%B3\n"
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%B7\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %B7,r25\n"
|
||||||
|
"mov r24,%A3\n"
|
||||||
|
"and r24,__tmp_reg__\n"
|
||||||
|
"ldd r25,%A7\n"
|
||||||
|
"eor r25,r24\n"
|
||||||
|
"std %A7,r25\n"
|
||||||
|
|
||||||
|
// Rotate V right by 1 bit.
|
||||||
|
"lsr %A0\n"
|
||||||
|
"ror %B0\n"
|
||||||
|
"ror %C0\n"
|
||||||
|
"ror %D0\n"
|
||||||
|
"ror %A1\n"
|
||||||
|
"ror %B1\n"
|
||||||
|
"ror %C1\n"
|
||||||
|
"ror %D1\n"
|
||||||
|
"ror %A2\n"
|
||||||
|
"ror %B2\n"
|
||||||
|
"ror %C2\n"
|
||||||
|
"ror %D2\n"
|
||||||
|
"ror %A3\n"
|
||||||
|
"ror %B3\n"
|
||||||
|
"ror %C3\n"
|
||||||
|
"ror %D3\n"
|
||||||
|
"mov r24,__zero_reg__\n"
|
||||||
|
"sbc r24,__zero_reg__\n"
|
||||||
|
"andi r24,0xE1\n"
|
||||||
|
"eor %A0,r24\n"
|
||||||
|
: "+r"(V0), "+r"(V1), "+r"(V2), "+r"(V3)
|
||||||
|
: "Q"(Z[0]), "Q"(Z[1]), "Q"(Z[2]), "Q"(Z[3]), "Q"(value)
|
||||||
|
: "r24", "r25"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have finished the block so copy Z into Y and byte-swap.
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
"ldd __tmp_reg__,%A0\n"
|
||||||
|
"st X+,__tmp_reg__\n"
|
||||||
|
"ldd __tmp_reg__,%B0\n"
|
||||||
|
"st X+,__tmp_reg__\n"
|
||||||
|
"ldd __tmp_reg__,%C0\n"
|
||||||
|
"st X+,__tmp_reg__\n"
|
||||||
|
"ldd __tmp_reg__,%D0\n"
|
||||||
|
"st X+,__tmp_reg__\n"
|
||||||
|
"ldd __tmp_reg__,%A1\n"
|
||||||
|
"st X+,__tmp_reg__\n"
|
||||||
|
"ldd __tmp_reg__,%B1\n"
|
||||||
|
"st X+,__tmp_reg__\n"
|
||||||
|
"ldd __tmp_reg__,%C1\n"
|
||||||
|
"st X+,__tmp_reg__\n"
|
||||||
|
"ldd __tmp_reg__,%D1\n"
|
||||||
|
"st X+,__tmp_reg__\n"
|
||||||
|
"ldd __tmp_reg__,%A2\n"
|
||||||
|
"st X+,__tmp_reg__\n"
|
||||||
|
"ldd __tmp_reg__,%B2\n"
|
||||||
|
"st X+,__tmp_reg__\n"
|
||||||
|
"ldd __tmp_reg__,%C2\n"
|
||||||
|
"st X+,__tmp_reg__\n"
|
||||||
|
"ldd __tmp_reg__,%D2\n"
|
||||||
|
"st X+,__tmp_reg__\n"
|
||||||
|
"ldd __tmp_reg__,%A3\n"
|
||||||
|
"st X+,__tmp_reg__\n"
|
||||||
|
"ldd __tmp_reg__,%B3\n"
|
||||||
|
"st X+,__tmp_reg__\n"
|
||||||
|
"ldd __tmp_reg__,%C3\n"
|
||||||
|
"st X+,__tmp_reg__\n"
|
||||||
|
"ldd __tmp_reg__,%D3\n"
|
||||||
|
"st X,__tmp_reg__\n"
|
||||||
|
: : "Q"(Z[0]), "Q"(Z[1]), "Q"(Z[2]), "Q"(Z[3]), "x"(Y)
|
||||||
|
);
|
||||||
|
#else // !__AVR__
|
||||||
|
uint32_t Z0 = 0; // Z = 0
|
||||||
|
uint32_t Z1 = 0;
|
||||||
|
uint32_t Z2 = 0;
|
||||||
|
uint32_t Z3 = 0;
|
||||||
|
uint32_t V0 = H[0]; // V = H
|
||||||
|
uint32_t V1 = H[1];
|
||||||
|
uint32_t V2 = H[2];
|
||||||
|
uint32_t V3 = H[3];
|
||||||
|
|
||||||
|
// Multiply Z by V for the set bits in Y, starting at the top.
|
||||||
|
// This is a very simple bit by bit version that may not be very
|
||||||
|
// fast but it should be resistant to cache timing attacks.
|
||||||
|
for (uint8_t posn = 0; posn < 16; ++posn) {
|
||||||
|
uint8_t value = ((const uint8_t *)Y)[posn];
|
||||||
|
for (uint8_t bit = 0; bit < 8; ++bit, value <<= 1) {
|
||||||
|
// Extract the high bit of "value" and turn it into a mask.
|
||||||
|
uint32_t mask = (~((uint32_t)(value >> 7))) + 1;
|
||||||
|
|
||||||
|
// XOR V with Z if the bit is 1.
|
||||||
|
Z0 ^= (V0 & mask);
|
||||||
|
Z1 ^= (V1 & mask);
|
||||||
|
Z2 ^= (V2 & mask);
|
||||||
|
Z3 ^= (V3 & mask);
|
||||||
|
|
||||||
|
// Rotate V right by 1 bit.
|
||||||
|
mask = ((~(V3 & 0x01)) + 1) & 0xE1000000;
|
||||||
|
V3 = (V3 >> 1) | (V2 << 31);
|
||||||
|
V2 = (V2 >> 1) | (V1 << 31);
|
||||||
|
V1 = (V1 >> 1) | (V0 << 31);
|
||||||
|
V0 = (V0 >> 1) ^ mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have finished the block so copy Z into Y and byte-swap.
|
||||||
|
Y[0] = htobe32(Z0);
|
||||||
|
Y[1] = htobe32(Z1);
|
||||||
|
Y[2] = htobe32(Z2);
|
||||||
|
Y[3] = htobe32(Z3);
|
||||||
|
#endif // !__AVR__
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Doubles a value in the GF(2^128) field.
|
||||||
|
*
|
||||||
|
* \param V The value to double, and the result. This array is
|
||||||
|
* assumed to be in big-endian order on entry and exit.
|
||||||
|
*
|
||||||
|
* Block cipher modes such as <a href="https://en.wikipedia.org/wiki/Disk_encryption_theory#Xor-encrypt-xor_.28XEX.29">XEX</a>
|
||||||
|
* are similar to CTR mode but instead of incrementing the nonce every
|
||||||
|
* block, the modes multiply the nonce by 2 in the GF(2^128) field every
|
||||||
|
* block. This function is provided to help with implementing such modes.
|
||||||
|
*
|
||||||
|
* \sa dblEAX(), mul()
|
||||||
|
*/
|
||||||
|
void GF128::dbl(uint32_t V[4])
|
||||||
|
{
|
||||||
|
#if defined(__AVR__)
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
"ld r16,Z\n"
|
||||||
|
"ldd r17,Z+1\n"
|
||||||
|
"ldd r18,Z+2\n"
|
||||||
|
"ldd r19,Z+3\n"
|
||||||
|
"lsr r16\n"
|
||||||
|
"ror r17\n"
|
||||||
|
"ror r18\n"
|
||||||
|
"ror r19\n"
|
||||||
|
"std Z+1,r17\n"
|
||||||
|
"std Z+2,r18\n"
|
||||||
|
"std Z+3,r19\n"
|
||||||
|
"ldd r17,Z+4\n"
|
||||||
|
"ldd r18,Z+5\n"
|
||||||
|
"ldd r19,Z+6\n"
|
||||||
|
"ldd r20,Z+7\n"
|
||||||
|
"ror r17\n"
|
||||||
|
"ror r18\n"
|
||||||
|
"ror r19\n"
|
||||||
|
"ror r20\n"
|
||||||
|
"std Z+4,r17\n"
|
||||||
|
"std Z+5,r18\n"
|
||||||
|
"std Z+6,r19\n"
|
||||||
|
"std Z+7,r20\n"
|
||||||
|
"ldd r17,Z+8\n"
|
||||||
|
"ldd r18,Z+9\n"
|
||||||
|
"ldd r19,Z+10\n"
|
||||||
|
"ldd r20,Z+11\n"
|
||||||
|
"ror r17\n"
|
||||||
|
"ror r18\n"
|
||||||
|
"ror r19\n"
|
||||||
|
"ror r20\n"
|
||||||
|
"std Z+8,r17\n"
|
||||||
|
"std Z+9,r18\n"
|
||||||
|
"std Z+10,r19\n"
|
||||||
|
"std Z+11,r20\n"
|
||||||
|
"ldd r17,Z+12\n"
|
||||||
|
"ldd r18,Z+13\n"
|
||||||
|
"ldd r19,Z+14\n"
|
||||||
|
"ldd r20,Z+15\n"
|
||||||
|
"ror r17\n"
|
||||||
|
"ror r18\n"
|
||||||
|
"ror r19\n"
|
||||||
|
"ror r20\n"
|
||||||
|
"std Z+12,r17\n"
|
||||||
|
"std Z+13,r18\n"
|
||||||
|
"std Z+14,r19\n"
|
||||||
|
"std Z+15,r20\n"
|
||||||
|
"mov r17,__zero_reg__\n"
|
||||||
|
"sbc r17,__zero_reg__\n"
|
||||||
|
"andi r17,0xE1\n"
|
||||||
|
"eor r16,r17\n"
|
||||||
|
"st Z,r16\n"
|
||||||
|
: : "z"(V)
|
||||||
|
: "r16", "r17", "r18", "r19", "r20"
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
uint32_t V0 = be32toh(V[0]);
|
||||||
|
uint32_t V1 = be32toh(V[1]);
|
||||||
|
uint32_t V2 = be32toh(V[2]);
|
||||||
|
uint32_t V3 = be32toh(V[3]);
|
||||||
|
uint32_t mask = ((~(V3 & 0x01)) + 1) & 0xE1000000;
|
||||||
|
V3 = (V3 >> 1) | (V2 << 31);
|
||||||
|
V2 = (V2 >> 1) | (V1 << 31);
|
||||||
|
V1 = (V1 >> 1) | (V0 << 31);
|
||||||
|
V0 = (V0 >> 1) ^ mask;
|
||||||
|
V[0] = htobe32(V0);
|
||||||
|
V[1] = htobe32(V1);
|
||||||
|
V[2] = htobe32(V2);
|
||||||
|
V[3] = htobe32(V3);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Doubles a value in the GF(2^128) field using EAX conventions.
|
||||||
|
*
|
||||||
|
* \param V The value to double, and the result. This array is
|
||||||
|
* assumed to be in big-endian order on entry and exit.
|
||||||
|
*
|
||||||
|
* This function differs from dbl() that it uses the conventions of EAX mode
|
||||||
|
* instead of those of NIST SP 800-38D (GCM). The two operations have
|
||||||
|
* equivalent security but the bits are ordered differently with the
|
||||||
|
* value shifted left instead of right.
|
||||||
|
*
|
||||||
|
* References: https://en.wikipedia.org/wiki/EAX_mode,
|
||||||
|
* http://web.cs.ucdavis.edu/~rogaway/papers/eax.html
|
||||||
|
*
|
||||||
|
* \sa dbl(), mul()
|
||||||
|
*/
|
||||||
|
void GF128::dblEAX(uint32_t V[4])
|
||||||
|
{
|
||||||
|
#if defined(__AVR__)
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
"ldd r16,Z+15\n"
|
||||||
|
"ldd r17,Z+14\n"
|
||||||
|
"ldd r18,Z+13\n"
|
||||||
|
"ldd r19,Z+12\n"
|
||||||
|
"lsl r16\n"
|
||||||
|
"rol r17\n"
|
||||||
|
"rol r18\n"
|
||||||
|
"rol r19\n"
|
||||||
|
"std Z+14,r17\n"
|
||||||
|
"std Z+13,r18\n"
|
||||||
|
"std Z+12,r19\n"
|
||||||
|
"ldd r17,Z+11\n"
|
||||||
|
"ldd r18,Z+10\n"
|
||||||
|
"ldd r19,Z+9\n"
|
||||||
|
"ldd r20,Z+8\n"
|
||||||
|
"rol r17\n"
|
||||||
|
"rol r18\n"
|
||||||
|
"rol r19\n"
|
||||||
|
"rol r20\n"
|
||||||
|
"std Z+11,r17\n"
|
||||||
|
"std Z+10,r18\n"
|
||||||
|
"std Z+9,r19\n"
|
||||||
|
"std Z+8,r20\n"
|
||||||
|
"ldd r17,Z+7\n"
|
||||||
|
"ldd r18,Z+6\n"
|
||||||
|
"ldd r19,Z+5\n"
|
||||||
|
"ldd r20,Z+4\n"
|
||||||
|
"rol r17\n"
|
||||||
|
"rol r18\n"
|
||||||
|
"rol r19\n"
|
||||||
|
"rol r20\n"
|
||||||
|
"std Z+7,r17\n"
|
||||||
|
"std Z+6,r18\n"
|
||||||
|
"std Z+5,r19\n"
|
||||||
|
"std Z+4,r20\n"
|
||||||
|
"ldd r17,Z+3\n"
|
||||||
|
"ldd r18,Z+2\n"
|
||||||
|
"ldd r19,Z+1\n"
|
||||||
|
"ld r20,Z\n"
|
||||||
|
"rol r17\n"
|
||||||
|
"rol r18\n"
|
||||||
|
"rol r19\n"
|
||||||
|
"rol r20\n"
|
||||||
|
"std Z+3,r17\n"
|
||||||
|
"std Z+2,r18\n"
|
||||||
|
"std Z+1,r19\n"
|
||||||
|
"st Z,r20\n"
|
||||||
|
"mov r17,__zero_reg__\n"
|
||||||
|
"sbc r17,__zero_reg__\n"
|
||||||
|
"andi r17,0x87\n"
|
||||||
|
"eor r16,r17\n"
|
||||||
|
"std Z+15,r16\n"
|
||||||
|
: : "z"(V)
|
||||||
|
: "r16", "r17", "r18", "r19", "r20"
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
uint32_t V0 = be32toh(V[0]);
|
||||||
|
uint32_t V1 = be32toh(V[1]);
|
||||||
|
uint32_t V2 = be32toh(V[2]);
|
||||||
|
uint32_t V3 = be32toh(V[3]);
|
||||||
|
uint32_t mask = ((~(V0 >> 31)) + 1) & 0x00000087;
|
||||||
|
V0 = (V0 << 1) | (V1 >> 31);
|
||||||
|
V1 = (V1 << 1) | (V2 >> 31);
|
||||||
|
V2 = (V2 << 1) | (V3 >> 31);
|
||||||
|
V3 = (V3 << 1) ^ mask;
|
||||||
|
V[0] = htobe32(V0);
|
||||||
|
V[1] = htobe32(V1);
|
||||||
|
V[2] = htobe32(V2);
|
||||||
|
V[3] = htobe32(V3);
|
||||||
|
#endif
|
||||||
|
}
|
41
libraries/Crypto/GF128.h
Normal file
41
libraries/Crypto/GF128.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CRYPTO_GF128_h
|
||||||
|
#define CRYPTO_GF128_h
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
class GF128
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
GF128() {}
|
||||||
|
~GF128() {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void mulInit(uint32_t H[4], const void *key);
|
||||||
|
static void mul(uint32_t Y[4], const uint32_t H[4]);
|
||||||
|
static void dbl(uint32_t V[4]);
|
||||||
|
static void dblEAX(uint32_t V[4]);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -21,8 +21,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "GHASH.h"
|
#include "GHASH.h"
|
||||||
|
#include "GF128.h"
|
||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
#include "utility/EndianUtil.h"
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,16 +66,7 @@ GHASH::~GHASH()
|
|||||||
*/
|
*/
|
||||||
void GHASH::reset(const void *key)
|
void GHASH::reset(const void *key)
|
||||||
{
|
{
|
||||||
// Copy the key into H and convert from big endian to host order.
|
GF128::mulInit(state.H, key);
|
||||||
memcpy(state.H, key, 16);
|
|
||||||
#if defined(CRYPTO_LITTLE_ENDIAN)
|
|
||||||
state.H[0] = be32toh(state.H[0]);
|
|
||||||
state.H[1] = be32toh(state.H[1]);
|
|
||||||
state.H[2] = be32toh(state.H[2]);
|
|
||||||
state.H[3] = be32toh(state.H[3]);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Reset the hash.
|
|
||||||
memset(state.Y, 0, sizeof(state.Y));
|
memset(state.Y, 0, sizeof(state.Y));
|
||||||
state.posn = 0;
|
state.posn = 0;
|
||||||
}
|
}
|
||||||
@ -106,7 +97,7 @@ void GHASH::update(const void *data, size_t len)
|
|||||||
len -= size;
|
len -= size;
|
||||||
d += size;
|
d += size;
|
||||||
if (state.posn == 16) {
|
if (state.posn == 16) {
|
||||||
processChunk();
|
GF128::mul(state.Y, state.H);
|
||||||
state.posn = 0;
|
state.posn = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,7 +139,7 @@ void GHASH::pad()
|
|||||||
if (state.posn != 0) {
|
if (state.posn != 0) {
|
||||||
// Padding involves XOR'ing the rest of state.Y with zeroes,
|
// Padding involves XOR'ing the rest of state.Y with zeroes,
|
||||||
// which does nothing. Immediately process the next chunk.
|
// which does nothing. Immediately process the next chunk.
|
||||||
processChunk();
|
GF128::mul(state.Y, state.H);
|
||||||
state.posn = 0;
|
state.posn = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,45 +151,3 @@ void GHASH::clear()
|
|||||||
{
|
{
|
||||||
clean(state);
|
clean(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GHASH::processChunk()
|
|
||||||
{
|
|
||||||
uint32_t Z0 = 0; // Z = 0
|
|
||||||
uint32_t Z1 = 0;
|
|
||||||
uint32_t Z2 = 0;
|
|
||||||
uint32_t Z3 = 0;
|
|
||||||
uint32_t V0 = state.H[0]; // V = H
|
|
||||||
uint32_t V1 = state.H[1];
|
|
||||||
uint32_t V2 = state.H[2];
|
|
||||||
uint32_t V3 = state.H[3];
|
|
||||||
|
|
||||||
// Multiply Z by V for the set bits in Y, starting at the top.
|
|
||||||
// This is a very simple bit by bit version that may not be very
|
|
||||||
// fast but it should be resistant to cache timing attacks.
|
|
||||||
for (uint8_t posn = 0; posn < 16; ++posn) {
|
|
||||||
uint8_t value = ((const uint8_t *)state.Y)[posn];
|
|
||||||
for (uint8_t bit = 0; bit < 8; ++bit, value <<= 1) {
|
|
||||||
// Extract the high bit of "value" and turn it into a mask.
|
|
||||||
uint32_t mask = (~((uint32_t)(value >> 7))) + 1;
|
|
||||||
|
|
||||||
// XOR V with Z if the bit is 1.
|
|
||||||
Z0 ^= (V0 & mask);
|
|
||||||
Z1 ^= (V1 & mask);
|
|
||||||
Z2 ^= (V2 & mask);
|
|
||||||
Z3 ^= (V3 & mask);
|
|
||||||
|
|
||||||
// Rotate V right by 1 bit.
|
|
||||||
mask = ((~(V3 & 0x01)) + 1) & 0xE1000000;
|
|
||||||
V3 = (V3 >> 1) | (V2 << 31);
|
|
||||||
V2 = (V2 >> 1) | (V1 << 31);
|
|
||||||
V1 = (V1 >> 1) | (V0 << 31);
|
|
||||||
V0 = (V0 >> 1) ^ mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have finished the block so copy Z into Y and byte-swap.
|
|
||||||
state.Y[0] = htobe32(Z0);
|
|
||||||
state.Y[1] = htobe32(Z1);
|
|
||||||
state.Y[2] = htobe32(Z2);
|
|
||||||
state.Y[3] = htobe32(Z3);
|
|
||||||
}
|
|
||||||
|
@ -46,8 +46,6 @@ private:
|
|||||||
uint32_t Y[4];
|
uint32_t Y[4];
|
||||||
uint8_t posn;
|
uint8_t posn;
|
||||||
} state;
|
} state;
|
||||||
|
|
||||||
void processChunk();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -233,6 +233,7 @@ static TestVector const testVectorEAX10 PROGMEM = {
|
|||||||
TestVector testVector;
|
TestVector testVector;
|
||||||
|
|
||||||
EAX<AES128> *eax;
|
EAX<AES128> *eax;
|
||||||
|
EAX<AES256> *eax256;
|
||||||
EAX<Speck> *eaxSpeck;
|
EAX<Speck> *eaxSpeck;
|
||||||
EAX<SpeckLowMemory> *eaxSpeckLowMemory;
|
EAX<SpeckLowMemory> *eaxSpeckLowMemory;
|
||||||
|
|
||||||
@ -353,7 +354,7 @@ void perfCipherSetKey(AuthenticatedCipher *cipher, const struct TestVector *test
|
|||||||
|
|
||||||
start = micros();
|
start = micros();
|
||||||
for (count = 0; count < 1000; ++count) {
|
for (count = 0; count < 1000; ++count) {
|
||||||
cipher->setKey(test->key, 16);
|
cipher->setKey(test->key, cipher->keySize());
|
||||||
cipher->setIV(test->iv, test->ivsize);
|
cipher->setIV(test->iv, test->ivsize);
|
||||||
}
|
}
|
||||||
elapsed = micros() - start;
|
elapsed = micros() - start;
|
||||||
@ -378,7 +379,7 @@ void perfCipherEncrypt(AuthenticatedCipher *cipher, const struct TestVector *tes
|
|||||||
Serial.print(test->name);
|
Serial.print(test->name);
|
||||||
Serial.print(" Encrypt ... ");
|
Serial.print(" Encrypt ... ");
|
||||||
|
|
||||||
cipher->setKey(test->key, 16);
|
cipher->setKey(test->key, cipher->keySize());
|
||||||
cipher->setIV(test->iv, test->ivsize);
|
cipher->setIV(test->iv, test->ivsize);
|
||||||
start = micros();
|
start = micros();
|
||||||
for (count = 0; count < 500; ++count) {
|
for (count = 0; count < 500; ++count) {
|
||||||
@ -406,7 +407,7 @@ void perfCipherDecrypt(AuthenticatedCipher *cipher, const struct TestVector *tes
|
|||||||
Serial.print(test->name);
|
Serial.print(test->name);
|
||||||
Serial.print(" Decrypt ... ");
|
Serial.print(" Decrypt ... ");
|
||||||
|
|
||||||
cipher->setKey(test->key, 16);
|
cipher->setKey(test->key, cipher->keySize());
|
||||||
cipher->setIV(test->iv, test->ivsize);
|
cipher->setIV(test->iv, test->ivsize);
|
||||||
start = micros();
|
start = micros();
|
||||||
for (count = 0; count < 500; ++count) {
|
for (count = 0; count < 500; ++count) {
|
||||||
@ -434,7 +435,7 @@ void perfCipherAddAuthData(AuthenticatedCipher *cipher, const struct TestVector
|
|||||||
Serial.print(test->name);
|
Serial.print(test->name);
|
||||||
Serial.print(" AddAuthData ... ");
|
Serial.print(" AddAuthData ... ");
|
||||||
|
|
||||||
cipher->setKey(test->key, 16);
|
cipher->setKey(test->key, cipher->keySize());
|
||||||
cipher->setIV(test->iv, test->ivsize);
|
cipher->setIV(test->iv, test->ivsize);
|
||||||
start = micros();
|
start = micros();
|
||||||
memset(buffer, 0xBA, 128);
|
memset(buffer, 0xBA, 128);
|
||||||
@ -463,7 +464,7 @@ void perfCipherComputeTag(AuthenticatedCipher *cipher, const struct TestVector *
|
|||||||
Serial.print(test->name);
|
Serial.print(test->name);
|
||||||
Serial.print(" ComputeTag ... ");
|
Serial.print(" ComputeTag ... ");
|
||||||
|
|
||||||
cipher->setKey(test->key, 16);
|
cipher->setKey(test->key, cipher->keySize());
|
||||||
cipher->setIV(test->iv, test->ivsize);
|
cipher->setIV(test->iv, test->ivsize);
|
||||||
start = micros();
|
start = micros();
|
||||||
for (count = 0; count < 1000; ++count) {
|
for (count = 0; count < 1000; ++count) {
|
||||||
@ -495,6 +496,8 @@ void setup()
|
|||||||
Serial.println("State Sizes:");
|
Serial.println("State Sizes:");
|
||||||
Serial.print("EAX<AES128> ... ");
|
Serial.print("EAX<AES128> ... ");
|
||||||
Serial.println(sizeof(*eax));
|
Serial.println(sizeof(*eax));
|
||||||
|
Serial.print("EAX<AES256> ... ");
|
||||||
|
Serial.println(sizeof(*eax256));
|
||||||
Serial.print("EAX<Speck> ... ");
|
Serial.print("EAX<Speck> ... ");
|
||||||
Serial.println(sizeof(*eaxSpeck));
|
Serial.println(sizeof(*eaxSpeck));
|
||||||
Serial.print("EAX<SpeckLowMemory> ... ");
|
Serial.print("EAX<SpeckLowMemory> ... ");
|
||||||
@ -520,6 +523,10 @@ void setup()
|
|||||||
perfCipher(eax, &testVectorEAX1, "AES-128");
|
perfCipher(eax, &testVectorEAX1, "AES-128");
|
||||||
Serial.println();
|
Serial.println();
|
||||||
delete eax;
|
delete eax;
|
||||||
|
eax256 = new EAX<AES256>();
|
||||||
|
perfCipher(eax, &testVectorEAX1, "AES-256");
|
||||||
|
Serial.println();
|
||||||
|
delete eax256;
|
||||||
eaxSpeck = new EAX<Speck>();
|
eaxSpeck = new EAX<Speck>();
|
||||||
perfCipher(eaxSpeck, &testVectorEAX1, "Speck");
|
perfCipher(eaxSpeck, &testVectorEAX1, "Speck");
|
||||||
Serial.println();
|
Serial.println();
|
||||||
|
@ -26,10 +26,19 @@ This example runs tests on the GCM implementation to verify correct behaviour.
|
|||||||
|
|
||||||
#include <Crypto.h>
|
#include <Crypto.h>
|
||||||
#include <AES.h>
|
#include <AES.h>
|
||||||
|
#include <Speck.h>
|
||||||
|
#include <SpeckLowMemory.h>
|
||||||
#include <GCM.h>
|
#include <GCM.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <avr/pgmspace.h>
|
#include <avr/pgmspace.h>
|
||||||
|
|
||||||
|
// There isn't enough memory to test both AES and Speck on the Uno,
|
||||||
|
// so disable Speck testing on AVR platforms unless explicitly enabled.
|
||||||
|
// When enabled, some of the AES tests are disabled to reclaim memory.
|
||||||
|
#if defined(__AVR__)
|
||||||
|
//#define TEST_SPECK 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MAX_PLAINTEXT_LEN 64
|
#define MAX_PLAINTEXT_LEN 64
|
||||||
|
|
||||||
struct TestVector
|
struct TestVector
|
||||||
@ -65,6 +74,7 @@ static TestVector const testVectorGCM1 PROGMEM = {
|
|||||||
.tagsize = 16,
|
.tagsize = 16,
|
||||||
.ivsize = 12
|
.ivsize = 12
|
||||||
};
|
};
|
||||||
|
#ifndef TEST_SPECK
|
||||||
static TestVector const testVectorGCM2 PROGMEM = {
|
static TestVector const testVectorGCM2 PROGMEM = {
|
||||||
.name = "AES-128 GCM #2",
|
.name = "AES-128 GCM #2",
|
||||||
.key = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
.key = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
@ -176,6 +186,7 @@ static TestVector const testVectorGCM5 PROGMEM = {
|
|||||||
.tagsize = 16,
|
.tagsize = 16,
|
||||||
.ivsize = 8
|
.ivsize = 8
|
||||||
};
|
};
|
||||||
|
#endif // !TEST_SPECK
|
||||||
static TestVector const testVectorGCM10 PROGMEM = {
|
static TestVector const testVectorGCM10 PROGMEM = {
|
||||||
.name = "AES-192 GCM #10",
|
.name = "AES-192 GCM #10",
|
||||||
.key = {0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
|
.key = {0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
|
||||||
@ -249,6 +260,8 @@ TestVector testVector;
|
|||||||
GCM<AES128> *gcmaes128 = 0;
|
GCM<AES128> *gcmaes128 = 0;
|
||||||
GCM<AES192> *gcmaes192 = 0;
|
GCM<AES192> *gcmaes192 = 0;
|
||||||
GCM<AES256> *gcmaes256 = 0;
|
GCM<AES256> *gcmaes256 = 0;
|
||||||
|
GCM<Speck> *gcmspeck = 0;
|
||||||
|
GCM<SpeckLowMemory> *gcmspecklm = 0;
|
||||||
|
|
||||||
byte buffer[128];
|
byte buffer[128];
|
||||||
|
|
||||||
@ -348,7 +361,7 @@ void testCipher(AuthenticatedCipher *cipher, const struct TestVector *test)
|
|||||||
Serial.println("Failed");
|
Serial.println("Failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
void perfCipherSetKey(AuthenticatedCipher *cipher, const struct TestVector *test)
|
void perfCipherSetKey(AuthenticatedCipher *cipher, const struct TestVector *test, const char *name)
|
||||||
{
|
{
|
||||||
unsigned long start;
|
unsigned long start;
|
||||||
unsigned long elapsed;
|
unsigned long elapsed;
|
||||||
@ -357,7 +370,7 @@ void perfCipherSetKey(AuthenticatedCipher *cipher, const struct TestVector *test
|
|||||||
memcpy_P(&testVector, test, sizeof(TestVector));
|
memcpy_P(&testVector, test, sizeof(TestVector));
|
||||||
test = &testVector;
|
test = &testVector;
|
||||||
|
|
||||||
Serial.print(test->name);
|
Serial.print(name);
|
||||||
Serial.print(" SetKey ... ");
|
Serial.print(" SetKey ... ");
|
||||||
|
|
||||||
start = micros();
|
start = micros();
|
||||||
@ -373,7 +386,7 @@ void perfCipherSetKey(AuthenticatedCipher *cipher, const struct TestVector *test
|
|||||||
Serial.println(" per second");
|
Serial.println(" per second");
|
||||||
}
|
}
|
||||||
|
|
||||||
void perfCipherEncrypt(AuthenticatedCipher *cipher, const struct TestVector *test)
|
void perfCipherEncrypt(AuthenticatedCipher *cipher, const struct TestVector *test, const char *name)
|
||||||
{
|
{
|
||||||
unsigned long start;
|
unsigned long start;
|
||||||
unsigned long elapsed;
|
unsigned long elapsed;
|
||||||
@ -382,7 +395,7 @@ void perfCipherEncrypt(AuthenticatedCipher *cipher, const struct TestVector *tes
|
|||||||
memcpy_P(&testVector, test, sizeof(TestVector));
|
memcpy_P(&testVector, test, sizeof(TestVector));
|
||||||
test = &testVector;
|
test = &testVector;
|
||||||
|
|
||||||
Serial.print(test->name);
|
Serial.print(name);
|
||||||
Serial.print(" Encrypt ... ");
|
Serial.print(" Encrypt ... ");
|
||||||
|
|
||||||
cipher->setKey(test->key, cipher->keySize());
|
cipher->setKey(test->key, cipher->keySize());
|
||||||
@ -399,7 +412,7 @@ void perfCipherEncrypt(AuthenticatedCipher *cipher, const struct TestVector *tes
|
|||||||
Serial.println(" bytes per second");
|
Serial.println(" bytes per second");
|
||||||
}
|
}
|
||||||
|
|
||||||
void perfCipherDecrypt(AuthenticatedCipher *cipher, const struct TestVector *test)
|
void perfCipherDecrypt(AuthenticatedCipher *cipher, const struct TestVector *test, const char *name)
|
||||||
{
|
{
|
||||||
unsigned long start;
|
unsigned long start;
|
||||||
unsigned long elapsed;
|
unsigned long elapsed;
|
||||||
@ -408,7 +421,7 @@ void perfCipherDecrypt(AuthenticatedCipher *cipher, const struct TestVector *tes
|
|||||||
memcpy_P(&testVector, test, sizeof(TestVector));
|
memcpy_P(&testVector, test, sizeof(TestVector));
|
||||||
test = &testVector;
|
test = &testVector;
|
||||||
|
|
||||||
Serial.print(test->name);
|
Serial.print(name);
|
||||||
Serial.print(" Decrypt ... ");
|
Serial.print(" Decrypt ... ");
|
||||||
|
|
||||||
cipher->setKey(test->key, cipher->keySize());
|
cipher->setKey(test->key, cipher->keySize());
|
||||||
@ -425,7 +438,7 @@ void perfCipherDecrypt(AuthenticatedCipher *cipher, const struct TestVector *tes
|
|||||||
Serial.println(" bytes per second");
|
Serial.println(" bytes per second");
|
||||||
}
|
}
|
||||||
|
|
||||||
void perfCipherAddAuthData(AuthenticatedCipher *cipher, const struct TestVector *test)
|
void perfCipherAddAuthData(AuthenticatedCipher *cipher, const struct TestVector *test, const char *name)
|
||||||
{
|
{
|
||||||
unsigned long start;
|
unsigned long start;
|
||||||
unsigned long elapsed;
|
unsigned long elapsed;
|
||||||
@ -434,7 +447,7 @@ void perfCipherAddAuthData(AuthenticatedCipher *cipher, const struct TestVector
|
|||||||
memcpy_P(&testVector, test, sizeof(TestVector));
|
memcpy_P(&testVector, test, sizeof(TestVector));
|
||||||
test = &testVector;
|
test = &testVector;
|
||||||
|
|
||||||
Serial.print(test->name);
|
Serial.print(name);
|
||||||
Serial.print(" AddAuthData ... ");
|
Serial.print(" AddAuthData ... ");
|
||||||
|
|
||||||
cipher->setKey(test->key, cipher->keySize());
|
cipher->setKey(test->key, cipher->keySize());
|
||||||
@ -452,7 +465,7 @@ void perfCipherAddAuthData(AuthenticatedCipher *cipher, const struct TestVector
|
|||||||
Serial.println(" bytes per second");
|
Serial.println(" bytes per second");
|
||||||
}
|
}
|
||||||
|
|
||||||
void perfCipherComputeTag(AuthenticatedCipher *cipher, const struct TestVector *test)
|
void perfCipherComputeTag(AuthenticatedCipher *cipher, const struct TestVector *test, const char *name)
|
||||||
{
|
{
|
||||||
unsigned long start;
|
unsigned long start;
|
||||||
unsigned long elapsed;
|
unsigned long elapsed;
|
||||||
@ -461,7 +474,7 @@ void perfCipherComputeTag(AuthenticatedCipher *cipher, const struct TestVector *
|
|||||||
memcpy_P(&testVector, test, sizeof(TestVector));
|
memcpy_P(&testVector, test, sizeof(TestVector));
|
||||||
test = &testVector;
|
test = &testVector;
|
||||||
|
|
||||||
Serial.print(test->name);
|
Serial.print(name);
|
||||||
Serial.print(" ComputeTag ... ");
|
Serial.print(" ComputeTag ... ");
|
||||||
|
|
||||||
cipher->setKey(test->key, cipher->keySize());
|
cipher->setKey(test->key, cipher->keySize());
|
||||||
@ -478,13 +491,13 @@ void perfCipherComputeTag(AuthenticatedCipher *cipher, const struct TestVector *
|
|||||||
Serial.println(" per second");
|
Serial.println(" per second");
|
||||||
}
|
}
|
||||||
|
|
||||||
void perfCipher(AuthenticatedCipher *cipher, const struct TestVector *test)
|
void perfCipher(AuthenticatedCipher *cipher, const struct TestVector *test, const char *name)
|
||||||
{
|
{
|
||||||
perfCipherSetKey(cipher, test);
|
perfCipherSetKey(cipher, test, name);
|
||||||
perfCipherEncrypt(cipher, test);
|
perfCipherEncrypt(cipher, test, name);
|
||||||
perfCipherDecrypt(cipher, test);
|
perfCipherDecrypt(cipher, test, name);
|
||||||
perfCipherAddAuthData(cipher, test);
|
perfCipherAddAuthData(cipher, test, name);
|
||||||
perfCipherComputeTag(cipher, test);
|
perfCipherComputeTag(cipher, test, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
@ -493,6 +506,7 @@ void setup()
|
|||||||
|
|
||||||
Serial.println();
|
Serial.println();
|
||||||
|
|
||||||
|
#ifndef TEST_SPECK
|
||||||
Serial.println("State Sizes:");
|
Serial.println("State Sizes:");
|
||||||
Serial.print("GCM<AES128> ... ");
|
Serial.print("GCM<AES128> ... ");
|
||||||
Serial.println(sizeof(*gcmaes128));
|
Serial.println(sizeof(*gcmaes128));
|
||||||
@ -500,15 +514,22 @@ void setup()
|
|||||||
Serial.println(sizeof(*gcmaes192));
|
Serial.println(sizeof(*gcmaes192));
|
||||||
Serial.print("GCM<AES256> ... ");
|
Serial.print("GCM<AES256> ... ");
|
||||||
Serial.println(sizeof(*gcmaes256));
|
Serial.println(sizeof(*gcmaes256));
|
||||||
|
Serial.print("GCM<Speck> ... ");
|
||||||
|
Serial.println(sizeof(*gcmspeck));
|
||||||
|
Serial.print("GCM<SpeckLowMemory> ... ");
|
||||||
|
Serial.println(sizeof(*gcmspecklm));
|
||||||
Serial.println();
|
Serial.println();
|
||||||
|
#endif
|
||||||
|
|
||||||
Serial.println("Test Vectors:");
|
Serial.println("Test Vectors:");
|
||||||
gcmaes128 = new GCM<AES128>();
|
gcmaes128 = new GCM<AES128>();
|
||||||
testCipher(gcmaes128, &testVectorGCM1);
|
testCipher(gcmaes128, &testVectorGCM1);
|
||||||
|
#ifndef TEST_SPECK
|
||||||
testCipher(gcmaes128, &testVectorGCM2);
|
testCipher(gcmaes128, &testVectorGCM2);
|
||||||
testCipher(gcmaes128, &testVectorGCM3);
|
testCipher(gcmaes128, &testVectorGCM3);
|
||||||
testCipher(gcmaes128, &testVectorGCM4);
|
testCipher(gcmaes128, &testVectorGCM4);
|
||||||
testCipher(gcmaes128, &testVectorGCM5);
|
testCipher(gcmaes128, &testVectorGCM5);
|
||||||
|
#endif
|
||||||
delete gcmaes128;
|
delete gcmaes128;
|
||||||
gcmaes192 = new GCM<AES192>();
|
gcmaes192 = new GCM<AES192>();
|
||||||
testCipher(gcmaes192, &testVectorGCM10);
|
testCipher(gcmaes192, &testVectorGCM10);
|
||||||
@ -520,15 +541,25 @@ void setup()
|
|||||||
Serial.println();
|
Serial.println();
|
||||||
|
|
||||||
Serial.println("Performance Tests:");
|
Serial.println("Performance Tests:");
|
||||||
|
#ifndef TEST_SPECK
|
||||||
gcmaes128 = new GCM<AES128>();
|
gcmaes128 = new GCM<AES128>();
|
||||||
perfCipher(gcmaes128, &testVectorGCM1);
|
perfCipher(gcmaes128, &testVectorGCM1, testVectorGCM1.name);
|
||||||
delete gcmaes128;
|
delete gcmaes128;
|
||||||
gcmaes192 = new GCM<AES192>();
|
gcmaes192 = new GCM<AES192>();
|
||||||
perfCipher(gcmaes192, &testVectorGCM10);
|
perfCipher(gcmaes192, &testVectorGCM10, testVectorGCM10.name);
|
||||||
delete gcmaes192;
|
delete gcmaes192;
|
||||||
gcmaes256 = new GCM<AES256>();
|
gcmaes256 = new GCM<AES256>();
|
||||||
perfCipher(gcmaes256, &testVectorGCM16);
|
perfCipher(gcmaes256, &testVectorGCM16, testVectorGCM16.name);
|
||||||
delete gcmaes256;
|
delete gcmaes256;
|
||||||
|
#endif
|
||||||
|
#if defined(TEST_SPECK) || !defined(__AVR__)
|
||||||
|
gcmspeck = new GCM<Speck>();
|
||||||
|
perfCipher(gcmspeck, &testVectorGCM16, "GCM-Speck-256");
|
||||||
|
delete gcmspeck;
|
||||||
|
gcmspecklm = new GCM<SpeckLowMemory>();
|
||||||
|
perfCipher(gcmspecklm, &testVectorGCM16, "GCM-SpeckLowMemory-256");
|
||||||
|
delete gcmspecklm;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user