1
0
mirror of https://github.com/taigrr/arduinolibs synced 2025-01-18 04:33:12 -08:00

Implementation of the NIST P-521 curve

This commit is contained in:
Rhys Weatherley 2016-03-26 19:03:31 +10:00
parent c8d7c3153d
commit 9ff24b0ddf
10 changed files with 2928 additions and 5 deletions

View File

@ -33,7 +33,7 @@
\li Hash algorithms: SHA256, SHA512, SHA3_256, SHA3_512, BLAKE2s, BLAKE2b (regular and HMAC modes) \li Hash algorithms: SHA256, SHA512, SHA3_256, SHA3_512, BLAKE2s, BLAKE2b (regular and HMAC modes)
\li Extendable output functions (XOF's): SHAKE128, SHAKE256 \li Extendable output functions (XOF's): SHAKE128, SHAKE256
\li Message authenticators: Poly1305, GHASH, OMAC \li Message authenticators: Poly1305, GHASH, OMAC
\li Public key algorithms: Curve25519, Ed25519 \li Public key algorithms: Curve25519, Ed25519, P521
\li Random number generation: \link RNGClass RNG\endlink, TransistorNoiseSource, RingOscillatorNoiseSource \li Random number generation: \link RNGClass RNG\endlink, TransistorNoiseSource, RingOscillatorNoiseSource
All cryptographic algorithms have been optimized for 8-bit Arduino platforms All cryptographic algorithms have been optimized for 8-bit Arduino platforms
@ -129,6 +129,12 @@ Ardunino Mega 2560 running at 16 MHz are similar:
<tr><td>Ed25519::sign()</td><td align="right">5148ms</td><td colspan="3">Digital signature generation</td></tr> <tr><td>Ed25519::sign()</td><td align="right">5148ms</td><td colspan="3">Digital signature generation</td></tr>
<tr><td>Ed25519::verify()</td><td align="right">8196ms</td><td colspan="3">Digital signature verification</td></tr> <tr><td>Ed25519::verify()</td><td align="right">8196ms</td><td colspan="3">Digital signature verification</td></tr>
<tr><td>Ed25519::derivePublicKey()</td><td align="right">5102ms</td><td colspan="3">Derive a public key from a private key</td></tr> <tr><td>Ed25519::derivePublicKey()</td><td align="right">5102ms</td><td colspan="3">Derive a public key from a private key</td></tr>
<tr><td>P521::eval()</td><td align="right">46290ms</td><td colspan="3">Raw curve evaluation</td></tr>
<tr><td>P521::dh1()</td><td align="right">46293ms</td><td colspan="3">First half of Diffie-Hellman key agreement</td></tr>
<tr><td>P521::dh2()</td><td align="right">46304ms</td><td colspan="3">Second half of Diffie-Hellman key agreement</td></tr>
<tr><td>P521::sign()</td><td align="right">60514ms</td><td colspan="3">Digital signature generation</td></tr>
<tr><td>P521::verify()</td><td align="right">109078ms</td><td colspan="3">Digital signature verification</td></tr>
<tr><td>P521::derivePublicKey()</td><td align="right">46290ms</td><td colspan="3">Derive a public key from a private key</td></tr>
</table> </table>
Where a cipher supports more than one key size (such as ChaCha), the values Where a cipher supports more than one key size (such as ChaCha), the values
@ -196,5 +202,11 @@ All figures are for the Arduino Due running at 84 MHz:
<tr><td>Ed25519::sign()</td><td align="right">195ms</td><td colspan="3">Digital signature generation</td></tr> <tr><td>Ed25519::sign()</td><td align="right">195ms</td><td colspan="3">Digital signature generation</td></tr>
<tr><td>Ed25519::verify()</td><td align="right">306ms</td><td colspan="3">Digital signature verification</td></tr> <tr><td>Ed25519::verify()</td><td align="right">306ms</td><td colspan="3">Digital signature verification</td></tr>
<tr><td>Ed25519::derivePublicKey()</td><td align="right">194ms</td><td colspan="3">Derive a public key from a private key</td></tr> <tr><td>Ed25519::derivePublicKey()</td><td align="right">194ms</td><td colspan="3">Derive a public key from a private key</td></tr>
<tr><td>P521::eval()</td><td align="right">1503ms</td><td colspan="3">Raw curve evaluation</td></tr>
<tr><td>P521::dh1()</td><td align="right">1503ms</td><td colspan="3">First half of Diffie-Hellman key agreement</td></tr>
<tr><td>P521::dh2()</td><td align="right">1503ms</td><td colspan="3">Second half of Diffie-Hellman key agreement</td></tr>
<tr><td>P521::sign()</td><td align="right">1860ms</td><td colspan="3">Digital signature generation</td></tr>
<tr><td>P521::verify()</td><td align="right">3423ms</td><td colspan="3">Digital signature verification</td></tr>
<tr><td>P521::derivePublicKey()</td><td align="right">1503ms</td><td colspan="3">Derive a public key from a private key</td></tr>
</table> </table>
*/ */

View File

@ -98,7 +98,7 @@ realtime clock and the LCD library to implement an alarm clock.
\li Hash algorithms: SHA256, SHA512, SHA3_256, SHA3_512, BLAKE2s, BLAKE2b (regular and HMAC modes) \li Hash algorithms: SHA256, SHA512, SHA3_256, SHA3_512, BLAKE2s, BLAKE2b (regular and HMAC modes)
\li Extendable output functions (XOF's): SHAKE128, SHAKE256 \li Extendable output functions (XOF's): SHAKE128, SHAKE256
\li Message authenticators: Poly1305, GHASH, OMAC \li Message authenticators: Poly1305, GHASH, OMAC
\li Public key algorithms: Curve25519, Ed25519 \li Public key algorithms: Curve25519, Ed25519, P521
\li Random number generation: \link RNGClass RNG\endlink, TransistorNoiseSource, RingOscillatorNoiseSource \li Random number generation: \link RNGClass RNG\endlink, TransistorNoiseSource, RingOscillatorNoiseSource
More information can be found on the \ref crypto "Cryptographic Library" page. More information can be found on the \ref crypto "Cryptographic Library" page.

View File

@ -47,6 +47,7 @@ SOURCES = \
NoiseSource.cpp \ NoiseSource.cpp \
OFB.cpp \ OFB.cpp \
OMAC.cpp \ OMAC.cpp \
P521.cpp \
Poly1305.cpp \ Poly1305.cpp \
RNG_host.cpp \ RNG_host.cpp \
SHA256.cpp \ SHA256.cpp \
@ -76,6 +77,8 @@ SKETCHES = \
TestGCM/TestGCM.ino \ TestGCM/TestGCM.ino \
TestGHASH/TestGHASH.ino \ TestGHASH/TestGHASH.ino \
TestOFB/TestOFB.ino \ TestOFB/TestOFB.ino \
TestP521/TestP521.ino \
TestP521Math/TestP521Math.ino \
TestPoly1305/TestPoly1305.ino \ TestPoly1305/TestPoly1305.ino \
TestSHA256/TestSHA256.ino \ TestSHA256/TestSHA256.ino \
TestSHA3_256/TestSHA3_256.ino \ TestSHA3_256/TestSHA3_256.ino \

