blob: af710190be7094fa31ba308a1befd8b0862b2b2e [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
* OpenSSL implementations of elliptic curve functions used by Weave security code.
*
*/
#ifndef OPENSSL_EXPERIMENTAL_ECJPAKE
#define OPENSSL_EXPERIMENTAL_ECJPAKE
#endif
#include "WeaveCrypto.h"
#include "EllipticCurve.h"
#include <Weave/Core/WeaveEncoding.h>
#include <Weave/Support/CodeUtils.h>
#if WEAVE_WITH_OPENSSL
#include <string.h>
#include <openssl/obj_mac.h>
#if WEAVE_CONFIG_ALLOW_NON_STANDARD_ELLIPTIC_CURVES
#include <openssl/objects.h>
#endif
#if WEAVE_IS_ECJPAKE_ENABLED
#include <openssl/ecjpake.h>
#endif
namespace nl {
namespace Weave {
namespace Crypto {
using namespace nl::Weave::ASN1;
using namespace nl::Weave::Encoding;
// ============================================================
// OpenSSL implementations of primary elliptic curve functions
// used by Weave security code.
// ============================================================
#if WEAVE_CONFIG_USE_OPENSSL_ECC
// Generate an ECDSA signature given a message hash and a EC private key.
NL_DLL_EXPORT WEAVE_ERROR GenerateECDSASignature(OID curveOID,
const uint8_t *msgHash, uint8_t msgHashLen,
const EncodedECPrivateKey& encodedPrivKey,
EncodedECDSASignature& encodedSig)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
EC_KEY *key = NULL;
ECDSA_SIG *ecSig = NULL;
// Decode the private key into a EC_KEY object.
err = DecodeECKey(curveOID, &encodedPrivKey, NULL, key);
SuccessOrExit(err);
// Generate the signature for the given message hash.
ecSig = ECDSA_do_sign(msgHash, msgHashLen, key);
VerifyOrExit(ecSig != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT); // TODO: use better error
// Encode the signature into the output structure.
err = EncodeECDSASignature(ecSig, encodedSig);
SuccessOrExit(err);
exit:
ECDSA_SIG_free(ecSig);
EC_KEY_free(key);
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 = WEAVE_NO_ERROR;
EC_KEY *key = NULL;
ECDSA_SIG *ecSig = NULL;
// Decode the private key into a EC_KEY object.
err = DecodeECKey(curveOID, &encodedPrivKey, NULL, key);
SuccessOrExit(err);
// Generate the signature for the given message hash.
ecSig = ECDSA_do_sign(msgHash, msgHashLen, key);
VerifyOrExit(ecSig != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT); // TODO: use better error
// Encode the signature into the output structure.
err = ECDSASigToFixedLenSig(curveOID, ecSig, fixedLenSig);
SuccessOrExit(err);
exit:
ECDSA_SIG_free(ecSig);
EC_KEY_free(key);
return err;
}
// Verify an ECDSA signature.
NL_DLL_EXPORT WEAVE_ERROR VerifyECDSASignature(OID curveOID,
const uint8_t *msgHash, uint8_t msgHashLen,
const EncodedECDSASignature& encodedSig,
const EncodedECPublicKey& encodedPubKey)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
EC_KEY *pubKey = NULL;
ECDSA_SIG *sig = NULL;
int res;
// Decode the public key into a EC_KEY object.
err = DecodeECKey(curveOID, NULL, &encodedPubKey, pubKey);
SuccessOrExit(err);
err = DecodeECDSASignature(encodedSig, sig);
SuccessOrExit(err);
res = ECDSA_do_verify(msgHash, msgHashLen, sig, pubKey);
VerifyOrExit(res == 1, err = WEAVE_ERROR_INVALID_SIGNATURE);
exit:
ECDSA_SIG_free(sig);
EC_KEY_free(pubKey);
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;
EC_KEY *pubKey = NULL;
ECDSA_SIG *sig = NULL;
int res;
// Decode the public key into a EC_KEY object.
err = DecodeECKey(curveOID, NULL, &encodedPubKey, pubKey);
SuccessOrExit(err);
// Convert fixed-length signature into a ECDSA_SIG object.
err = FixedLenSigToECDSASig(curveOID, fixedLenSig, sig);
SuccessOrExit(err);
// Verify the signature for the given message hash.
res = ECDSA_do_verify(msgHash, msgHashLen, sig, pubKey);
VerifyOrExit(res == 1, err = WEAVE_ERROR_INVALID_SIGNATURE);
exit:
ECDSA_SIG_free(sig);
EC_KEY_free(pubKey);
return err;
}
// Generate a public/private key pair suitable for Elliptic Curve Diffie-Hellman.
WEAVE_ERROR GenerateECDHKey(OID curveOID, EncodedECPublicKey& encodedPubKey, EncodedECPrivateKey& encodedPrivKey)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
EC_GROUP *ecGroup = NULL;
EC_KEY *key = NULL;
const BIGNUM *privKey;
int res, privKeyLen;
err = GetECGroupForCurve(curveOID, ecGroup);
SuccessOrExit(err);
key = EC_KEY_new();
VerifyOrExit(key != NULL, err = WEAVE_ERROR_NO_MEMORY);
res = EC_KEY_set_group(key, ecGroup);
VerifyOrExit(res, err = WEAVE_ERROR_NO_MEMORY);
res = EC_KEY_generate_key(key);
VerifyOrExit(res, err = WEAVE_ERROR_NO_MEMORY); // TODO: map OpenSSL error
err = EncodeX962ECPoint(curveOID, ecGroup, EC_KEY_get0_public_key(key), encodedPubKey.ECPoint, encodedPubKey.ECPointLen, encodedPubKey.ECPointLen);
SuccessOrExit(err);
privKey = EC_KEY_get0_private_key(key);
privKeyLen = BN_num_bytes(privKey);
VerifyOrExit(encodedPrivKey.PrivKeyLen >= privKeyLen, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
BN_bn2bin(privKey, encodedPrivKey.PrivKey);
encodedPrivKey.PrivKeyLen = privKeyLen;
exit:
EC_GROUP_free(ecGroup);
EC_KEY_free(key);
return err;
}
WEAVE_ERROR ECDHComputeSharedSecret(OID curveOID, const EncodedECPublicKey& encodedPubKey, const EncodedECPrivateKey& encodedPrivKey,
uint8_t *sharedSecretBuf, uint16_t sharedSecretBufSize, uint16_t& sharedSecretLen)
{
WEAVE_ERROR err;
EC_GROUP *ecGroup = NULL;
EC_POINT *pubKey = NULL;
BIGNUM *privKey = NULL;
err = GetECGroupForCurve(curveOID, ecGroup);
SuccessOrExit(err);
err = DecodeX962ECPoint(encodedPubKey.ECPoint, encodedPubKey.ECPointLen, ecGroup, pubKey);
SuccessOrExit(err);
privKey = BN_bin2bn(encodedPrivKey.PrivKey, encodedPrivKey.PrivKeyLen, NULL);
VerifyOrExit(privKey != NULL, err = WEAVE_ERROR_NO_MEMORY);
err = ECDHComputeSharedSecret(curveOID, ecGroup, pubKey, privKey, sharedSecretBuf, sharedSecretBufSize, sharedSecretLen);
SuccessOrExit(err);
exit:
BN_clear_free(privKey);
EC_POINT_free(pubKey);
EC_GROUP_free(ecGroup);
return err;
}
int GetCurveSize(const OID curveOID)
{
int curveSize = 0;
EC_GROUP *ecGroup = NULL;
if (GetECGroupForCurve(curveOID, ecGroup) == WEAVE_NO_ERROR)
curveSize = GetCurveSize(curveOID, ecGroup);
EC_GROUP_free(ecGroup);
return curveSize;
}
WEAVE_ERROR GetCurveG(OID curveOID, EncodedECPublicKey& encodedG)
{
WEAVE_ERROR err;
EC_GROUP *ecGroup = NULL;
err = GetECGroupForCurve(curveOID, ecGroup);
SuccessOrExit(err);
err = EncodeX962ECPoint(curveOID, ecGroup, EC_GROUP_get0_generator(ecGroup), encodedG.ECPoint, encodedG.ECPointLen, encodedG.ECPointLen);
SuccessOrExit(err);
exit:
EC_GROUP_free(ecGroup);
return err;
}
#endif // WEAVE_CONFIG_USE_OPENSSL_ECC
// ============================================================
// OpenSSL-specific elliptic curve utility functions.
// ============================================================
// ECDSA_SIG_get0() and ECDSA_SIG_set0() methods were introduced in OpenSSL
// version 1.1.0; for older versions of OpenSSL these methods are defined here.
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
{
if (pr != NULL)
*pr = sig->r;
if (ps != NULL)
*ps = sig->s;
}
static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
{
if (r == NULL || s == NULL)
return 0;
BN_clear_free(sig->r);
BN_clear_free(sig->s);
sig->r = r;
sig->s = s;
return 1;
}
#endif // (OPENSSL_VERSION_NUMBER < 0x10100000L)
int GetCurveSize(const OID curveOID, const EC_GROUP *ecGroup)
{
return ((EC_GROUP_get_degree(ecGroup) + 7) / 8);
}
NL_DLL_EXPORT WEAVE_ERROR GetECGroupForCurve(OID curveOID, EC_GROUP *& ecGroup)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
int curveNID;
switch (curveOID)
{
case kOID_EllipticCurve_secp160r1:
curveNID = NID_secp160r1;
break;
case kOID_EllipticCurve_prime192v1:
curveNID = NID_X9_62_prime192v1;
break;
case kOID_EllipticCurve_secp224r1:
curveNID = NID_secp224r1;
break;
case kOID_EllipticCurve_prime256v1:
curveNID = NID_X9_62_prime256v1;
break;
default:
#if WEAVE_CONFIG_ALLOW_NON_STANDARD_ELLIPTIC_CURVES
{
ASN1_OBJECT *curveASN1Obj;
const uint8_t *encodedOID;
uint16_t encodedOIDLen;
if (!GetEncodedObjectID(curveOID, encodedOID, encodedOIDLen))
ExitNow(err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
curveASN1Obj = c2i_ASN1_OBJECT(&curveASN1Obj, &encodedOID, encodedOIDLen);
VerifyOrExit(curveASN1Obj != NULL, err = WEAVE_ERROR_NO_MEMORY);
curveNID = OBJ_obj2nid(curveASN1Obj);
ASN1_OBJECT_free(curveASN1Obj)
VerifyOrExit(curveNID != NID_undef, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
}
#endif
case kOID_Unknown:
ExitNow(err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
break;
}
ecGroup = EC_GROUP_new_by_curve_name(curveNID);
VerifyOrExit(ecGroup != NULL, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
// Only include the curve name when generating an ASN.1 encoding of a public key.
EC_GROUP_set_asn1_flag(ecGroup, OPENSSL_EC_NAMED_CURVE);
exit:
return err;
}
// Perform the Elliptic Curve Diffie-Hellman computation to generate a shared secret
// from a EC public key and a private key.
NL_DLL_EXPORT WEAVE_ERROR ECDHComputeSharedSecret(OID curveOID, const EC_GROUP *ecGroup,
const EC_POINT *pubKeyPoint, const BIGNUM *privKeyBN,
uint8_t *sharedSecretBuf, uint16_t sharedSecretBufSize, uint16_t& sharedSecretLen)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
EC_POINT *sharedSecretPoint = NULL;
BIGNUM *sharedSecretX = NULL;
BIGNUM *sharedSecretY = NULL;
int sharedSecretXLen;
// Determine the output size of the shared key in bytes. This is equal to the size
// of the curve prime.
sharedSecretLen = GetCurveSize(curveOID, ecGroup);
VerifyOrExit(sharedSecretLen != 0, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
VerifyOrExit(sharedSecretLen <= sharedSecretBufSize, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
// Create a EC_POINT object to hold the shared key point.
sharedSecretPoint = EC_POINT_new(ecGroup);
VerifyOrExit(sharedSecretPoint != NULL, err = WEAVE_ERROR_NO_MEMORY); // TODO: translate OpenSSL error
// Multiply the public key point by the private key number to produce the shared key point.
// (This is the crux of the EC Diffie-Hellman algorithm).
if (!EC_POINT_mul(ecGroup, sharedSecretPoint, NULL, pubKeyPoint, privKeyBN, NULL))
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT); // TODO: translate OpenSSL error
// Extract the coordinate values from the shared key point.
sharedSecretX = BN_new();
VerifyOrExit(sharedSecretX != NULL, err = WEAVE_ERROR_NO_MEMORY); // TODO: translate OpenSSL error
sharedSecretY = BN_new();
VerifyOrExit(sharedSecretX != NULL, err = WEAVE_ERROR_NO_MEMORY); // TODO: translate OpenSSL error
if (!EC_POINT_get_affine_coordinates_GFp(ecGroup, sharedSecretPoint, sharedSecretX, sharedSecretY, NULL))
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT); // TODO: translate OpenSSL error
// Determine the size in bytes of the shared key X coordinate.
sharedSecretXLen = BN_num_bytes(sharedSecretX);
VerifyOrExit(sharedSecretXLen <= (int)sharedSecretLen, err = WEAVE_ERROR_INVALID_ARGUMENT);
// Convert the shared key X coordinate to a big-endian byte array, padded on the left with 0s
// to the shared key output size.
memset(sharedSecretBuf, 0, sharedSecretLen);
if (sharedSecretXLen != (int)BN_bn2bin(sharedSecretX, sharedSecretBuf + sharedSecretLen - sharedSecretXLen))
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT); // TODO: translate OpenSSL error
exit:
BN_clear_free(sharedSecretX);
BN_clear_free(sharedSecretY);
EC_POINT_clear_free(sharedSecretPoint);
return err;
}
NL_DLL_EXPORT WEAVE_ERROR EncodeX962ECPoint(OID curveOID, EC_GROUP *ecGroup, const EC_POINT *point, uint8_t *buf, uint16_t bufSize, uint16_t& encodedPointLen)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
BIGNUM *x = NULL;
BIGNUM *y = NULL;
int fieldLen, valLen;
// Encoding point at infinity not supported.
VerifyOrExit(!EC_POINT_is_at_infinity(ecGroup, point), err = WEAVE_ERROR_INVALID_ARGUMENT);
// Determine the encoded size of the point's fields. This is equal to the size of the curve prime.
fieldLen = GetCurveSize(curveOID, ecGroup);
VerifyOrExit(fieldLen != 0, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
// Verify that the buffer is big enough.
encodedPointLen = 1 + (fieldLen * 2);
VerifyOrExit(bufSize >= encodedPointLen, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
// Get the point's coordinates.
x = BN_new();
VerifyOrExit(x != NULL, err = WEAVE_ERROR_NO_MEMORY);
y = BN_new();
VerifyOrExit(y != NULL, err = WEAVE_ERROR_NO_MEMORY);
if (!EC_POINT_get_affine_coordinates_GFp(ecGroup, point, x, y, NULL))
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT); // TODO: translate OpenSSL error
// Clear the output buffer. This primes it with zeros in case one of the coordinates
// is less than fieldLen long, in which case the zeros act as padding.
memset(buf, 0, encodedPointLen);
// Encode the format byte (0x04 = uncompressed point).
buf[0] = kX963EncodedPointFormat_Uncompressed;
// Encode the X value right aligned in the field length.
valLen = BN_num_bytes(x);
VerifyOrExit(valLen <= fieldLen, err = WEAVE_ERROR_INVALID_ARGUMENT);
BN_bn2bin(x, &buf[1 + fieldLen - valLen]);
// Encode the Y value, also right aligned.
valLen = BN_num_bytes(y);
VerifyOrExit(valLen <= fieldLen, err = WEAVE_ERROR_INVALID_ARGUMENT);
BN_bn2bin(y, &buf[1 + fieldLen + fieldLen - valLen]);
exit:
BN_free(x);
BN_free(y);
return err;
}
NL_DLL_EXPORT WEAVE_ERROR DecodeX962ECPoint(const uint8_t *encodedPoint, uint16_t encodedPointLen, EC_GROUP *group, EC_POINT *& point)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
BIGNUM *x = NULL;
BIGNUM *y = NULL;
point = NULL;
err = DecodeX962ECPoint(encodedPoint, encodedPointLen, x, y);
SuccessOrExit(err);
point = EC_POINT_new(group);
VerifyOrExit(point != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT); // TODO: translate OpenSSL error
// TODO: validate point is on curve
if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, NULL))
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT); // TODO: translate OpenSSL error
exit:
if (err != WEAVE_NO_ERROR && point != NULL)
{
EC_POINT_free(point);
point = NULL;
}
BN_free(x);
BN_free(y);
return err;
}
WEAVE_ERROR DecodeX962ECPoint(const uint8_t *encodedPoint, uint16_t encodedPointLen, BIGNUM *& x, BIGNUM *& y)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint16_t fieldSize;
x = y = NULL;
VerifyOrExit(encodedPointLen >= 3, err = WEAVE_ERROR_INVALID_ARGUMENT);
// Verify that the encoding is an uncompressed point (0x04). Point-at-infinity (0x00) and compressed
// points (0x02, 0x03) are not supported.
VerifyOrExit(*encodedPoint == kX963EncodedPointFormat_Uncompressed, err = WEAVE_ERROR_INVALID_ARGUMENT);
// Length must be odd (encoding byte plus x and y coordinates, each the same size).
VerifyOrExit((encodedPointLen & 1) == 1, err = WEAVE_ERROR_INVALID_ARGUMENT);
fieldSize = (encodedPointLen - 1) / 2;
x = BN_bin2bn(encodedPoint + 1, fieldSize, NULL);
VerifyOrExit(x != NULL, err = WEAVE_ERROR_NO_MEMORY);
y = BN_bin2bn(encodedPoint + 1 + fieldSize, fieldSize, NULL);
VerifyOrExit(x != NULL, err = WEAVE_ERROR_NO_MEMORY);
exit:
if (err != WEAVE_NO_ERROR)
{
BN_free(x);
x = NULL;
BN_free(y);
y = NULL;
}
return err;
}
// Decode an elliptic curve private key or/and public key in X9.62 format, and return an EC_KEY object.
NL_DLL_EXPORT WEAVE_ERROR DecodeECKey(OID curveOID, const EncodedECPrivateKey *encodedPrivKey, const EncodedECPublicKey *encodedPubKey, EC_KEY *& ecKey)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
EC_GROUP *ecGroup = NULL;
EC_POINT *pubKeyPoint = NULL;
BIGNUM *privKeyBN = NULL;
int res;
ecKey = NULL;
// Verify that at least one of the (public/private) key inputs is provided.
VerifyOrExit(encodedPrivKey != NULL || encodedPubKey != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
// TODO: optimize this to eliminate duplication of the EC_POINT and EC_GROUP objects.
err = GetECGroupForCurve(curveOID, ecGroup);
SuccessOrExit(err);
ecKey = EC_KEY_new();
VerifyOrExit(ecKey != NULL, err = WEAVE_ERROR_NO_MEMORY);
res = EC_KEY_set_group(ecKey, ecGroup);
VerifyOrExit(res, err = WEAVE_ERROR_NO_MEMORY);
if (encodedPubKey != NULL)
{
VerifyOrExit(encodedPubKey->ECPoint != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
err = DecodeX962ECPoint(encodedPubKey->ECPoint, encodedPubKey->ECPointLen, ecGroup, pubKeyPoint);
SuccessOrExit(err);
res = EC_KEY_set_public_key(ecKey, pubKeyPoint);
VerifyOrExit(res, err = WEAVE_ERROR_NO_MEMORY);
}
if (encodedPrivKey != NULL)
{
VerifyOrExit(encodedPrivKey->PrivKey != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
privKeyBN = BN_bin2bn(encodedPrivKey->PrivKey, encodedPrivKey->PrivKeyLen, NULL);
VerifyOrExit(privKeyBN != NULL, err = WEAVE_ERROR_NO_MEMORY);
res = EC_KEY_set_private_key(ecKey, privKeyBN);
VerifyOrExit(res, err = WEAVE_ERROR_NO_MEMORY);
}
exit:
BN_free(privKeyBN);
EC_POINT_free(pubKeyPoint);
if (err != WEAVE_NO_ERROR)
{
EC_KEY_free(ecKey);
ecKey = NULL;
}
EC_GROUP_free(ecGroup);
return err;
}
// Encode an OpenSSL ECDSA signature into a pair of buffers.
WEAVE_ERROR EncodeECDSASignature(const ECDSA_SIG *sig, EncodedECDSASignature& encodedSig)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
const BIGNUM *sigR;
const BIGNUM *sigS;
int valLen;
ECDSA_SIG_get0(sig, &sigR, &sigS);
valLen = BN_num_bytes(sigR);
VerifyOrExit(valLen <= encodedSig.RLen, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
BN_bn2bin(sigR, encodedSig.R);
if ((encodedSig.R[0] & 0x80) != 0)
{
VerifyOrExit(valLen < encodedSig.RLen, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
memmove(encodedSig.R + 1, encodedSig.R, valLen);
encodedSig.R[0] = 0;
encodedSig.RLen = valLen + 1;
}
else
encodedSig.RLen = valLen;
valLen = BN_num_bytes(sigS);
VerifyOrExit(valLen <= encodedSig.SLen, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
BN_bn2bin(sigS, encodedSig.S);
if ((encodedSig.S[0] & 0x80) != 0)
{
VerifyOrExit(valLen < encodedSig.SLen, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
memmove(encodedSig.S + 1, encodedSig.S, valLen);
encodedSig.S[0] = 0;
encodedSig.SLen = valLen + 1;
}
else
encodedSig.SLen = valLen;
exit:
return err;
}
// Decode an ECDSA signature consisting of r and s values encoded as a big-endian sequences of bytes.
WEAVE_ERROR DecodeECDSASignature(const EncodedECDSASignature& encodedSig, ECDSA_SIG *& sig)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
BIGNUM *sigR = NULL;
BIGNUM *sigS = NULL;
int retVal;
sig = ECDSA_SIG_new();
VerifyOrExit(sig != NULL, err = WEAVE_ERROR_NO_MEMORY);
sigR = BN_bin2bn(encodedSig.R, encodedSig.RLen, sigR);
VerifyOrExit(sigR != NULL, err = WEAVE_ERROR_NO_MEMORY);
sigS = BN_bin2bn(encodedSig.S, encodedSig.SLen, sigS);
VerifyOrExit(sigS != NULL, err = WEAVE_ERROR_NO_MEMORY);
retVal = ECDSA_SIG_set0(sig, sigR, sigS);
VerifyOrExit(retVal != 0, err = WEAVE_ERROR_NO_MEMORY);
exit:
if (err != WEAVE_NO_ERROR && sig != NULL)
{
ECDSA_SIG_free(sig);
sig = NULL;
}
return err;
}
// Convert an OpenSSL ECDSA signature into a fixed-length signature.
WEAVE_ERROR ECDSASigToFixedLenSig(OID curveOID, const ECDSA_SIG *ecSig, uint8_t *fixedLenSig)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
const BIGNUM *sigR;
const BIGNUM *sigS;
uint16_t fieldLen;
int valLen;
fieldLen = GetCurveSize(curveOID);
VerifyOrExit(fieldLen != 0, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
ECDSA_SIG_get0(ecSig, &sigR, &sigS);
// Clear the output buffer. This primes it with zeros in case one of the signature
// companents {r, s} is less than fieldLen long, in which case the zeros act as padding.
memset(fixedLenSig, 0, 2 * fieldLen);
// Encode the R value right aligned in the field length.
valLen = BN_num_bytes(sigR);
VerifyOrExit(valLen <= fieldLen, err = WEAVE_ERROR_INVALID_ARGUMENT);
BN_bn2bin(sigR, &fixedLenSig[fieldLen - valLen]);
// Encode the S value, also right aligned.
valLen = BN_num_bytes(sigS);
VerifyOrExit(valLen <= fieldLen, err = WEAVE_ERROR_INVALID_ARGUMENT);
BN_bn2bin(sigS, &fixedLenSig[2 * fieldLen - valLen]);
exit:
return err;
}
// Convert a fixed-length signature into an OpenSSL ECDSA signature format.
extern WEAVE_ERROR FixedLenSigToECDSASig(OID curveOID, const uint8_t *fixedLenSig, ECDSA_SIG *& ecSig)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
BIGNUM *sigR = NULL;
BIGNUM *sigS = NULL;
int retVal;
uint16_t fieldLen;
ecSig = ECDSA_SIG_new();
VerifyOrExit(ecSig != NULL, err = WEAVE_ERROR_NO_MEMORY);
fieldLen = GetCurveSize(curveOID);
VerifyOrExit(fieldLen != 0, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
sigR = BN_bin2bn(fixedLenSig, fieldLen, sigR);
VerifyOrExit(sigR != NULL, err = WEAVE_ERROR_NO_MEMORY);
sigS = BN_bin2bn(fixedLenSig + fieldLen, fieldLen, sigS);
VerifyOrExit(sigS != NULL, err = WEAVE_ERROR_NO_MEMORY);
retVal = ECDSA_SIG_set0(ecSig, sigR, sigS);
VerifyOrExit(retVal != 0, err = WEAVE_ERROR_NO_MEMORY);
exit:
if (err != WEAVE_NO_ERROR && ecSig != NULL)
{
ECDSA_SIG_free(ecSig);
ecSig = NULL;
}
return err;
}
// ============================================================
// Elliptic Curve JPAKE Class Functions
// ============================================================
#if WEAVE_IS_ECJPAKE_ENABLED && WEAVE_CONFIG_USE_OPENSSL_ECC
static int GetCurveWordCount(const EC_GROUP *ecGroup)
{
int curveNID = EC_GROUP_get_curve_name(ecGroup);
switch (curveNID)
{
#if WEAVE_CONFIG_SUPPORT_ELLIPTIC_CURVE_SECP160R1
case NID_secp160r1:
return 5;
#endif
#if WEAVE_CONFIG_SUPPORT_ELLIPTIC_CURVE_SECP192R1
case NID_X9_62_prime192v1:
return 6;
#endif
#if WEAVE_CONFIG_SUPPORT_ELLIPTIC_CURVE_SECP224R1
case NID_secp224r1:
return 7;
#endif
#if WEAVE_CONFIG_SUPPORT_ELLIPTIC_CURVE_SECP256R1
case NID_X9_62_prime256v1:
return 8;
#endif
default:
#if WEAVE_CONFIG_ALLOW_NON_STANDARD_ELLIPTIC_CURVES
return ((EC_GROUP_get_degree(ecGroup) + 31) / 32);
#else
return 0;
#endif
}
}
static int GetOrderWordCount(const EC_GROUP *ecGroup)
{
int curveNID = EC_GROUP_get_curve_name(ecGroup);
switch (curveNID)
{
#if WEAVE_CONFIG_SUPPORT_ELLIPTIC_CURVE_SECP160R1
case NID_secp160r1:
return 6;
#endif
#if WEAVE_CONFIG_SUPPORT_ELLIPTIC_CURVE_SECP192R1
case NID_X9_62_prime192v1:
return 6;
#endif
#if WEAVE_CONFIG_SUPPORT_ELLIPTIC_CURVE_SECP224R1
case NID_secp224r1:
return 7;
#endif
#if WEAVE_CONFIG_SUPPORT_ELLIPTIC_CURVE_SECP256R1
case NID_X9_62_prime256v1:
return 8;
#endif
default:
#if WEAVE_CONFIG_ALLOW_NON_STANDARD_ELLIPTIC_CURVES
BIGNUM *order = NULL;
int ret = 0;
if ((order = BN_new()) == NULL) goto err;
if (!EC_GROUP_get_order(ecGroup, order, NULL)) goto err;
ret = order->top;
err:
BN_free(order);
return ret;
#else
return 0;
#endif
}
}
static WEAVE_ERROR EncodeECPointValue(const EC_GROUP *ecGroup, const EC_POINT *ecPoint, const uint8_t wordCount, uint8_t *& p)
{
WEAVE_ERROR err;
BIGNUM *ecPointX = NULL;
BIGNUM *ecPointY = NULL;
ecPointX = BN_new();
VerifyOrExit(ecPointX != NULL, err = WEAVE_ERROR_NO_MEMORY);
ecPointY = BN_new();
VerifyOrExit(ecPointY != NULL, err = WEAVE_ERROR_NO_MEMORY);
if (!EC_POINT_get_affine_coordinates_GFp(ecGroup, ecPoint, ecPointX, ecPointY, NULL))
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT);
err = EncodeBIGNUMValueLE(*ecPointX, wordCount * sizeof(uint32_t), p);
SuccessOrExit(err);
err = EncodeBIGNUMValueLE(*ecPointY, wordCount * sizeof(uint32_t), p);
SuccessOrExit(err);
exit:
BN_free(ecPointY);
BN_free(ecPointX);
return err;
}
static WEAVE_ERROR DecodeECPointValue(const EC_GROUP *ecGroup, EC_POINT *ecPoint, const uint8_t wordCount, const uint8_t *& p)
{
WEAVE_ERROR err;
BIGNUM *ecPointX = NULL;
BIGNUM *ecPointY = NULL;
ecPointX = BN_new();
VerifyOrExit(ecPointX != NULL, err = WEAVE_ERROR_NO_MEMORY);
ecPointY = BN_new();
VerifyOrExit(ecPointY != NULL, err = WEAVE_ERROR_NO_MEMORY);
err = DecodeBIGNUMValueLE(*ecPointX, wordCount * sizeof(uint32_t), p);
SuccessOrExit(err);
err = DecodeBIGNUMValueLE(*ecPointY, wordCount * sizeof(uint32_t), p);
SuccessOrExit(err);
if (!EC_POINT_set_affine_coordinates_GFp(ecGroup, ecPoint, ecPointX, ecPointY, NULL))
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT);
exit:
BN_free(ecPointY);
BN_free(ecPointX);
return err;
}
static WEAVE_ERROR EncodeStepPartFields(const ECJPAKE_CTX *ctx, const ECJPAKE_STEP_PART *stepPart, const uint8_t *buf, const uint16_t bufSize, uint16_t& stepDataLen)
{
WEAVE_ERROR err;
uint8_t gWordCount, bWordCount;
uint8_t *p = (uint8_t *)buf + stepDataLen;
gWordCount = GetCurveWordCount(ECJPAKE_get_ecGroup(ctx));
VerifyOrExit(gWordCount != 0, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
bWordCount = GetOrderWordCount(ECJPAKE_get_ecGroup(ctx));
VerifyOrExit(bWordCount != 0, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
stepDataLen += (4 * gWordCount + bWordCount) * sizeof(uint32_t);
VerifyOrExit(stepDataLen <= bufSize, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
err = EncodeECPointValue(ECJPAKE_get_ecGroup(ctx), stepPart->Gx, gWordCount, p);
SuccessOrExit(err);
err = EncodeECPointValue(ECJPAKE_get_ecGroup(ctx), stepPart->zkpx.Gr, gWordCount, p);
SuccessOrExit(err);
err = EncodeBIGNUMValueLE(*stepPart->zkpx.b, bWordCount * sizeof(uint32_t), p);
SuccessOrExit(err);
exit:
return err;
}
static WEAVE_ERROR DecodeStepPartFields(const ECJPAKE_CTX *ctx, ECJPAKE_STEP_PART *stepPart, const uint8_t *buf, const uint16_t bufSize, uint16_t& stepDataLen)
{
WEAVE_ERROR err;
uint8_t gWordCount, bWordCount;
const uint8_t *p = buf + stepDataLen;
gWordCount = GetCurveWordCount(ECJPAKE_get_ecGroup(ctx));
VerifyOrExit(gWordCount != 0, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
bWordCount = GetOrderWordCount(ECJPAKE_get_ecGroup(ctx));
VerifyOrExit(bWordCount != 0, err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
stepDataLen += (4 * gWordCount + bWordCount) * sizeof(uint32_t);
VerifyOrExit(stepDataLen <= bufSize, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
err = DecodeECPointValue(ECJPAKE_get_ecGroup(ctx), stepPart->Gx, gWordCount, p);
SuccessOrExit(err);
err = DecodeECPointValue(ECJPAKE_get_ecGroup(ctx), stepPart->zkpx.Gr, gWordCount, p);
SuccessOrExit(err);
err = DecodeBIGNUMValueLE(*stepPart->zkpx.b, bWordCount * sizeof(uint32_t), p);
SuccessOrExit(err);
exit:
return err;
}
/* This function will be used by ECJPAKE to calculate SHA256 hash of Elliptic Curve Point */
static int ECJPAKE_HashECPoint(ECJPAKE_CTX *ctx, SHA256_CTX *sha, const EC_POINT *ecPoint)
{
WEAVE_ERROR err;
uint8_t fieldWordCount;
uint8_t *ecPointEncoded = NULL;
uint8_t *p = NULL;
int ret = 1;
fieldWordCount = GetCurveWordCount(ECJPAKE_get_ecGroup(ctx));
VerifyOrExit(fieldWordCount != 0, ret = 0);
ecPointEncoded = (uint8_t *)OPENSSL_malloc(2 * sizeof(uint32_t) * fieldWordCount);
VerifyOrExit(ecPointEncoded != NULL, ret = 0);
// Assign another pointer because the value is updated by EncodeECPointValue function
p = ecPointEncoded;
err = EncodeECPointValue(ECJPAKE_get_ecGroup(ctx), ecPoint, fieldWordCount, p);
VerifyOrExit(err == WEAVE_NO_ERROR, ret = 0);
SHA256_Update(sha, ecPointEncoded, 2 * sizeof(uint32_t) * fieldWordCount);
exit:
if (ecPointEncoded != NULL) OPENSSL_free(ecPointEncoded);
return ret;
}
void EllipticCurveJPAKE::Init()
{
ECJPAKECtx = NULL;
}
/* clear secret content of JPAKE CTX Struct */
void EllipticCurveJPAKE::Shutdown()
{
Reset();
}
/* clear secret content of JPAKE CTX Struct */
void EllipticCurveJPAKE::Reset()
{
if (ECJPAKECtx != NULL)
{
EC_GROUP *ecGroup = (EC_GROUP *)ECJPAKE_get_ecGroup(ECJPAKECtx);
if (ecGroup != NULL)
{
EC_GROUP_free(ecGroup);
}
ECJPAKE_CTX_free(ECJPAKECtx);
ECJPAKECtx = NULL;
}
}
/* Init JPAKE CTX Struct */
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;
EC_GROUP *ecGroup = NULL;
BIGNUM *secret = NULL;
// TODO: should we limit MAX length and check these inputs: pwLen, localNameLen, peerNameLen ?
err = GetECGroupForCurve(curveOID, ecGroup);
SuccessOrExit(err);
secret = BN_new();
VerifyOrExit(secret != NULL, err = WEAVE_ERROR_NO_MEMORY);
BN_bin2bn(pw, pwLen, secret);
ECJPAKECtx = ECJPAKE_CTX_new(ecGroup, secret, localName, localNameLen, peerName, peerNameLen);
VerifyOrExit(ECJPAKECtx != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Set ECJPAKE_HashECPoint function to be used by ECJPAKE for EC Point Hash
ECJPAKE_Set_HashECPoint(ECJPAKE_HashECPoint);
exit:
BN_clear_free(secret);
return err;
}
WEAVE_ERROR EllipticCurveJPAKE::GenerateStep1(const uint8_t *buf, const uint16_t bufSize, uint16_t& stepDataLen)
{
WEAVE_ERROR err;
ECJPAKE_STEP1 step1Data;
int res;
res = ECJPAKE_STEP1_init(&step1Data, ECJPAKECtx);
VerifyOrExit(res, err = WEAVE_ERROR_NO_MEMORY);
res = ECJPAKE_STEP1_generate(&step1Data, ECJPAKECtx);
VerifyOrExit(res, err = WEAVE_ERROR_NO_MEMORY);
err = EncodeStepPartFields(ECJPAKECtx, &step1Data.p1, buf, bufSize, stepDataLen);
SuccessOrExit(err);
err = EncodeStepPartFields(ECJPAKECtx, &step1Data.p2, buf, bufSize, stepDataLen);
SuccessOrExit(err);
exit:
ECJPAKE_STEP1_release(&step1Data);
return err;
}
WEAVE_ERROR EllipticCurveJPAKE::ProcessStep1(const uint8_t *buf, const uint16_t bufSize, uint16_t& stepDataLen)
{
WEAVE_ERROR err;
ECJPAKE_STEP1 step1Data;
int res;
res = ECJPAKE_STEP1_init(&step1Data, ECJPAKECtx);
VerifyOrExit(res, err = WEAVE_ERROR_NO_MEMORY);
err = DecodeStepPartFields(ECJPAKECtx, &step1Data.p1, buf, bufSize, stepDataLen);
SuccessOrExit(err);
err = DecodeStepPartFields(ECJPAKECtx, &step1Data.p2, buf, bufSize, stepDataLen);
SuccessOrExit(err);
res = ECJPAKE_STEP1_process(ECJPAKECtx, &step1Data);
VerifyOrExit(res, err = WEAVE_ERROR_INVALID_ARGUMENT);
exit:
ECJPAKE_STEP1_release(&step1Data);
return err;
}
WEAVE_ERROR EllipticCurveJPAKE::GenerateStep2(const uint8_t *buf, const uint16_t bufSize, uint16_t& stepDataLen)
{
WEAVE_ERROR err;
ECJPAKE_STEP2 step2Data;
int res;
res = ECJPAKE_STEP2_init(&step2Data, ECJPAKECtx);
VerifyOrExit(res, err = WEAVE_ERROR_NO_MEMORY);
res = ECJPAKE_STEP2_generate(&step2Data, ECJPAKECtx);
VerifyOrExit(res, err = WEAVE_ERROR_NO_MEMORY);
err = EncodeStepPartFields(ECJPAKECtx, &step2Data, buf, bufSize, stepDataLen);
SuccessOrExit(err);
exit:
ECJPAKE_STEP2_release(&step2Data);
return err;
}
WEAVE_ERROR EllipticCurveJPAKE::ProcessStep2(const uint8_t *buf, const uint16_t bufSize, uint16_t& stepDataLen)
{
WEAVE_ERROR err;
ECJPAKE_STEP2 step2Data;
int res;
res = ECJPAKE_STEP2_init(&step2Data, ECJPAKECtx);
VerifyOrExit(res, err = WEAVE_ERROR_NO_MEMORY);
err = DecodeStepPartFields(ECJPAKECtx, &step2Data, buf, bufSize, stepDataLen);
SuccessOrExit(err);
res = ECJPAKE_STEP2_process(ECJPAKECtx, &step2Data);
VerifyOrExit(res, err = WEAVE_ERROR_INVALID_ARGUMENT);
exit:
ECJPAKE_STEP2_release(&step2Data);
return err;
}
uint8_t *EllipticCurveJPAKE::GetSharedSecret()
{
return (uint8_t *)ECJPAKE_get_shared_key(ECJPAKECtx);
}
int EllipticCurveJPAKE::GetCurveSize()
{
return (4 * GetCurveWordCount(ECJPAKE_get_ecGroup(ECJPAKECtx)));
}
#endif /* WEAVE_IS_ECJPAKE_ENABLED && WEAVE_CONFIG_USE_OPENSSL_ECC */
} // namespace Crypto
} // namespace Weave
} // namespace nl
#endif /* WEAVE_WITH_OPENSSL */