blob: 448d9e547edd006792cb081cc2dea26175413609 [file] [log] [blame]
/*
*
* Copyright (c) 2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* Micro-ecc implementations of elliptic curve functions used by Weave security code.
*
*/
#include <string.h>
#include "WeaveCrypto.h"
#include "EllipticCurve.h"
#include "HashAlgos.h"
#include <Weave/Core/WeaveEncoding.h>
#include <Weave/Support/CodeUtils.h>
#if WEAVE_CONFIG_USE_MICRO_ECC
namespace nl {
namespace Weave {
namespace Crypto {
using namespace nl::Weave::ASN1;
using namespace nl::Weave::Platform::Security;
static uECC_Curve CurveOID2uECC_Curve(OID curveOID)
{
switch (curveOID)
{
#if WEAVE_CONFIG_SUPPORT_ELLIPTIC_CURVE_SECP160R1
case kOID_EllipticCurve_secp160r1:
return uECC_secp160r1();
#endif
#if WEAVE_CONFIG_SUPPORT_ELLIPTIC_CURVE_SECP192R1
case kOID_EllipticCurve_prime192v1:
return uECC_secp192r1();
#endif
#if WEAVE_CONFIG_SUPPORT_ELLIPTIC_CURVE_SECP224R1
case kOID_EllipticCurve_secp224r1:
return uECC_secp224r1();
#endif
#if WEAVE_CONFIG_SUPPORT_ELLIPTIC_CURVE_SECP256R1
case kOID_EllipticCurve_prime256v1:
return uECC_secp256r1();
#endif
default:
return NULL;
}
}
int GetCurveSize(const OID curveOID)
{
uECC_Curve curve = CurveOID2uECC_Curve(curveOID);
int curveLen = 0;
if (curve != NULL)
curveLen = uECC_curve_num_bytes(curve);
return curveLen;
}
static int GetSecureRandomData_uECC(uint8_t *buf, unsigned len)
{
if ( len > 0xFFFF )
return 0;
if ( GetSecureRandomData(buf, (uint16_t)len) == WEAVE_NO_ERROR )
return 1;
return 0;
}
static WEAVE_ERROR DecodeDERInt(const uint8_t *derInt, uint16_t derIntLen, uint8_t *eccInt, const uint16_t eccIntLen)
{
if ( derIntLen == 0 )
return WEAVE_ERROR_INVALID_ARGUMENT;
/* one leading zero is allowed for positive integer in ASN1 DER format */
if ( *derInt == 0 )
{
derInt++;
derIntLen--;
}
if ( (derIntLen > eccIntLen) || (derIntLen > 0 && *derInt == 0) )
return WEAVE_ERROR_INVALID_ARGUMENT;
memset(eccInt, 0, (eccIntLen - derIntLen));
memcpy(eccInt + (eccIntLen - derIntLen), derInt, derIntLen);
return WEAVE_NO_ERROR;
}
static WEAVE_ERROR EncodeDERInt(const uint8_t *eccInt, uint16_t eccIntLen, uint8_t *derInt, const uint16_t derIntSize, uint16_t& derIntLen)
{
while (*eccInt == 0)
{
eccInt++;
eccIntLen--;
}
if ( *eccInt & 0x80 ) /* Need Leading Zero */
{
if ( derIntSize < eccIntLen + 1 )
return WEAVE_ERROR_BUFFER_TOO_SMALL;
*derInt++ = 0;
derIntLen = eccIntLen + 1;
}
else
{
if ( derIntSize < eccIntLen )
return WEAVE_ERROR_BUFFER_TOO_SMALL;
derIntLen = eccIntLen;
}
memcpy(derInt, eccInt, eccIntLen);
return WEAVE_NO_ERROR;
}
static WEAVE_ERROR DecodeECPrivateKey(const EncodedECPrivateKey& encodedPrivKey, uint8_t *privKey, uint16_t privKeyLen)
{
return DecodeDERInt(encodedPrivKey.PrivKey, encodedPrivKey.PrivKeyLen, privKey, privKeyLen);
}
static WEAVE_ERROR EncodeECPrivateKey(const uint8_t *privKey, uint16_t privKeyLen, EncodedECPrivateKey& encodedPrivKey)
{
return EncodeDERInt(privKey, privKeyLen, encodedPrivKey.PrivKey, encodedPrivKey.PrivKeyLen, encodedPrivKey.PrivKeyLen);
}
// Generate an ECDSA signature given a message hash and a EC private key.
WEAVE_ERROR GenerateECDSASignature(OID curveOID,
const uint8_t *msgHash, uint8_t msgHashLen,
const EncodedECPrivateKey& encodedPrivKey,
EncodedECDSASignature& encodedSig)
{
WEAVE_ERROR err;
uint8_t l_sig[2 * kuECC_MaxByteCount]; /* l_sig = {r, s} */
uint16_t tmpLen;
uECC_Curve curve;
uint16_t curveLen;
curve = CurveOID2uECC_Curve(curveOID);
VerifyOrExit(curve != NULL, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
err = GenerateECDSASignature(curveOID, msgHash, msgHashLen, encodedPrivKey, l_sig);
SuccessOrExit(err);
// Curve length in bytes
curveLen = uECC_curve_num_bytes(curve);
err = EncodeDERInt(l_sig, curveLen, encodedSig.R, encodedSig.RLen, tmpLen);
SuccessOrExit(err);
encodedSig.RLen = tmpLen;
err = EncodeDERInt(l_sig + curveLen, curveLen, encodedSig.S, encodedSig.SLen, tmpLen);
SuccessOrExit(err);
encodedSig.SLen = tmpLen;
exit:
return err;
}
// Generate an ECDSA signature given a message hash and a EC private key.
WEAVE_ERROR GenerateECDSASignature(OID curveOID,
const uint8_t *msgHash, uint8_t msgHashLen,
const EncodedECPrivateKey& encodedPrivKey,
uint8_t *fixedLenSig)
{
WEAVE_ERROR err;
uint8_t privKey[kuECC_MaxByteCount];
int res;
uECC_Curve curve;
uint16_t privKeyLen;
curve = CurveOID2uECC_Curve(curveOID);
VerifyOrExit(curve != NULL, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
// Private key length in bytes
privKeyLen = uECC_curve_num_n_bytes(curve);
err = DecodeECPrivateKey(encodedPrivKey, privKey, privKeyLen);
SuccessOrExit(err);
// Set GetSecureRandomData_uECC function to be used by Micro-ECC for random bytes generation
uECC_set_rng(GetSecureRandomData_uECC);
// Attempt to sign the message, producing the R and S values in the process.
// uECC_sign repeats the process several times if the generated random number was not suitable for signing.
res = uECC_sign(privKey, msgHash, msgHashLen, fixedLenSig, curve);
VerifyOrExit(res == 1, err = WEAVE_ERROR_RANDOM_DATA_UNAVAILABLE);
exit:
ClearSecretData(privKey, sizeof(privKey));
return err;
}
// Verify an ECDSA signature.
WEAVE_ERROR VerifyECDSASignature(OID curveOID,
const uint8_t *msgHash, uint8_t msgHashLen,
const EncodedECDSASignature& encodedSig,
const EncodedECPublicKey& encodedPubKey)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint8_t l_sig[2 * kuECC_MaxByteCount]; /* l_sig = {r, s} */
int res;
uECC_Curve curve;
uint16_t curveLen;
curve = CurveOID2uECC_Curve(curveOID);
VerifyOrExit(curve != NULL, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
// Curve length in bytes
curveLen = uECC_curve_num_bytes(curve);
err = DecodeDERInt(encodedSig.R, encodedSig.RLen, l_sig, curveLen);
SuccessOrExit(err);
err = DecodeDERInt(encodedSig.S, encodedSig.SLen, l_sig + curveLen, curveLen);
SuccessOrExit(err);
// Verify encoded point size and X9.63 uncompressed format (0x04)
VerifyOrExit(encodedPubKey.ECPointLen == 2 * curveLen + 1, err = WEAVE_ERROR_INVALID_ARGUMENT);
VerifyOrExit(*encodedPubKey.ECPoint == kX963EncodedPointFormat_Uncompressed, err = WEAVE_ERROR_INVALID_ARGUMENT);
res = uECC_verify(encodedPubKey.ECPoint + 1, msgHash, msgHashLen, l_sig, curve);
VerifyOrExit(res == 1, err = WEAVE_ERROR_INVALID_SIGNATURE);
exit:
return err;
}
// Verify an ECDSA signature.
WEAVE_ERROR VerifyECDSASignature(OID curveOID,
const uint8_t *msgHash, uint8_t msgHashLen,
const uint8_t *fixedLenSig,
const EncodedECPublicKey& encodedPubKey)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
int res;
uECC_Curve curve;
uint16_t curveLen;
curve = CurveOID2uECC_Curve(curveOID);
VerifyOrExit(curve != NULL, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
// Curve length in bytes
curveLen = uECC_curve_num_bytes(curve);
// Verify encoded point size and X9.63 uncompressed format (0x04)
VerifyOrExit(encodedPubKey.ECPointLen == 2 * curveLen + 1, err = WEAVE_ERROR_INVALID_ARGUMENT);
VerifyOrExit(*encodedPubKey.ECPoint == kX963EncodedPointFormat_Uncompressed, err = WEAVE_ERROR_INVALID_ARGUMENT);
res = uECC_verify(encodedPubKey.ECPoint + 1, msgHash, msgHashLen, fixedLenSig, curve);
VerifyOrExit(res == 1, err = WEAVE_ERROR_INVALID_SIGNATURE);
exit:
return err;
}
#if WEAVE_CONFIG_SECURITY_TEST_MODE
// Constant-time check if the supplied private key has the integer value of 1 (big endian).
static bool IsOneKey(const uint8_t *privKey, uint16_t len)
{
uint8_t keyBits = 0;
int i = 0;
for (; i < len - 1; i++)
keyBits |= privKey[i];
keyBits |= (privKey[i] - 1);
return (keyBits == 0);
}
#endif
WEAVE_ERROR ECDHComputeSharedSecret(OID curveOID, const EncodedECPublicKey& encodedPubKey, const EncodedECPrivateKey& encodedPrivKey,
uint8_t *sharedSecretBuf, uint16_t sharedSecretBufSize, uint16_t& sharedSecretLen)
{
WEAVE_ERROR err;
uint8_t privKey[kuECC_MaxByteCount];
uECC_Curve curve;
uint16_t curveLen;
int res;
curve = CurveOID2uECC_Curve(curveOID);
VerifyOrExit(curve != NULL, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
// Curve length in bytes
curveLen = uECC_curve_num_bytes(curve);
VerifyOrExit(sharedSecretBufSize >= curveLen, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
// Verify encoded point size and X9.63 uncompressed format (0x04)
VerifyOrExit(encodedPubKey.ECPointLen >= 2 * curveLen + 1, err = WEAVE_ERROR_INVALID_ARGUMENT);
VerifyOrExit(*encodedPubKey.ECPoint == kX963EncodedPointFormat_Uncompressed, err = WEAVE_ERROR_INVALID_ARGUMENT);
err = DecodeECPrivateKey(encodedPrivKey, privKey, curveLen);
SuccessOrExit(err);
res = uECC_shared_secret(encodedPubKey.ECPoint + 1, privKey, sharedSecretBuf, curve);
#if WEAVE_CONFIG_SECURITY_TEST_MODE
// uECC does not handle multiplying a point by 1. So if the private key is the well-known
// test private key (1), ignore the result of uECC_shared_secret() and set the derived
// shared secret to the X value of the peer's public key.
if (IsOneKey(privKey, curveLen))
{
memcpy(sharedSecretBuf, encodedPubKey.ECPoint + 1, curveLen);
res = 1;
}
#endif
VerifyOrExit(res != 0, err = WEAVE_ERROR_INVALID_ARGUMENT);
sharedSecretLen = curveLen;
exit:
ClearSecretData(privKey, sizeof(privKey));
return err;
}
WEAVE_ERROR GenerateECDHKey(OID curveOID, EncodedECPublicKey& encodedPubKey, EncodedECPrivateKey& encodedPrivKey)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint8_t privKey[kuECC_MaxByteCount];
int res;
uECC_Curve curve;
uint16_t curveLen;
curve = CurveOID2uECC_Curve(curveOID);
VerifyOrExit(curve != NULL, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
// Curve length in bytes
curveLen = uECC_curve_num_bytes(curve);
VerifyOrExit(encodedPubKey.ECPointLen >= 2 * curveLen + 1, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
// Set GetSecureRandomData_uECC function to be used by Micro-ECC for random bytes generation
uECC_set_rng(GetSecureRandomData_uECC);
// uECC_make_key repeats the process 16 times if the generated random number was not suitable for signing
res = uECC_make_key(encodedPubKey.ECPoint + 1, privKey, curve);
VerifyOrExit(res == 1, err = WEAVE_ERROR_RANDOM_DATA_UNAVAILABLE);
// Encode EC Point to X9.63 uncompressed format.
*encodedPubKey.ECPoint = kX963EncodedPointFormat_Uncompressed;
encodedPubKey.ECPointLen = 2 * curveLen + 1;
err = EncodeECPrivateKey(privKey, curveLen, encodedPrivKey);
SuccessOrExit(err);
exit:
ClearSecretData(privKey, sizeof(privKey));
return err;
}
WEAVE_ERROR GetCurveG(OID curveOID, EncodedECPublicKey& encodedG)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uECC_Curve curve;
uint16_t curveLen, encodedPointLen;
const uECC_word_t *g;
curve = CurveOID2uECC_Curve(curveOID);
VerifyOrExit(curve != NULL, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
// Curve length in bytes
curveLen = uECC_curve_num_bytes(curve);
// Compute the encoded point length.
encodedPointLen = 2 * curveLen + 1;
VerifyOrExit(encodedG.ECPointLen >= encodedPointLen, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
g = uECC_curve_G(curve);
*encodedG.ECPoint = kX963EncodedPointFormat_Uncompressed;
uECC_vli_nativeToBytes(encodedG.ECPoint + 1, curveLen, g);
uECC_vli_nativeToBytes(encodedG.ECPoint + 1 + curveLen, curveLen, g + uECC_curve_num_words(curve));
encodedG.ECPointLen = encodedPointLen;
exit:
return err;
}
// ============================================================
// Elliptic Curve JPAKE Class Functions
// ============================================================
#if WEAVE_IS_ECJPAKE_ENABLED
/* Compares points: returns 1 if left == right */
#define uECC_point_equal(left, right, num_words) \
uECC_vli_equal((left), (right), 2 * (num_words))
/* Sets point: result = point */
#define uECC_point_set(result, point, num_words) \
uECC_vli_set((result), (point), 2 * (num_words))
/* Checks if point is zero: returns 1 if point is zero */
#define uECC_point_isZero(point, num_words) \
uECC_vli_isZero((point), 2 * (num_words))
/* Zeroizes point: point = 0 */
#define uECC_point_clear(point, num_words) \
uECC_vli_clear((point), 2 * (num_words))
/* Points addition: result = left + right */
extern void uECC_point_add(uECC_word_t *result,
const uECC_word_t *left,
const uECC_word_t *right,
uECC_Curve curve)
{
uECC_word_t Rx[kuECC_MaxWordCount];
uECC_word_t Ry[kuECC_MaxWordCount];
uECC_word_t l[kuECC_MaxWordCount] = { 0 };
const uECC_word_t *curve_p = uECC_curve_p(curve);
const wordcount_t num_words = uECC_curve_num_words(curve);
/* if left == 0, then result = right */
if (uECC_point_isZero(left, num_words)) {
uECC_point_set(result, right, num_words);
return;
}
/* if right == 0, then result = left */
if (uECC_point_isZero(right, num_words)) {
uECC_point_set(result, left, num_words);
return;
}
/* if left == right, then result = 2 * left */
if (uECC_point_equal(left, right, num_words)) {
l[0] = 0x02;
uECC_point_mult(result, left, l, curve);
return;
}
/* if left == - right, then result = 0 */
if (uECC_vli_equal(left, right, num_words)) {
uECC_point_clear(result, num_words);
return;
}
/**
* Calculate:
* (Rx, Ry) = (leftX, leftY) + (rightX, rightY)
* Method for two Elliptic Curve Points addition:
* lambda (l) = (rightY - leftY) / (rightX - leftX)
* then:
* resultX = lambda ^ 2 - rightX - leftX
* resultY = lambda * (leftX - Rx) - leftY
*/
/* calculate l = (rightY - leftY) / (rightX - leftX) */
uECC_vli_modSub(l, right, left, curve_p, num_words);
uECC_vli_modSub(Rx, right + num_words, left + num_words,
curve_p, num_words);
uECC_vli_modInv(l, l, curve_p, num_words);
uECC_vli_modMult_fast(l, l, Rx, curve);
/* calculate Rx = l ^ 2 - rightX - leftX */
uECC_vli_modMult_fast(Rx, l, l, curve);
uECC_vli_modSub(Rx, Rx, right, curve_p, num_words);
uECC_vli_modSub(Rx, Rx, left, curve_p, num_words);
/* calculate Ry = l * (leftX - Rx) - leftY */
uECC_vli_modSub(Ry, left, Rx, curve_p, num_words);
uECC_vli_modMult_fast(Ry, Ry, l, curve);
uECC_vli_modSub(Ry, Ry, left + num_words, curve_p, num_words);
/* assign output */
uECC_vli_set(result, Rx, num_words);
uECC_vli_set(result + num_words, Ry, num_words);
}
/* Converts long integer in big-endian form (input) into uECC native VLI form (modulo n) */
static void uECC_vli_bytesToNative_mod_n(uECC_word_t *result,
const uint8_t *input,
wordcount_t input_len,
uECC_Curve curve)
{
const uint8_t num_n_words = uECC_curve_num_n_words(curve);
uint32_t input_vli[2 * kuECC_MaxWordCount] = { 0 };
/* Convert to VLI (native) form */
uECC_vli_bytesToNative(input_vli, input, input_len);
if (((input_len + 3) / 4) < num_n_words) {
/* Copy result */
memcpy((uint8_t *)result, (uint8_t *)input_vli, 4 * num_n_words);
} else {
/* Modulo reduction: result = input_vli % n */
uECC_vli_mmod(result, input_vli, uECC_curve_n(curve), num_n_words);
}
}
void EllipticCurveJPAKE::Init()
{
}
void EllipticCurveJPAKE::Shutdown()
{
Reset();
}
/* Clear secret content and Reset algorithm parameters */
void EllipticCurveJPAKE::Reset()
{
memset(this, 0, sizeof(*this));
}
/* Init EllipticCurveJPAKE algorithm parameters */
WEAVE_ERROR EllipticCurveJPAKE::Init(const OID curveOID,
const uint8_t *pw, const uint16_t pwLen,
const uint8_t *localName, const uint16_t localNameLen,
const uint8_t *peerName, const uint16_t peerNameLen)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
Curve = CurveOID2uECC_Curve(curveOID);
VerifyOrExit(Curve != NULL, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
// verify valid length inputs
VerifyOrExit(pwLen <= kECJPAKE_MaxPasswordLength &&
localNameLen <= kECJPAKE_MaxNameLength &&
peerNameLen <= kECJPAKE_MaxNameLength, err = WEAVE_ERROR_INVALID_ARGUMENT);
// convert password into VLI integer form
uECC_vli_bytesToNative_mod_n(XbS, pw, pwLen, Curve);
// init local and peer names
memcpy(LocalName, localName, localNameLen);
memcpy(PeerName, peerName, peerNameLen);
LocalNameLen = localNameLen;
PeerNameLen = peerNameLen;
// clear Gxacd/Gxabc as protocol assumes they are zero at initialization
memset((uint8_t *)Gxacd, 0, sizeof(EccPoint));
memset((uint8_t *)Gxabc, 0, sizeof(EccPoint));
// Set GetSecureRandomData_uECC function to be used by Micro-ECC for random bytes generation
uECC_set_rng(GetSecureRandomData_uECC);
exit:
if (err != WEAVE_NO_ERROR)
Reset();
return err;
}
/* h = hash(G, G*r, G*x, name) */
static void ZeroKnowledgeProofHash(const wordcount_t words, uint8_t *h, const EccPoint zkpG, const ECJPAKEStepPart *stepPart, const uint8_t *name, const uint16_t nameLen)
{
SHA256 hash;
const uint16_t pointLen = 2 * kuECC_WordSize * words;
hash.Begin();
hash.AddData((uint8_t *)zkpG, pointLen); // Hash G point
hash.AddData((uint8_t *)stepPart->Gr, pointLen); // Hash Gr point
hash.AddData((uint8_t *)stepPart->Gx, pointLen); // Hash Gx point
hash.AddData(name, nameLen); // Hash name string
hash.Finish(h);
}
/* Find Step Part Data Fields in the Buffer */
WEAVE_ERROR EllipticCurveJPAKE::FindStepPartDataPointers(ECJPAKEStepPart *stepPart, const uint8_t *buf, const uint16_t bufSize, uint16_t& stepDataLen)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
const uint8_t pointWordCount = 2 * uECC_curve_num_words(Curve);
const uint8_t stepPartByteCount = (2 * pointWordCount + uECC_curve_num_n_words(Curve)) * kuECC_WordSize;
VerifyOrExit(stepPartByteCount <= bufSize - stepDataLen, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
stepPart->Gx = (uECC_word_t *)(buf + stepDataLen);
stepPart->Gr = stepPart->Gx + pointWordCount;
stepPart->b = stepPart->Gr + pointWordCount;
stepDataLen += stepPartByteCount;
exit:
return err;
}
WEAVE_ERROR EllipticCurveJPAKE::VerifyZeroKnowledgeProof(const ECJPAKEStepPart *stepPart, const EccPoint zkpG, const uint8_t *name, const uint16_t nameLen)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
union
{
uint8_t hash[kECJPAKE_HashLength];
uECC_word_t hashVLI[kuECC_MaxWordCount];
};
EccPoint ecPoint1;
EccPoint ecPoint2;
/* hash = hash(G, G*r, G*x, name) */
ZeroKnowledgeProofHash(uECC_curve_num_words(Curve), hash, zkpG, stepPart, name, nameLen);
/* Convert ZKP Hash result into VLI format */
uECC_vli_bytesToNative_mod_n(hashVLI, hash, kECJPAKE_HashLength, Curve);
/* ecPoint1 = G*b */
uECC_point_mult(ecPoint1, zkpG, stepPart->b, Curve);
/* ecPoint2 = (G*x)*h = G*{h*x} */
uECC_point_mult(ecPoint2, stepPart->Gx, hashVLI, Curve);
/* ecPoint2 = ecPoint1 + ecPoint2 = G*{hx} + G*b = G*{hx+b} = G*r (allegedly) */
uECC_point_add(ecPoint2, ecPoint1, ecPoint2, Curve);
/* verify ecPoint2 == G*r (ecPoint1) */
if (!uECC_point_equal(ecPoint2, stepPart->Gr, uECC_curve_num_words(Curve)))
err = WEAVE_ERROR_INVALID_PASE_PARAMETER;
return err;
}
/* Prove knowledge of x (in G*x) */
WEAVE_ERROR EllipticCurveJPAKE::GenerateZeroKnowledgeProof(ECJPAKEStepPart *stepPart, const uECC_word_t *x, const EccPoint zkpG, const uint8_t *name, const uint16_t nameLen)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
union
{
uint8_t hash[kECJPAKE_HashLength];
uECC_word_t hashVLI[kuECC_MaxWordCount];
};
const uECC_word_t *curve_n = uECC_curve_n(Curve);
uint8_t num_n_words = uECC_curve_num_n_words(Curve);
/* Generate random number r in [1, n-1] */
/* uses b as temp storage for random number */
VerifyOrExit(uECC_generate_random_int(stepPart->b, curve_n, num_n_words),
err = WEAVE_ERROR_RANDOM_DATA_UNAVAILABLE);
/* G*r */
uECC_point_mult(stepPart->Gr, zkpG, stepPart->b, Curve);
/* hash = hash(G, G*r, G*x, name) */
ZeroKnowledgeProofHash(uECC_curve_num_words(Curve), hash, zkpG, stepPart, name, nameLen);
/* Convert ZKP Hash result into VLI format */
uECC_vli_bytesToNative_mod_n(hashVLI, hash, kECJPAKE_HashLength, Curve);
/* b = (r - x * h) % n */
uECC_vli_modMult(hashVLI, x, hashVLI, curve_n, num_n_words);
uECC_vli_modSub(stepPart->b, stepPart->b, hashVLI, curve_n, num_n_words);
exit:
return err;
}
WEAVE_ERROR EllipticCurveJPAKE::GenerateStepPart(ECJPAKEStepPart *stepPart, const uECC_word_t *x, const EccPoint G, const uint8_t *name, const uint16_t nameLen)
{
uECC_point_mult(stepPart->Gx, G, x, Curve);
return GenerateZeroKnowledgeProof(stepPart, x, G, name, nameLen);
}
WEAVE_ERROR EllipticCurveJPAKE::GenerateStep1(const uint8_t *buf, const uint16_t bufSize, uint16_t& stepDataLen)
{
WEAVE_ERROR err;
ECJPAKEStepPart stepPart;
const uECC_word_t *curve_n = uECC_curve_n(Curve);
uint8_t num_n_words = uECC_curve_num_n_words(Curve);
/* Find Step1 (Part1) Data Pointers */
err = FindStepPartDataPointers(&stepPart, buf, bufSize, stepDataLen);
SuccessOrExit(err);
/* Generate Random xa */
/* xa is temporary stored at Xb and then overriden with Xb value */
VerifyOrExit(uECC_generate_random_int(Xb, curve_n, num_n_words),
err = WEAVE_ERROR_RANDOM_DATA_UNAVAILABLE);
/* Generate STEP1 Part 1 */
err = GenerateStepPart(&stepPart, Xb, uECC_curve_G(Curve), LocalName, LocalNameLen);
SuccessOrExit(err);
/* add Gxa to Gxacd and to Gxabc */
uECC_point_add(Gxacd, Gxacd, stepPart.Gx, Curve);
uECC_point_add(Gxabc, Gxabc, stepPart.Gx, Curve);
/* Find Step1 (Part2) Data Pointers */
err = FindStepPartDataPointers(&stepPart, buf, bufSize, stepDataLen);
SuccessOrExit(err);
/* Generate Random xb */
VerifyOrExit(uECC_generate_random_int(Xb, curve_n, num_n_words),
err = WEAVE_ERROR_RANDOM_DATA_UNAVAILABLE);
/* Generate STEP1 Part 2 */
err = GenerateStepPart(&stepPart, Xb, uECC_curve_G(Curve), LocalName, LocalNameLen);
SuccessOrExit(err);
/* add Gxb to Gxabc */
uECC_point_add(Gxabc, Gxabc, stepPart.Gx, Curve);
/* Calculate and Store (Xb * Secret % n) for future use in STEP2 Generate/Process */
/* NOTE: XbS is initialized with secret value in VLI format */
uECC_vli_modMult(XbS, Xb, XbS, curve_n, num_n_words);
exit:
return err;
}
WEAVE_ERROR EllipticCurveJPAKE::ProcessStep1(const uint8_t *buf, const uint16_t bufSize, uint16_t& stepDataLen)
{
WEAVE_ERROR err;
ECJPAKEStepPart stepPart;
/* Find Step1 (Part1) Data Pointers */
err = FindStepPartDataPointers(&stepPart, buf, bufSize, stepDataLen);
SuccessOrExit(err);
/* check Gxc is legal point */
VerifyOrExit(uECC_valid_point(stepPart.Gx, Curve), err = WEAVE_ERROR_INVALID_ARGUMENT);
/* verify ZKP(xc) */
err = VerifyZeroKnowledgeProof(&stepPart, uECC_curve_G(Curve), PeerName, PeerNameLen);
SuccessOrExit(err);
/* add Gxc to Gxacd and to Gxabc */
uECC_point_add(Gxacd, Gxacd, stepPart.Gx, Curve);
uECC_point_add(Gxabc, Gxabc, stepPart.Gx, Curve);
/* Find Step1 (Part2) Data Pointers */
err = FindStepPartDataPointers(&stepPart, buf, bufSize, stepDataLen);
SuccessOrExit(err);
/* check Gxd is legal point */
VerifyOrExit(uECC_valid_point(stepPart.Gx, Curve), err = WEAVE_ERROR_INVALID_ARGUMENT);
/* verify ZKP(xd) */
err = VerifyZeroKnowledgeProof(&stepPart, uECC_curve_G(Curve), PeerName, PeerNameLen);
SuccessOrExit(err);
/* add Gxd to Gxacd and store at Gxd */
uECC_point_add(Gxacd, Gxacd, stepPart.Gx, Curve);
uECC_point_set(Gxd, stepPart.Gx, uECC_curve_num_words(Curve));
exit:
return err;
}
WEAVE_ERROR EllipticCurveJPAKE::GenerateStep2(const uint8_t *buf, const uint16_t bufSize, uint16_t& stepDataLen)
{
WEAVE_ERROR err;
ECJPAKEStepPart stepPart;
/**
* For STEP2 the generator is:
* G2 = G * {xa + xc + xd} ( Gxacd )
* and the random value is:
* x2 = xb * s ( XbS )
*/
/* Find Step2 Data Fields */
err = FindStepPartDataPointers(&stepPart, buf, bufSize, stepDataLen);
SuccessOrExit(err);
/* Generate Step2 Data with ZKP(xb * s) */
err = GenerateStepPart(&stepPart, XbS, Gxacd, LocalName, LocalNameLen);
exit:
return err;
}
/* Gx = G * {(xc + xa + xb) * xd * s} */
void EllipticCurveJPAKE::ComputeSharedSecret(const EccPoint Gx)
{
SHA256 hash;
EccPoint ecPoint;
const wordcount_t num_words = uECC_curve_num_words(Curve);
/**
* K = (Gx - G * {xb * xd * s}) * xb
* = (G * {(xc + xa + xb) * xd * s - xb * xd * s}) * xb
* = (G * {(xc + xa) * xd * s}) * xb
* = G * {(xa + xc) * xb * xd * s}
* [which is the same regardless of who calculates it]
*/
/* ecPoint = G * {xb * xd * s} */
uECC_point_mult(ecPoint, Gxd, XbS, Curve);
/* ecPoint = - ecPoint */
uECC_vli_sub(ecPoint + num_words, uECC_curve_p(Curve), ecPoint + num_words, num_words);
/* ecPoint = Gx - G * {xb * xd * s} = G * {(xc + xa) * xd * s} */
uECC_point_add(ecPoint, Gx, ecPoint, Curve);
/* ecPoint = ecPoint * xb = G * {(xa + xc) * xb * xd * s} */
uECC_point_mult(ecPoint, ecPoint, Xb, Curve);
/* HASH ecPoint to get Shared Key */
hash.Begin();
hash.AddData((uint8_t *)ecPoint, 2 * kuECC_WordSize * uECC_curve_num_words(Curve));
hash.Finish(SharedSecret);
}
WEAVE_ERROR EllipticCurveJPAKE::ProcessStep2(const uint8_t *buf, const uint16_t bufSize, uint16_t& stepDataLen)
{
WEAVE_ERROR err;
ECJPAKEStepPart stepPart;
/* Find Step2 Data Fields Pointers */
err = FindStepPartDataPointers(&stepPart, buf, bufSize, stepDataLen);
SuccessOrExit(err);
/* verify ZKP(xd * s), where G' = G*{xc + xa + xb} = Gxabc */
err = VerifyZeroKnowledgeProof(&stepPart, Gxabc, PeerName, PeerNameLen);
SuccessOrExit(err);
/* Compute Key */
ComputeSharedSecret(stepPart.Gx);
exit:
return err;
}
uint8_t *EllipticCurveJPAKE::GetSharedSecret()
{
return SharedSecret;
}
int EllipticCurveJPAKE::GetCurveSize()
{
return (kuECC_WordSize * uECC_curve_num_words(Curve));
}
#endif /* WEAVE_IS_ECJPAKE_ENABLED */
} // namespace Crypto
} // namespace Weave
} // namespace nl
#endif /* WEAVE_CONFIG_USE_MICRO_ECC */