View File

@ -634,3 +634,22 @@ void BigNumberUtil::reduceQuick_P(limb_t *result, const limb_t *x,
--size; --size;
} }
} }
/**
* \brief Determine if a big number is zero.
*
* \param x Points to the number to test.
* \param size The number of limbs in \a x.
* \return Returns 1 if \a x is zero or 0 otherwise.
*
* This function attempts to make the determination in constant time.
*/
limb_t BigNumberUtil::isZero(const limb_t *x, size_t size)
{
limb_t word = 0;
while (size > 0) {
word |= *x++;
--size;
}
return (limb_t)(((((dlimb_t)1) << LIMB_BITS) - word) >> LIMB_BITS);
}

View File

@ -86,6 +86,8 @@ public:
static void reduceQuick_P(limb_t *result, const limb_t *x, static void reduceQuick_P(limb_t *result, const limb_t *x,
const limb_t *y, size_t size); const limb_t *y, size_t size);
static limb_t isZero(const limb_t *x, size_t size);
private: private:
// Constructor and destructor are private - cannot instantiate this class. // Constructor and destructor are private - cannot instantiate this class.
BigNumberUtil() {} BigNumberUtil() {}

1642
libraries/Crypto/P521.cpp Normal file

File diff suppressed because it is too large Load Diff

112
libraries/Crypto/P521.h Normal file
View File

@ -0,0 +1,112 @@
/*
* 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_P521_h
#define CRYPTO_P521_h
#include "BigNumberUtil.h"
class Hash;
class P521
{
public:
static bool eval(uint8_t result[132], const uint8_t f[66], const uint8_t point[132]);
static void dh1(uint8_t k[132], uint8_t f[66]);
static bool dh2(const uint8_t k[132], uint8_t f[66]);
static void sign(uint8_t signature[132], const uint8_t privateKey[66],
const void *message, size_t len, Hash *hash = 0);
static bool verify(const uint8_t signature[132],
const uint8_t publicKey[132],
const void *message, size_t len, Hash *hash = 0);
static void generatePrivateKey(uint8_t privateKey[66]);
static void derivePublicKey(uint8_t publicKey[132], const uint8_t privateKey[66]);
static bool isValidPrivateKey(const uint8_t privateKey[66]);
static bool isValidPublicKey(const uint8_t publicKey[132]);
static bool isValidCurvePoint(const uint8_t point[132])
{
return isValidPublicKey(point);
}
#if defined(TEST_P521_FIELD_OPS)
public:
#else
private:
#endif
static void evaluate(limb_t *x, limb_t *y, const uint8_t f[66]);
static void addAffine(limb_t *x1, limb_t *y1,
const limb_t *x2, const limb_t *y2);
static bool validate(const limb_t *x, const limb_t *y);
static bool inRange(const limb_t *x);
static void reduce(limb_t *result, const limb_t *x);
static void reduceQuick(limb_t *x);
static void mulNoReduce(limb_t *result, const limb_t *x, const limb_t *y);
static void mul(limb_t *result, const limb_t *x, const limb_t *y);
static void square(limb_t *result, const limb_t *x)
{
mul(result, x, x);
}
static void mulLiteral(limb_t *result, const limb_t *x, limb_t y);
static void add(limb_t *result, const limb_t *x, const limb_t *y);
static void sub(limb_t *result, const limb_t *x, const limb_t *y);
static void dblPoint(limb_t *xout, limb_t *yout, limb_t *zout,
const limb_t *xin, const limb_t *yin,
const limb_t *zin);
static void addPoint(limb_t *xout, limb_t *yout, limb_t *zout,
const limb_t *x1, const limb_t *y1,
const limb_t *z1, const limb_t *x2,
const limb_t *y2);
static void cmove(limb_t select, limb_t *x, const limb_t *y);
static void cmove1(limb_t select, limb_t *x);
static void recip(limb_t *result, const limb_t *x);
static void reduceQ(limb_t *result, const limb_t *r);
static void mulQ(limb_t *result, const limb_t *x, const limb_t *y);
static void recipQ(limb_t *result, const limb_t *x);
static void generateK(uint8_t k[66], const uint8_t hm[66],
const uint8_t x[66], Hash *hash, uint64_t count);
static void generateK(uint8_t k[66], const uint8_t hm[66],
const uint8_t x[66], uint64_t count);
// Constructor and destructor are private - cannot instantiate this class.
P521() {}
~P521() {}
};
#endif

View File

@ -0,0 +1,519 @@
/*
* 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.
*/
/*
This example runs tests on the P521 algorithm.
Note: This example is too large to run on the Arduino Uno or other
AVR platforms with 32K or less of flash memory.
*/
#include <Crypto.h>
#include <P521.h>
#include <SHA256.h>
#include <SHA512.h>
#include <RNG.h>
#include <RNG.h>
#include <string.h>
#include <avr/pgmspace.h>
void printNumber(const char *name, const uint8_t *x, size_t len)
{
static const char hexchars[] = "0123456789ABCDEF";
Serial.print(name);
Serial.print(" = ");
for (size_t posn = 0; posn < len; ++posn) {
Serial.print(hexchars[(x[posn] >> 4) & 0x0F]);
Serial.print(hexchars[x[posn] & 0x0F]);
}
Serial.println();
}
static int P521_memcmp_P(const void *s1, const void *s2, size_t len)
{
const uint8_t *u1 = (const uint8_t *)s1;
const uint8_t *u2 = (const uint8_t *)s2;
while (len > 0) {
int ch1 = *u1++;
int ch2 = pgm_read_byte(u2++);
if (ch1 != ch2)
return ch1 - ch2;
--len;
}
return 0;
}
static uint8_t alice_k[132];
static uint8_t alice_f[66];
static uint8_t bob_k[132];
static uint8_t bob_f[66];
// Check the eval() function using the test vectors from RFC 5903.
void testEval()
{
static uint8_t const alice_private[66] PROGMEM = {
0x00, 0x37, 0xAD, 0xE9, 0x31, 0x9A, 0x89, 0xF4,
0xDA, 0xBD, 0xB3, 0xEF, 0x41, 0x1A, 0xAC, 0xCC,
0xA5, 0x12, 0x3C, 0x61, 0xAC, 0xAB, 0x57, 0xB5,
0x39, 0x3D, 0xCE, 0x47, 0x60, 0x81, 0x72, 0xA0,
0x95, 0xAA, 0x85, 0xA3, 0x0F, 0xE1, 0xC2, 0x95,
0x2C, 0x67, 0x71, 0xD9, 0x37, 0xBA, 0x97, 0x77,
0xF5, 0x95, 0x7B, 0x26, 0x39, 0xBA, 0xB0, 0x72,
0x46, 0x2F, 0x68, 0xC2, 0x7A, 0x57, 0x38, 0x2D,
0x4A, 0x52
};
static uint8_t const alice_public[132] PROGMEM = {
0x00, 0x15, 0x41, 0x7E, 0x84, 0xDB, 0xF2, 0x8C,
0x0A, 0xD3, 0xC2, 0x78, 0x71, 0x33, 0x49, 0xDC,
0x7D, 0xF1, 0x53, 0xC8, 0x97, 0xA1, 0x89, 0x1B,
0xD9, 0x8B, 0xAB, 0x43, 0x57, 0xC9, 0xEC, 0xBE,
0xE1, 0xE3, 0xBF, 0x42, 0xE0, 0x0B, 0x8E, 0x38,
0x0A, 0xEA, 0xE5, 0x7C, 0x2D, 0x10, 0x75, 0x64,
0x94, 0x18, 0x85, 0x94, 0x2A, 0xF5, 0xA7, 0xF4,
0x60, 0x17, 0x23, 0xC4, 0x19, 0x5D, 0x17, 0x6C,
0xED, 0x3E, 0x01, 0x7C, 0xAE, 0x20, 0xB6, 0x64,
0x1D, 0x2E, 0xEB, 0x69, 0x57, 0x86, 0xD8, 0xC9,
0x46, 0x14, 0x62, 0x39, 0xD0, 0x99, 0xE1, 0x8E,
0x1D, 0x5A, 0x51, 0x4C, 0x73, 0x9D, 0x7C, 0xB4,
0xA1, 0x0A, 0xD8, 0xA7, 0x88, 0x01, 0x5A, 0xC4,
0x05, 0xD7, 0x79, 0x9D, 0xC7, 0x5E, 0x7B, 0x7D,
0x5B, 0x6C, 0xF2, 0x26, 0x1A, 0x6A, 0x7F, 0x15,
0x07, 0x43, 0x8B, 0xF0, 0x1B, 0xEB, 0x6C, 0xA3,
0x92, 0x6F, 0x95, 0x82
};
static uint8_t const bob_private[66] PROGMEM = {
0x01, 0x45, 0xBA, 0x99, 0xA8, 0x47, 0xAF, 0x43,
0x79, 0x3F, 0xDD, 0x0E, 0x87, 0x2E, 0x7C, 0xDF,
0xA1, 0x6B, 0xE3, 0x0F, 0xDC, 0x78, 0x0F, 0x97,
0xBC, 0xCC, 0x3F, 0x07, 0x83, 0x80, 0x20, 0x1E,
0x9C, 0x67, 0x7D, 0x60, 0x0B, 0x34, 0x37, 0x57,
0xA3, 0xBD, 0xBF, 0x2A, 0x31, 0x63, 0xE4, 0xC2,
0xF8, 0x69, 0xCC, 0xA7, 0x45, 0x8A, 0xA4, 0xA4,
0xEF, 0xFC, 0x31, 0x1F, 0x5C, 0xB1, 0x51, 0x68,
0x5E, 0xB9
};
static uint8_t const bob_public[132] PROGMEM = {
0x00, 0xD0, 0xB3, 0x97, 0x5A, 0xC4, 0xB7, 0x99,
0xF5, 0xBE, 0xA1, 0x6D, 0x5E, 0x13, 0xE9, 0xAF,
0x97, 0x1D, 0x5E, 0x9B, 0x98, 0x4C, 0x9F, 0x39,
0x72, 0x8B, 0x5E, 0x57, 0x39, 0x73, 0x5A, 0x21,
0x9B, 0x97, 0xC3, 0x56, 0x43, 0x6A, 0xDC, 0x6E,
0x95, 0xBB, 0x03, 0x52, 0xF6, 0xBE, 0x64, 0xA6,
0xC2, 0x91, 0x2D, 0x4E, 0xF2, 0xD0, 0x43, 0x3C,
0xED, 0x2B, 0x61, 0x71, 0x64, 0x00, 0x12, 0xD9,
0x46, 0x0F, 0x01, 0x5C, 0x68, 0x22, 0x63, 0x83,
0x95, 0x6E, 0x3B, 0xD0, 0x66, 0xE7, 0x97, 0xB6,
0x23, 0xC2, 0x7C, 0xE0, 0xEA, 0xC2, 0xF5, 0x51,
0xA1, 0x0C, 0x2C, 0x72, 0x4D, 0x98, 0x52, 0x07,
0x7B, 0x87, 0x22, 0x0B, 0x65, 0x36, 0xC5, 0xC4,
0x08, 0xA1, 0xD2, 0xAE, 0xBB, 0x8E, 0x86, 0xD6,
0x78, 0xAE, 0x49, 0xCB, 0x57, 0x09, 0x1F, 0x47,
0x32, 0x29, 0x65, 0x79, 0xAB, 0x44, 0xFC, 0xD1,
0x7F, 0x0F, 0xC5, 0x6A
};
static uint8_t const shared_secret[66] PROGMEM = {
0x01, 0x14, 0x4C, 0x7D, 0x79, 0xAE, 0x69, 0x56,
0xBC, 0x8E, 0xDB, 0x8E, 0x7C, 0x78, 0x7C, 0x45,
0x21, 0xCB, 0x08, 0x6F, 0xA6, 0x44, 0x07, 0xF9,
0x78, 0x94, 0xE5, 0xE6, 0xB2, 0xD7, 0x9B, 0x04,
0xD1, 0x42, 0x7E, 0x73, 0xCA, 0x4B, 0xAA, 0x24,
0x0A, 0x34, 0x78, 0x68, 0x59, 0x81, 0x0C, 0x06,
0xB3, 0xC7, 0x15, 0xA3, 0xA8, 0xCC, 0x31, 0x51,
0xF2, 0xBE, 0xE4, 0x17, 0x99, 0x6D, 0x19, 0xF3,
0xDD, 0xEA
};
// Evaluate the curve function and check the public keys.
uint8_t result[132];
Serial.println("Fixed test vectors:");
Serial.print("Computing Alice's public key ... ");
Serial.flush();
memcpy_P(alice_f, alice_private, 66);
unsigned long start = micros();
P521::eval(result, alice_f, 0);
unsigned long elapsed = micros() - start;
if (P521_memcmp_P(result, alice_public, 132) == 0) {
Serial.print("ok");
} else {
Serial.println("failed");
printNumber("actual ", result, 132);
printNumber("expected", alice_f, 132);
}
Serial.print(" (elapsed ");
Serial.print(elapsed);
Serial.println(" us)");
Serial.print("Computing Bob's public key ... ");
Serial.flush();
memcpy_P(bob_f, bob_private, 66);
start = micros();
P521::eval(result, bob_f, 0);
elapsed = micros() - start;
if (P521_memcmp_P(result, bob_public, 132) == 0) {
Serial.print("ok");
} else {
Serial.println("failed");
printNumber("actual ", result, 132);
printNumber("expected", bob_f, 132);
}
Serial.print(" (elapsed ");
Serial.print(elapsed);
Serial.println(" us)");
// Compute the shared secret from each side.
Serial.print("Computing Alice's shared secret ... ");
Serial.flush();
memcpy_P(alice_f, alice_private, 66);
memcpy_P(bob_k, bob_public, 132);
memcpy_P(bob_f, shared_secret, 66);
start = micros();
P521::eval(result, alice_f, bob_k);
elapsed = micros() - start;
if (P521_memcmp_P(result, shared_secret, 66) == 0) {
Serial.print("ok");
} else {
Serial.println("failed");
printNumber("actual ", result, 66);
printNumber("expected", bob_f, 66);
}
Serial.print(" (elapsed ");
Serial.print(elapsed);
Serial.println(" us)");
Serial.print("Computing Bob's shared secret ... ");
Serial.flush();
memcpy_P(bob_f, bob_private, 66);
memcpy_P(alice_k, alice_public, 132);
memcpy_P(alice_f, shared_secret, 66);
start = micros();
P521::eval(result, bob_f, alice_k);
elapsed = micros() - start;
if (P521_memcmp_P(result, shared_secret, 66) == 0) {
Serial.print("ok");
} else {
Serial.println("failed");
printNumber("actual ", result, 66);
printNumber("expected", alice_f, 66);
}
Serial.print(" (elapsed ");
Serial.print(elapsed);
Serial.println(" us)");
}
void testDH()
{
Serial.println("Diffie-Hellman key exchange:");
Serial.print("Generate random k/f for Alice ... ");
Serial.flush();
unsigned long start = micros();
P521::dh1(alice_k, alice_f);
unsigned long elapsed = micros() - start;
Serial.print("elapsed ");
Serial.print(elapsed);
Serial.println(" us");
Serial.print("Generate random k/f for Bob ... ");
Serial.flush();
start = micros();
P521::dh1(bob_k, bob_f);
elapsed = micros() - start;
Serial.print("elapsed ");
Serial.print(elapsed);
Serial.println(" us");
Serial.print("Generate shared secret for Alice ... ");
Serial.flush();
start = micros();
P521::dh2(bob_k, alice_f);
elapsed = micros() - start;
Serial.print("elapsed ");
Serial.print(elapsed);
Serial.println(" us");
Serial.print("Generate shared secret for Bob ... ");
Serial.flush();
start = micros();
P521::dh2(alice_k, bob_f);
elapsed = micros() - start;
Serial.print("elapsed ");
Serial.print(elapsed);
Serial.println(" us");
Serial.print("Check that the shared secrets match ... ");
if (memcmp(alice_f, bob_f, 66) == 0) {
Serial.println("ok");
} else {
Serial.println("failed");
printNumber("actual ", alice_f, 66);
printNumber("expected", bob_f, 66);
}
}
struct TestSignKey
{
uint8_t privateKey[66];
uint8_t publicKey[132];
};
// Test key from RFC 6979, Appendix A.2.7.
static TestSignKey const testKeyP521 PROGMEM = {
{0x00, 0xFA, 0xD0, 0x6D, 0xAA, 0x62, 0xBA, 0x3B, // x
0x25, 0xD2, 0xFB, 0x40, 0x13, 0x3D, 0xA7, 0x57,
0x20, 0x5D, 0xE6, 0x7F, 0x5B, 0xB0, 0x01, 0x8F,
0xEE, 0x8C, 0x86, 0xE1, 0xB6, 0x8C, 0x7E, 0x75,
0xCA, 0xA8, 0x96, 0xEB, 0x32, 0xF1, 0xF4, 0x7C,
0x70, 0x85, 0x58, 0x36, 0xA6, 0xD1, 0x6F, 0xCC,
0x14, 0x66, 0xF6, 0xD8, 0xFB, 0xEC, 0x67, 0xDB,
0x89, 0xEC, 0x0C, 0x08, 0xB0, 0xE9, 0x96, 0xB8,
0x35, 0x38},
{0x01, 0x89, 0x45, 0x50, 0xD0, 0x78, 0x59, 0x32, // Ux
0xE0, 0x0E, 0xAA, 0x23, 0xB6, 0x94, 0xF2, 0x13,
0xF8, 0xC3, 0x12, 0x1F, 0x86, 0xDC, 0x97, 0xA0,
0x4E, 0x5A, 0x71, 0x67, 0xDB, 0x4E, 0x5B, 0xCD,
0x37, 0x11, 0x23, 0xD4, 0x6E, 0x45, 0xDB, 0x6B,
0x5D, 0x53, 0x70, 0xA7, 0xF2, 0x0F, 0xB6, 0x33,
0x15, 0x5D, 0x38, 0xFF, 0xA1, 0x6D, 0x2B, 0xD7,
0x61, 0xDC, 0xAC, 0x47, 0x4B, 0x9A, 0x2F, 0x50,
0x23, 0xA4,
0x00, 0x49, 0x31, 0x01, 0xC9, 0x62, 0xCD, 0x4D, // Uy
0x2F, 0xDD, 0xF7, 0x82, 0x28, 0x5E, 0x64, 0x58,
0x41, 0x39, 0xC2, 0xF9, 0x1B, 0x47, 0xF8, 0x7F,
0xF8, 0x23, 0x54, 0xD6, 0x63, 0x0F, 0x74, 0x6A,
0x28, 0xA0, 0xDB, 0x25, 0x74, 0x1B, 0x5B, 0x34,
0xA8, 0x28, 0x00, 0x8B, 0x22, 0xAC, 0xC2, 0x3F,
0x92, 0x4F, 0xAA, 0xFB, 0xD4, 0xD3, 0x3F, 0x81,
0xEA, 0x66, 0x95, 0x6D, 0xFE, 0xAA, 0x2B, 0xFD,
0xFC, 0xF5}
};
struct TestSignVector
{
const char *name;
const char *data;
uint8_t signature[132];
};
// Test vectors from RFC 6979, Appendix A.2.7.
static TestSignVector const testVectorP521_1 PROGMEM = {
// P-521 test case with SHA-256 and message "sample".
"P-521 #1",
"sample",
{0x01, 0x51, 0x1B, 0xB4, 0xD6, 0x75, 0x11, 0x4F, // r
0xE2, 0x66, 0xFC, 0x43, 0x72, 0xB8, 0x76, 0x82,
0xBA, 0xEC, 0xC0, 0x1D, 0x3C, 0xC6, 0x2C, 0xF2,
0x30, 0x3C, 0x92, 0xB3, 0x52, 0x60, 0x12, 0x65,
0x9D, 0x16, 0x87, 0x6E, 0x25, 0xC7, 0xC1, 0xE5,
0x76, 0x48, 0xF2, 0x3B, 0x73, 0x56, 0x4D, 0x67,
0xF6, 0x1C, 0x6F, 0x14, 0xD5, 0x27, 0xD5, 0x49,
0x72, 0x81, 0x04, 0x21, 0xE7, 0xD8, 0x75, 0x89,
0xE1, 0xA7,
0x00, 0x4A, 0x17, 0x11, 0x43, 0xA8, 0x31, 0x63, // s
0xD6, 0xDF, 0x46, 0x0A, 0xAF, 0x61, 0x52, 0x26,
0x95, 0xF2, 0x07, 0xA5, 0x8B, 0x95, 0xC0, 0x64,
0x4D, 0x87, 0xE5, 0x2A, 0xA1, 0xA3, 0x47, 0x91,
0x6E, 0x4F, 0x7A, 0x72, 0x93, 0x0B, 0x1B, 0xC0,
0x6D, 0xBE, 0x22, 0xCE, 0x3F, 0x58, 0x26, 0x4A,
0xFD, 0x23, 0x70, 0x4C, 0xBB, 0x63, 0xB2, 0x9B,
0x93, 0x1F, 0x7D, 0xE6, 0xC9, 0xD9, 0x49, 0xA7,
0xEC, 0xFC}
};
static TestSignVector const testVectorP521_2 PROGMEM = {
// P-521 test case with SHA-512 and message "sample".
"P-521 #2",
"sample",
{0x00, 0xC3, 0x28, 0xFA, 0xFC, 0xBD, 0x79, 0xDD, // r
0x77, 0x85, 0x03, 0x70, 0xC4, 0x63, 0x25, 0xD9,
0x87, 0xCB, 0x52, 0x55, 0x69, 0xFB, 0x63, 0xC5,
0xD3, 0xBC, 0x53, 0x95, 0x0E, 0x6D, 0x4C, 0x5F,
0x17, 0x4E, 0x25, 0xA1, 0xEE, 0x90, 0x17, 0xB5,
0xD4, 0x50, 0x60, 0x6A, 0xDD, 0x15, 0x2B, 0x53,
0x49, 0x31, 0xD7, 0xD4, 0xE8, 0x45, 0x5C, 0xC9,
0x1F, 0x9B, 0x15, 0xBF, 0x05, 0xEC, 0x36, 0xE3,
0x77, 0xFA,
0x00, 0x61, 0x7C, 0xCE, 0x7C, 0xF5, 0x06, 0x48, // s
0x06, 0xC4, 0x67, 0xF6, 0x78, 0xD3, 0xB4, 0x08,
0x0D, 0x6F, 0x1C, 0xC5, 0x0A, 0xF2, 0x6C, 0xA2,
0x09, 0x41, 0x73, 0x08, 0x28, 0x1B, 0x68, 0xAF,
0x28, 0x26, 0x23, 0xEA, 0xA6, 0x3E, 0x5B, 0x5C,
0x07, 0x23, 0xD8, 0xB8, 0xC3, 0x7F, 0xF0, 0x77,
0x7B, 0x1A, 0x20, 0xF8, 0xCC, 0xB1, 0xDC, 0xCC,
0x43, 0x99, 0x7F, 0x1E, 0xE0, 0xE4, 0x4D, 0xA4,
0xA6, 0x7A}
};
static TestSignVector const testVectorP521_3 PROGMEM = {
// P-521 test case with SHA-256 and message "test".
"P-521 #3",
"test",
{0x00, 0x0E, 0x87, 0x1C, 0x4A, 0x14, 0xF9, 0x93, // r
0xC6, 0xC7, 0x36, 0x95, 0x01, 0x90, 0x0C, 0x4B,
0xC1, 0xE9, 0xC7, 0xB0, 0xB4, 0xBA, 0x44, 0xE0,
0x48, 0x68, 0xB3, 0x0B, 0x41, 0xD8, 0x07, 0x10,
0x42, 0xEB, 0x28, 0xC4, 0xC2, 0x50, 0x41, 0x1D,
0x0C, 0xE0, 0x8C, 0xD1, 0x97, 0xE4, 0x18, 0x8E,
0xA4, 0x87, 0x6F, 0x27, 0x9F, 0x90, 0xB3, 0xD8,
0xD7, 0x4A, 0x3C, 0x76, 0xE6, 0xF1, 0xE4, 0x65,
0x6A, 0xA8,
0x00, 0xCD, 0x52, 0xDB, 0xAA, 0x33, 0xB0, 0x63, // s
0xC3, 0xA6, 0xCD, 0x80, 0x58, 0xA1, 0xFB, 0x0A,
0x46, 0xA4, 0x75, 0x4B, 0x03, 0x4F, 0xCC, 0x64,
0x47, 0x66, 0xCA, 0x14, 0xDA, 0x8C, 0xA5, 0xCA,
0x9F, 0xDE, 0x00, 0xE8, 0x8C, 0x1A, 0xD6, 0x0C,
0xCB, 0xA7, 0x59, 0x02, 0x52, 0x99, 0x07, 0x9D,
0x7A, 0x42, 0x7E, 0xC3, 0xCC, 0x5B, 0x61, 0x9B,
0xFB, 0xC8, 0x28, 0xE7, 0x76, 0x9B, 0xCD, 0x69,
0x4E, 0x86}
};
static TestSignVector const testVectorP521_4 PROGMEM = {
// P-521 test case with SHA-512 and message "test".
"P-521 #4",
"test",
{0x01, 0x3E, 0x99, 0x02, 0x0A, 0xBF, 0x5C, 0xEE, // r
0x75, 0x25, 0xD1, 0x6B, 0x69, 0xB2, 0x29, 0x65,
0x2A, 0xB6, 0xBD, 0xF2, 0xAF, 0xFC, 0xAE, 0xF3,
0x87, 0x73, 0xB4, 0xB7, 0xD0, 0x87, 0x25, 0xF1,
0x0C, 0xDB, 0x93, 0x48, 0x2F, 0xDC, 0xC5, 0x4E,
0xDC, 0xEE, 0x91, 0xEC, 0xA4, 0x16, 0x6B, 0x2A,
0x7C, 0x62, 0x65, 0xEF, 0x0C, 0xE2, 0xBD, 0x70,
0x51, 0xB7, 0xCE, 0xF9, 0x45, 0xBA, 0xBD, 0x47,
0xEE, 0x6D,
0x01, 0xFB, 0xD0, 0x01, 0x3C, 0x67, 0x4A, 0xA7, // s
0x9C, 0xB3, 0x98, 0x49, 0x52, 0x79, 0x16, 0xCE,
0x30, 0x1C, 0x66, 0xEA, 0x7C, 0xE8, 0xB8, 0x06,
0x82, 0x78, 0x6A, 0xD6, 0x0F, 0x98, 0xF7, 0xE7,
0x8A, 0x19, 0xCA, 0x69, 0xEF, 0xF5, 0xC5, 0x74,
0x00, 0xE3, 0xB3, 0xA0, 0xAD, 0x66, 0xCE, 0x09,
0x78, 0x21, 0x4D, 0x13, 0xBA, 0xF4, 0xE9, 0xAC,
0x60, 0x75, 0x2F, 0x7B, 0x15, 0x5E, 0x2D, 0xE4,
0xDC, 0xE3}
};
void testSignCommon(const struct TestSignVector *_test, Hash *hash)
{
uint8_t *privateKey = alice_f;
uint8_t *publicKey = alice_k;
uint8_t *sig = bob_k;
static TestSignVector test;
memcpy_P(&test, _test, sizeof(test));
Serial.print(test.name);
Serial.print(" Sign ... ");
Serial.flush();
memcpy_P(privateKey, testKeyP521.privateKey, 66);
unsigned long start = micros();
P521::sign(sig, privateKey, test.data, strlen(test.data), hash);
unsigned long elapsed = micros() - start;
Serial.print(elapsed);
Serial.print(" us ... ");
bool ok = !memcmp(sig, test.signature, 132);
if (ok) {
Serial.println("ok");
} else {
Serial.println("failed");
printNumber("actual ", sig, 132);
printNumber("expected", test.signature, 132);
}
Serial.print(test.name);
Serial.print(" Verify ... ");
Serial.flush();
memcpy_P(publicKey, testKeyP521.publicKey, 132);
start = micros();
bool verified = P521::verify
(test.signature, publicKey, test.data, strlen(test.data), hash);
elapsed = micros() - start;
Serial.print(elapsed);
Serial.print(" us ... ");
if (verified)
Serial.println("ok");
else
Serial.println("failed");
Serial.print(test.name);
Serial.print(" Derive Public Key ... ");
Serial.flush();
memcpy_P(privateKey, testKeyP521.privateKey, 66);
start = micros();
P521::derivePublicKey(publicKey, privateKey);
elapsed = micros() - start;
Serial.print(elapsed);
Serial.print(" us ... ");
ok = !P521_memcmp_P(publicKey, testKeyP521.publicKey, 132);
if (ok) {
Serial.println("ok");
} else {
Serial.println("failed");
printNumber("actual ", publicKey, 132);
memcpy_P(publicKey, testKeyP521.publicKey, 132);
printNumber("expected", publicKey, 132);
}
}
void testSignSHA256(const struct TestSignVector *test)
{
SHA256 hash;
testSignCommon(test, &hash);
}
void testSignSHA512(const struct TestSignVector *test)
{
SHA512 hash;
testSignCommon(test, &hash);
}
void testSign()
{
Serial.println("Digital signatures:");
testSignSHA256(&testVectorP521_1);
testSignSHA512(&testVectorP521_2);
testSignSHA256(&testVectorP521_3);
testSignSHA512(&testVectorP521_4);
}
void setup()
{
Serial.begin(9600);
// Start the random number generator. We don't initialise a noise
// source here because we don't need one for testing purposes.
// Real DH applications should of course use a proper noise source.
RNG.begin("TestP521 1.0", 950);
// Perform the tests.
testEval();
Serial.println();
testDH();
Serial.println();
testSign();
Serial.println();
}
void loop()
{
}

View File

@ -0,0 +1,612 @@
/*
* 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.
*/
/*
This example runs tests on the P521 field mathematics independent
of the full curve operation itself.
*/
// Enable access to the internals of P521 to test the raw field ops.
#define TEST_P521_FIELD_OPS 1
#include <Crypto.h>
#include <P521.h>
#include <utility/ProgMemUtil.h>
#include <string.h>
// Copy some definitions from the P521 class for convenience.
#define NUM_LIMBS ((66 + sizeof(limb_t) - 1) / sizeof(limb_t))
#define LIMB_BITS (8 * sizeof(limb_t))
#define INVERSE_LIMB (~((limb_t)0))
// For simpleMod() below we need a type that is 4 times the size of limb_t.
#if BIGNUMBER_LIMB_8BIT
#define qlimb_t uint32_t
#elif BIGNUMBER_LIMB_16BIT
#define qlimb_t uint64_t
#else
#define BIGNUMBER_NO_QLIMB 1
#endif
limb_t arg1[NUM_LIMBS];
limb_t arg2[NUM_LIMBS];
limb_t result[NUM_LIMBS];
limb_t result2[NUM_LIMBS * 2 + 1];
limb_t temp[NUM_LIMBS];
// Convert a decimal string in program memory into a number.
void fromString(limb_t *x, uint8_t size, const char *str)
{
uint8_t ch, posn;
memset(x, 0, sizeof(limb_t) * size);
while ((ch = pgm_read_byte((uint8_t *)str)) != '\0') {
if (ch >= '0' && ch <= '9') {
// Quick and simple method to multiply by 10 and add the new digit.
dlimb_t carry = ch - '0';
for (posn = 0; posn < size; ++posn) {
carry += ((dlimb_t)x[posn]) * 10U;
x[posn] = (limb_t)carry;
carry >>= LIMB_BITS;
}
}
++str;
}
}
// Compare two numbers of NUM_LIMBS in length. Returns -1, 0, or 1.
int compare(const limb_t *x, const limb_t *y)
{
for (uint8_t posn = NUM_LIMBS; posn > 0; --posn) {
limb_t a = x[posn - 1];
limb_t b = y[posn - 1];
if (a < b)
return -1;
else if (a > b)
return 1;
}
return 0;
}
// Compare two numbers where one is a decimal string. Returns -1, 0, or 1.
int compare(const limb_t *x, const char *y)
{
limb_t val[NUM_LIMBS];
fromString(val, NUM_LIMBS, y);
return compare(x, val);
}
void printNumber(const char *name, const limb_t *x)
{
static const char hexchars[] = "0123456789ABCDEF";
Serial.print(name);
Serial.print(" = ");
for (uint8_t posn = NUM_LIMBS; posn > 0; --posn) {
for (uint8_t bit = LIMB_BITS; bit > 0; ) {
bit -= 4;
Serial.print(hexchars[(x[posn - 1] >> bit) & 0x0F]);
}
Serial.print(' ');
}
Serial.println();
}
// Standard numbers that are useful in field operation tests.
char const num_0[] PROGMEM = "0";
char const num_1[] PROGMEM = "1";
char const num_2[] PROGMEM = "2";
char const num_4[] PROGMEM = "4";
char const num_5[] PROGMEM = "5";
char const num_128[] PROGMEM = "128";
char const num_256[] PROGMEM = "256";
char const num_2_64_m7[] PROGMEM = "18446744073709551609"; // 2^64 - 7
char const num_2_129_m5[] PROGMEM = "680564733841876926926749214863536422907"; // 2^129 - 5
char const num_pi[] PROGMEM = "31415926535897932384626433832795028841971693993751058209749445923078164062862"; // 77 digits of pi
char const num_2_255_m253[] PROGMEM = "57896044618658097711785492504343953926634992332820282019728792003956564819715"; // 2^255 - 253
char const num_2_255_m20[] PROGMEM = "57896044618658097711785492504343953926634992332820282019728792003956564819948"; // 2^255 - 20
char const num_2_255_m19[] PROGMEM = "57896044618658097711785492504343953926634992332820282019728792003956564819949"; // 2^255 - 19
char const num_2_255_m19_x2[] PROGMEM = "115792089237316195423570985008687907853269984665640564039457584007913129639898"; // (2^255 - 19) * 2
char const num_2_521_m1[] PROGMEM = "6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151"; // 2^521 - 1
char const num_2_521_m2[] PROGMEM = "6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057150"; // 2^521 - 2
char const num_2_521_m20[] PROGMEM = "6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057132"; // 2^521 - 20
char const num_pi_big[] PROGMEM = "3141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117"; // 157 digits of pi
// Table of useful numbers less than 2^521 - 1.
const char * const numbers[] = {
num_0,
num_1,
num_2,
num_4,
num_5,
num_128,
num_256,
num_2_64_m7,
num_2_129_m5,
num_pi,
num_2_255_m253,
num_2_255_m20,
num_2_255_m19_x2,
num_pi_big,
num_2_521_m2,
num_2_521_m20,
0
};
#define numbers_count ((sizeof(numbers) / sizeof(numbers[0])) - 1)
#define foreach_number(var) \
const char *var = numbers[0]; \
for (unsigned index##var = 0; index##var < numbers_count; \
++index##var, var = numbers[index##var])
void printProgMem(const char *str)
{
uint8_t ch;
while ((ch = pgm_read_byte((uint8_t *)str)) != '\0') {
Serial.print((char)ch);
++str;
}
}
// Simple implementation of modular addition to cross-check the library.
void simpleAdd(limb_t *result, const limb_t *x, const limb_t *y)
{
uint8_t posn;
dlimb_t carry = 0;
for (posn = 0; posn < NUM_LIMBS; ++posn) {
carry += x[posn];
carry += y[posn];
result[posn] = (limb_t)carry;
carry >>= LIMB_BITS;
}
if (compare(result, num_2_521_m1) >= 0) {
// Subtract 2^521 - 1 to get the final result.
// Same as add 1 and then subtract 2^521.
carry = 1;
for (posn = 0; posn < NUM_LIMBS; ++posn) {
carry += result[posn];
result[posn] = (limb_t)carry;
carry >>= LIMB_BITS;
}
#if BIGNUMBER_LIMB_8BIT
result[NUM_LIMBS - 1] &= 0x01;
#else
result[NUM_LIMBS - 1] &= 0x1FF;
#endif
}
}
// Simple implementation of subtraction to cross-check the library.
// Note: this does not reduce the result modulo 2^521 - 1 and we
// assume that x is greater than or equal to y.
void simpleSub(limb_t *result, const limb_t *x, const limb_t *y)
{
uint8_t posn;
dlimb_t borrow = 0;
for (posn = 0; posn < NUM_LIMBS; ++posn) {
borrow = ((dlimb_t)x[posn]) - y[posn] - borrow;
result[posn] = (limb_t)borrow;
borrow = (borrow >> LIMB_BITS) != 0;
}
}
// Simple implementation of multiplication to cross-check the library.
// Note: this does not reduce the result modulo 2^521 - 1.
// The "result" buffer must contain at least NUM_LIMBS * 2 limbs.
void simpleMul(limb_t *result, const limb_t *x, const limb_t *y)
{
memset(result, 0, NUM_LIMBS * 2 * sizeof(limb_t));
for (uint8_t i = 0; i < NUM_LIMBS; ++i) {
for (uint8_t j = 0; j < NUM_LIMBS; ++j) {
uint8_t n = i + j;
dlimb_t carry =
((dlimb_t)x[i]) * y[j] + result[n];
result[n] = (limb_t)carry;
carry >>= LIMB_BITS;
++n;
while (carry != 0 && n < (NUM_LIMBS * 2)) {
carry += result[n];
result[n] = (limb_t)carry;
carry >>= LIMB_BITS;
++n;
}
}
}
}
#if defined(BIGNUMBER_NO_QLIMB)
// Quick check to correct the estimate on a quotient word.
static inline limb_t correctEstimate
(limb_t q, limb_t y1, limb_t y2, dlimb_t x01, limb_t x2)
{
// Algorithm D from section 4.3.1 of "The Art Of Computer Programming",
// D. Knuth, Volume 2, "Seminumerical Algorithms", Second Edition, 1981.
//
// We want to check if (y2 * q) > ((x01 - y1 * q) * b + x2) where
// b is (1 << LIMB_BITS). If it is, then q must be reduced by 1.
//
// One wrinkle that isn't obvious from Knuth's description is that it
// is possible for (x01 - y1 * q) >= b, especially in the case where
// x0 = y1 and q = b - 1. This will cause an overflow of the intermediate
// double-word result ((x01 - y1 * q) * b).
//
// In assembly language, we could use the carry flag to detect when
// (x01 - y1 * q) * b overflows, but we can't access the carry flag
// in C++. So we have to account for the carry in a different way here.
// Calculate the remainder using the estimated quotient.
dlimb_t r = x01 - ((dlimb_t)y1) * q;
// If there will be a double-word carry when we calculate (r * b),
// then (y2 * q) is obviously going to be less than (r * b), so we
// can stop here. The estimated quotient is correct.
if (r & (((dlimb_t)INVERSE_LIMB) << LIMB_BITS))
return q;
// Bail out if (y2 * q) <= (r * b + x2). The estimate is correct.
dlimb_t y2q = ((dlimb_t)y2) * q;
if (y2q <= ((r << LIMB_BITS) + x2))
return q;
// Correct for the estimated quotient being off by 1.
--q;
// Now repeat the check to correct for q values that are off by 2.
r += y1; // r' = (x01 - y1 * (q - 1)) = (x01 - y1 * q + y2) = r + y1
if (r & (((dlimb_t)INVERSE_LIMB) << LIMB_BITS))
return q;
// y2q' = (y2 * (q - 1)) = (y2 * q - y2) = y2q - y2
if ((y2q - y2) <= ((r << LIMB_BITS) + x2))
return q;
// Perform the final correction for q values that are off by 2.
return q - 1;
}
#endif
// Shift a big number left by a number of bits.
void shiftLeft(limb_t *x, size_t size, uint8_t shift)
{
dlimb_t carry = 0;
while (size > 0) {
carry += ((dlimb_t)(*x)) << shift;
*x++ = (limb_t)carry;
carry >>= LIMB_BITS;
--size;
}
}
// Shift a big number right by a number of bits.
void shiftRight(limb_t *x, size_t size, uint8_t shift)
{
limb_t carry = 0;
limb_t word;
while (size > 0) {
--size;
word = x[size];
x[size] = (word >> shift) | carry;
carry = (word << (LIMB_BITS - shift));
}
}
// Simple implementation of modular division to cross-check the library.
// Calling this "simple" is a bit of a misnomer. It is a full implementation
// of Algorithm D from section 4.3.1 of "The Art Of Computer Programming",
// D. Knuth, Volume 2, "Seminumerical Algorithms", Second Edition, 1981.
// This is quite slow on embedded platforms, but it should be correct.
// Note: "x" is assumed to be (NUM_LIMBS * 2 + 1) limbs in size because
// we need a limb for the extra leading zero word added by step D1.
void simpleMod(limb_t *x)
{
limb_t divisor[NUM_LIMBS];
uint8_t j, k, shift;
// Step D1. Normalize.
// The divisor (2^521 - 1) and "x" need to be shifted left until
// the top-most bit of the divisor is 1.
#if BIGNUMBER_LIMB_8BIT
shift = 7;
#else
shift = LIMB_BITS - 9;
#endif
fromString(divisor, NUM_LIMBS, num_2_521_m1);
shiftLeft(divisor, NUM_LIMBS, shift);
x[NUM_LIMBS * 2] = 0;
shiftLeft(x, NUM_LIMBS * 2 + 1, shift);
// Step D2/D7. Loop on j
for (j = 0; j <= NUM_LIMBS; ++j) {
// Step D3. Calculate an estimate of the top-most quotient word.
limb_t *u = x + NUM_LIMBS * 2 - 2 - j;
limb_t *v = divisor + NUM_LIMBS - 2;
limb_t q;
dlimb_t uword = ((((dlimb_t)u[2]) << LIMB_BITS) + u[1]);
if (u[2] == v[1])
q = ~((limb_t)0);
else
q = (limb_t)(uword / v[1]);
// Step D3, part 2. Correct the estimate downwards by 1 or 2.
// One subtlety of Knuth's algorithm is that it looks like the test
// is working with double-word quantities but it is actually using
// double-word plus a carry bit. So we need to use qlimb_t for this.
#if !defined(BIGNUMBER_NO_QLIMB)
qlimb_t test = ((((qlimb_t)uword) - ((dlimb_t)q) * v[1]) << LIMB_BITS) + u[0];
if ((((dlimb_t)q) * v[0]) > test) {
--q;
test = ((((qlimb_t)uword) - ((dlimb_t)q) * v[1]) << LIMB_BITS) + u[0];
if ((((dlimb_t)q) * v[0]) > test)
--q;
}
#else
// 32-bit platform - we don't have a 128-bit numeric type so we have
// to calculate the estimate in another way to preserve the carry bit.
q = correctEstimate(q, v[0], v[1], uword, u[0]);
#endif
// Step D4. Multiply and subtract.
u = x + (NUM_LIMBS - j);
v = divisor;
dlimb_t carry = 0;
dlimb_t borrow = 0;
for (k = 0; k < NUM_LIMBS; ++k) {
carry += ((dlimb_t)v[k]) * q;
borrow = ((dlimb_t)u[k]) - ((limb_t)carry) - borrow;
u[k] = (dlimb_t)borrow;
carry >>= LIMB_BITS;
borrow = ((borrow >> LIMB_BITS) != 0);
}
borrow = ((dlimb_t)u[k]) - ((limb_t)carry) - borrow;
u[k] = (dlimb_t)borrow;
// Step D5. Test remainder. Nothing further to do if no borrow.
if ((borrow >> LIMB_BITS) == 0)
continue;
// Step D6. Borrow occurred: add back.
carry = 0;
for (k = 0; k < NUM_LIMBS; ++k) {
carry += u[k];
carry += v[k];
u[k] = (limb_t)carry;
carry >>= LIMB_BITS;
}
u[k] += (limb_t)carry;
}
// Step D8. Unnormalize.
// Shift the remainder right to undo the earlier left shift.
shiftRight(x, NUM_LIMBS, shift);
}
void testAdd(const char *x, const char *y)
{
printProgMem(x);
Serial.print(" + ");
printProgMem(y);
Serial.print(": ");
Serial.flush();
fromString(arg1, NUM_LIMBS, x);
fromString(arg2, NUM_LIMBS, y);
P521::add(result, arg1, arg2);
simpleAdd(result2, arg1, arg2);
if (compare(result, result2) == 0) {
Serial.println("ok");
} else {
Serial.println("failed");
printNumber("actual ", result);
printNumber("expected", result2);
}
}
void testAdd()
{
Serial.println("Addition:");
foreach_number (x) {
foreach_number (y) {
testAdd(x, y);
}
}
Serial.println();
}
void testSub(const char *x, const char *y)
{
printProgMem(x);
Serial.print(" - ");
printProgMem(y);
Serial.print(": ");
Serial.flush();
fromString(arg1, NUM_LIMBS, x);
fromString(arg2, NUM_LIMBS, y);
P521::sub(result, arg1, arg2);
if (compare(arg1, arg2) >= 0) {
// First argument is larger than the second.
simpleSub(result2, arg1, arg2);
} else {
// First argument is smaller than the second.
// Compute arg1 + (2^521 - 1 - arg2).
fromString(temp, NUM_LIMBS, num_2_521_m1);
simpleSub(result2, temp, arg2);
simpleAdd(result2, arg1, result2);
}
if (compare(result, result2) == 0) {
Serial.println("ok");
} else {
Serial.println("failed");
printNumber("actual ", result);
printNumber("expected", result2);
}
}
void testSub()
{
Serial.println("Subtraction:");
foreach_number (x) {
foreach_number (y) {
testSub(x, y);
}
}
Serial.println();
}
void testMul(const char *x, const char *y)
{
printProgMem(x);
Serial.print(" * ");
printProgMem(y);
Serial.print(": ");
Serial.flush();
fromString(arg1, NUM_LIMBS, x);
fromString(arg2, NUM_LIMBS, y);
if (compare(arg1, arg2) != 0)
P521::mul(result, arg1, arg2);
else
P521::square(result, arg1);
simpleMul(result2, arg1, arg2);
simpleMod(result2);
if (compare(result, result2) == 0) {
Serial.println("ok");
} else {
Serial.println("failed");
printNumber("actual ", result);
printNumber("expected", result2);
}
}
void testMul()
{
Serial.println("Multiplication:");
foreach_number (x) {
foreach_number (y) {
testMul(x, y);
}
}
Serial.println();
}
void testMove(const char *x, const char *y, uint8_t select)
{
printProgMem(x);
Serial.print(" <- ");
printProgMem(y);
Serial.print(": ");
Serial.flush();
fromString(arg1, NUM_LIMBS, x);
fromString(arg2, NUM_LIMBS, y);
memcpy(result, arg1, NUM_LIMBS * sizeof(limb_t));
memcpy(result2, arg2, NUM_LIMBS * sizeof(limb_t));
// Perform the move using the selection bit.
P521::cmove(select, result, result2);
bool ok = compare(result, arg2) == 0;
// Reset and test not moving.
memcpy(result, arg1, NUM_LIMBS * sizeof(limb_t));
memcpy(result2, arg2, NUM_LIMBS * sizeof(limb_t));
P521::cmove(0, result, result2);
if (ok)
ok = compare(result, arg1) == 0;
if (ok) {
Serial.println("ok");
} else {
Serial.println("failed");
}
}
void testMove()
{
Serial.println("Move:");
uint8_t bit = 0;
foreach_number (x) {
foreach_number (y) {
testMove(x, y, ((uint8_t)1) << bit);
bit = (bit + 1) % 8;
}
}
Serial.println();
}
void testRecip(const char *x)
{
printProgMem(x);
Serial.print("^-1");
Serial.print(": ");
Serial.flush();
fromString(arg1, NUM_LIMBS, x);
P521::recip(result, arg1);
bool ok;
if (compare(arg1, num_0) == 0) {
// 0^-1 = 0
ok = (compare(result, num_0) == 0);
} else {
// Multiply the result with arg1 - we expect 1 as the result.
P521::mul(result2, result, arg1);
ok = (compare(result2, num_1) == 0);
}
if (ok) {
Serial.println("ok");
} else {
Serial.println("failed");
printNumber("actual", result);
}
}
void testRecip()
{
Serial.println("Reciprocal:");
foreach_number (x) {
testRecip(x);
}
Serial.println();
}
void setup()
{
Serial.begin(9600);
testAdd();
testSub();
testMul();
testMove();
testRecip();
}
void loop()
{
}

View File

@ -26,9 +26,11 @@
#include "ProgMemUtil.h" #include "ProgMemUtil.h"
// Number of limbs in a big number value of various sizes. // Number of limbs in a big number value of various sizes.
#define NUM_LIMBS_128BIT (16 / sizeof(limb_t)) #define NUM_LIMBS_BITS(n) \
#define NUM_LIMBS_256BIT (32 / sizeof(limb_t)) (((n) + sizeof(limb_t) * 8 - 1) / (8 * sizeof(limb_t)))
#define NUM_LIMBS_512BIT (64 / sizeof(limb_t)) #define NUM_LIMBS_128BIT NUM_LIMBS_BITS(128)
#define NUM_LIMBS_256BIT NUM_LIMBS_BITS(256)
#define NUM_LIMBS_512BIT NUM_LIMBS_BITS(512)
// The number of bits in a limb. // The number of bits in a limb.
#define LIMB_BITS (8 * sizeof(limb_t)) #define LIMB_BITS (8 * sizeof(limb_t))