blob: e4dd0f03c3ffff8bb6270e25e7ca0fc1b334c939 [file] [log] [blame]
/*
*
* Copyright (c) 2013-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
* This file implements utility functions for reading, parsing,
* encoding, and decoding Weave key material.
*
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "weave-tool.h"
#include <Weave/Profiles/WeaveProfiles.h>
#include <Weave/Support/crypto/EllipticCurve.h>
#include <Weave/Profiles/security/WeaveSecurity.h>
#include <Weave/Profiles/security/WeavePrivateKey.h>
using namespace nl::Weave::Profiles;
using namespace nl::Weave::Profiles::Security;
using namespace nl::Weave::ASN1;
using namespace nl::Weave::TLV;
using namespace nl::Weave::Crypto;
bool ReadPrivateKey(const char *fileName, const char *prompt, EVP_PKEY *& key)
{
bool res = true;
uint8_t *keyData = NULL;
uint32_t keyDataLen;
key = NULL;
res = ReadFileIntoMem(fileName, keyData, keyDataLen);
if (!res)
ExitNow();
res = DecodePrivateKey(keyData, keyDataLen, kKeyFormat_Unknown, fileName, prompt, key);
exit:
if (keyData != NULL)
free(keyData);
return res;
}
bool ReadWeavePrivateKey(const char *fileName, uint8_t *& key, uint32_t &keyLen)
{
bool res = true;
KeyFormat keyFormat;
key = NULL;
keyLen = 0;
res = ReadFileIntoMem(fileName, key, keyLen);
if (!res)
ExitNow();
if (keyLen > MAX_KEY_SIZE)
{
fprintf(stderr, "weave: Error reading %s\nKey too large\n", fileName);
ExitNow(res = false);
}
keyFormat = DetectKeyFormat(key, keyLen);
if (keyFormat == kKeyFormat_Weave_Base64)
{
uint8_t *tmpKeyBuf = Base64Decode(key, keyLen, NULL, 0, keyLen);
if (tmpKeyBuf == NULL)
ExitNow(res = false);
free(key);
key = tmpKeyBuf;
}
else if (keyFormat != kKeyFormat_Weave_Raw)
{
fprintf(stderr, "weave: Error reading %s\nUnsupported private key format\n", fileName);
ExitNow(res = false);
}
exit:
if (!res && key != NULL)
{
free(key);
key = NULL;
keyLen = 0;
}
return res;
}
WEAVE_ERROR DecodeWeavePrivateKey(const uint8_t *encodedKeyBuf, uint32_t encodedKeyLen, EVP_PKEY *& key)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
TLVReader reader;
uint64_t tag;
reader.Init(encodedKeyBuf, encodedKeyLen);
err = reader.Next();
SuccessOrExit(err);
VerifyOrExit(reader.GetType() == kTLVType_Structure, err = WEAVE_ERROR_INVALID_ARGUMENT);
tag = reader.GetTag();
if (tag == ProfileTag(kWeaveProfile_Security, kTag_EllipticCurvePrivateKey))
{
uint32_t weaveCurveId;
EC_KEY *ecKey;
EncodedECPublicKey pubKey;
EncodedECPrivateKey privKey;
err = DecodeWeaveECPrivateKey(encodedKeyBuf, encodedKeyLen, weaveCurveId, pubKey, privKey);
SuccessOrExit(err);
err = DecodeECKey(WeaveCurveIdToOID(weaveCurveId), &privKey, &pubKey, ecKey);
SuccessOrExit(err);
key = EVP_PKEY_new();
VerifyOrExit(key != NULL, err = WEAVE_ERROR_NO_MEMORY);
EVP_PKEY_assign_EC_KEY(key, ecKey);
}
else if (tag == ProfileTag(kWeaveProfile_Security, kTag_RSAPrivateKey))
{
// TODO: implement support for RSA private keys.
ExitNow(err = WEAVE_ERROR_NOT_IMPLEMENTED);
}
else
ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT);
exit:
return err;
}
bool DecodePrivateKey(uint8_t *keyData, uint32_t keyDataLen, KeyFormat keyFormat, const char *keySource, const char *prompt, EVP_PKEY *& key)
{
bool res = true;
uint8_t *tmpKeyBuf = NULL;
BIO *keyBIO = NULL;
key = NULL;
if (keyFormat == kKeyFormat_Unknown)
keyFormat = DetectKeyFormat(keyData, keyDataLen);
if (keyFormat == kKeyFormat_Weave_Base64)
{
tmpKeyBuf = Base64Decode(keyData, keyDataLen, NULL, 0, keyDataLen);
if (tmpKeyBuf == NULL)
goto exit;
keyData = tmpKeyBuf;
keyFormat = kKeyFormat_Weave_Raw;
}
if (keyFormat == kKeyFormat_Weave_Raw)
{
WEAVE_ERROR err;
err = DecodeWeavePrivateKey(keyData, keyDataLen, key);
if (err != WEAVE_NO_ERROR)
{
fprintf(stderr, "Failed to decode Weave private key %s: %s\n", keySource, nl::ErrorStr(err));
ExitNow(res = false);
}
}
else
{
#ifndef OPENSSL_IS_BORINGSSL
EVP_set_pw_prompt(prompt);
#endif
keyBIO = BIO_new_mem_buf((void*)keyData, keyDataLen);
if (keyBIO == NULL)
{
fprintf(stderr, "Memory allocation error\n");
ExitNow(res = false);
}
if (keyFormat == kKeyFormat_PEM)
{
if (PEM_read_bio_PrivateKey(keyBIO, &key, NULL, NULL) == NULL)
{
fprintf(stderr, "Unable to read %s\n", keySource);
ReportOpenSSLErrorAndExit("PEM_read_bio_PrivateKey", res = false);
}
}
else
{
if (d2i_PrivateKey_bio(keyBIO, &key) == NULL)
{
fprintf(stderr, "Unable to read %s\n", keySource);
ReportOpenSSLErrorAndExit("d2i_PrivateKey_bio", res = false);
}
}
}
exit:
if (tmpKeyBuf != NULL)
free(tmpKeyBuf);
if (keyBIO != NULL)
BIO_free_all(keyBIO);
return res;
}
bool DetectKeyFormat(FILE *keyFile, KeyFormat& keyFormat)
{
uint8_t keyData[16];
int lenRead;
fseek(keyFile, 0, SEEK_SET);
lenRead = fread(keyData, 1, sizeof(keyData), keyFile);
if (lenRead < 0)
{
// TODO: handle error
}
fseek(keyFile, 0, SEEK_SET);
keyFormat = DetectKeyFormat(keyData, (uint32_t)lenRead);
return true;
}
KeyFormat DetectKeyFormat(uint8_t *key, uint32_t keyLen)
{
static const uint8_t ecWeaveRawPrefix[] = { 0xD5, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00 };
static const char *ecWeaveB64Prefix = "1QAABAAC";
static const uint32_t ecWeaveB64PrefixLen = sizeof(ecWeaveB64Prefix) - 1;
static const char *ecPEMMarker = "-----BEGIN EC PRIVATE KEY-----";
static const uint8_t rsaWeaveRawPrefix[] = { 0xD5, 0x00, 0x00, 0x04, 0x00, 0x03, 0x00 };
static const char *rsaWeaveB64Prefix = "1QAABAAD";
static const uint32_t rsaWeaveB64PrefixLen = sizeof(rsaWeaveB64Prefix) - 1;
static const char *rsaPEMMarker = "-----BEGIN RSA PRIVATE KEY-----";
static const char *genPEMMarker = "-----BEGIN PRIVATE KEY-----";
if (keyLen > sizeof(ecWeaveRawPrefix) && memcmp(key, ecWeaveRawPrefix, sizeof(ecWeaveRawPrefix)) == 0)
return kKeyFormat_Weave_Raw;
if (keyLen > sizeof(rsaWeaveRawPrefix) && memcmp(key, rsaWeaveRawPrefix, sizeof(rsaWeaveB64Prefix)) == 0)
return kKeyFormat_Weave_Raw;
if (keyLen > ecWeaveB64PrefixLen && memcmp(key, ecWeaveB64Prefix, ecWeaveB64PrefixLen) == 0)
return kKeyFormat_Weave_Base64;
if (keyLen > rsaWeaveB64PrefixLen && memcmp(key, rsaWeaveB64Prefix, rsaWeaveB64PrefixLen) == 0)
return kKeyFormat_Weave_Base64;
if (ContainsPEMMarker(ecPEMMarker, key, keyLen))
return kKeyFormat_PEM;
if (ContainsPEMMarker(rsaPEMMarker, key, keyLen))
return kKeyFormat_PEM;
if (ContainsPEMMarker(genPEMMarker, key, keyLen))
return kKeyFormat_PEM;
return kKeyFormat_DER;
}
bool GenerateKeyPair(const char *curveName, EVP_PKEY *& key)
{
bool res = true;
EC_KEY *ecKey = NULL;
EC_GROUP *ecGroup = NULL;
int curveNID;
key = NULL;
curveNID = OBJ_sn2nid(curveName);
if (curveNID == 0)
{
fprintf(stderr, "Unknown elliptic curve: %s\n", curveName);
ExitNow(res = false);
}
ecGroup = EC_GROUP_new_by_curve_name(curveNID);
if (ecGroup == NULL)
ReportOpenSSLErrorAndExit("EC_GROUP_new_by_curve_name", res = false);
// Only include the curve name in the ASN.1 encoding of the public key.
EC_GROUP_set_asn1_flag(ecGroup, OPENSSL_EC_NAMED_CURVE);
ecKey = EC_KEY_new();
if (ecKey == NULL)
ReportOpenSSLErrorAndExit("EC_KEY_new", res = false);
if (EC_KEY_set_group(ecKey, ecGroup) == 0)
ReportOpenSSLErrorAndExit("EC_KEY_set_group", res = false);
if (!EC_KEY_generate_key(ecKey))
ReportOpenSSLErrorAndExit("EC_KEY_generate_key", res = false);
key = EVP_PKEY_new();
if (key == NULL)
ReportOpenSSLErrorAndExit("EVP_PKEY_new", res = false);
if (!EVP_PKEY_assign_EC_KEY(key, ecKey))
ReportOpenSSLErrorAndExit("EVP_PKEY_assign_EC_KEY", res = false);
ecKey = NULL;
exit:
if (ecKey != NULL)
EC_KEY_free(ecKey);
if (key != NULL && !res)
{
EVP_PKEY_free(key);
key = NULL;
}
return res;
}
bool EncodePrivateKey(EVP_PKEY *key, KeyFormat keyFormat, uint8_t *& encodedKey, uint32_t& encodedKeyLen)
{
bool res = true;
BIO *outBIO = NULL;
BUF_MEM *memBuf;
encodedKey = NULL;
if (keyFormat == kKeyFormat_DER || keyFormat == kKeyFormat_PEM)
{
if (EVP_PKEY_type(EVP_PKEY_base_id(key)) == EVP_PKEY_EC)
{
EC_KEY *ecKey = EVP_PKEY_get1_EC_KEY(key);
EC_KEY_set_enc_flags(ecKey, EC_PKEY_NO_PARAMETERS);
}
outBIO = BIO_new(BIO_s_mem());
if (outBIO == NULL)
{
fprintf(stderr, "Memory allocation error\n");
ExitNow(res = false);
}
if (keyFormat == kKeyFormat_DER)
{
if (i2d_PrivateKey_bio(outBIO, key) < 0)
ReportOpenSSLErrorAndExit("i2d_PrivateKey_bio", res = false);
}
else
{
if (!PEM_write_bio_PrivateKey(outBIO, key, NULL, NULL, 0, NULL, NULL))
ReportOpenSSLErrorAndExit("PEM_write_bio_PrivateKey", res = false);
}
BIO_get_mem_ptr(outBIO, &memBuf);
encodedKey = (uint8_t *)malloc(memBuf->length);
if (encodedKey == NULL)
{
fprintf(stderr, "Memory allocation error\n");
ExitNow(res = false);
}
memcpy(encodedKey, memBuf->data, memBuf->length);
encodedKeyLen = memBuf->length;
}
else
{
uint8_t *tmpEncodedKey;
uint32_t tmpEncodedKeyLen;
if (EVP_PKEY_type(EVP_PKEY_base_id(key)) == EVP_PKEY_RSA)
{
fprintf(stderr, "Encoding of RSA private keys not yet supported\n");
ExitNow(res = false);
}
else if (EVP_PKEY_type(EVP_PKEY_base_id(key)) == EVP_PKEY_EC)
{
EC_KEY *ecKey = EVP_PKEY_get1_EC_KEY(key);
res = WeaveEncodeECPrivateKey(ecKey, true, tmpEncodedKey, tmpEncodedKeyLen);
VerifyOrExit(res == true, (void)0);
}
else
{
fprintf(stderr, "Unsupported private key type\n");
ExitNow(res = false);
}
if (keyFormat == kKeyFormat_Weave_Raw)
{
encodedKey = tmpEncodedKey;
encodedKeyLen = tmpEncodedKeyLen;
}
else
{
encodedKey = Base64Encode(tmpEncodedKey, tmpEncodedKeyLen, NULL, 0, encodedKeyLen);
free(tmpEncodedKey);
if (encodedKey == NULL)
{
fprintf(stderr, "Memory allocation failure\n");
ExitNow(res = false);
}
}
}
exit:
if (outBIO != NULL)
BIO_free(outBIO); // FIXME: possible memory leak of BUF_MEM???
return res;
}
// TODO: remove this
bool WeaveEncodePrivateKey(EVP_PKEY *key, uint8_t *& encodedKey, uint32_t& encodedKeyLen)
{
bool res = true;
if (EVP_PKEY_type(EVP_PKEY_base_id(key)) == EVP_PKEY_RSA)
{
fprintf(stderr, "Encoding of RSA private keys not yet supported\n");
ExitNow(res = false);
}
else if (EVP_PKEY_type(EVP_PKEY_base_id(key)) == EVP_PKEY_EC)
{
EC_KEY *ecKey = EVP_PKEY_get1_EC_KEY(key);
res = WeaveEncodeECPrivateKey(ecKey, true, encodedKey, encodedKeyLen);
}
else
{
fprintf(stderr, "Unsupported private key type\n");
ExitNow(res = false);
}
exit:
return res;
}
bool WeaveEncodeECPrivateKey(EC_KEY *key, bool includePubKey, uint8_t *& encodedKey, uint32_t& encodedKeyLen)
{
bool res = true;
WEAVE_ERROR err;
EncodedECPublicKey encodedPubKey;
EncodedECPrivateKey encodedPrivKey;
uint32_t encodedKeyBufLen;
OID curveOID;
encodedPubKey.ECPoint = NULL;
encodedPrivKey.PrivKey = NULL;
encodedKey = NULL;
curveOID = NIDToWeaveOID(EC_GROUP_get_curve_name(EC_KEY_get0_group(key)));
if (curveOID == kOID_Unknown)
{
fprintf(stderr, "Unsupported elliptic curve: %d\n", EC_GROUP_get_curve_name(EC_KEY_get0_group(key)));
ExitNow(res = false);
}
encodedPrivKey.PrivKeyLen = (uint16_t)BN_num_bytes(EC_KEY_get0_private_key(key));
encodedPrivKey.PrivKey = (uint8_t *)malloc(encodedPrivKey.PrivKeyLen);
if (encodedPrivKey.PrivKey == NULL)
{
fprintf(stderr, "Memory allocation failure\n");
ExitNow(res = false);
}
if (!BN_bn2bin(EC_KEY_get0_private_key(key), encodedPrivKey.PrivKey))
ReportOpenSSLErrorAndExit("BN_bn2bin", res = false);
if (includePubKey)
{
encodedPubKey.ECPointLen = (uint32_t)EC_POINT_point2oct(EC_KEY_get0_group(key), EC_KEY_get0_public_key(key), POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
encodedPubKey.ECPoint = (uint8_t *)malloc(encodedPubKey.ECPointLen);
if (encodedPubKey.ECPoint == NULL)
{
fprintf(stderr, "Memory allocation failure\n");
ExitNow(res = false);
}
if (EC_POINT_point2oct(EC_KEY_get0_group(key), EC_KEY_get0_public_key(key), POINT_CONVERSION_UNCOMPRESSED, encodedPubKey.ECPoint, encodedPubKey.ECPointLen, NULL) == 0)
ReportOpenSSLErrorAndExit("EC_POINT_point2oct", res = false);
}
else
encodedPubKey.ECPointLen = 0;
encodedKeyBufLen = 64 + encodedPrivKey.PrivKeyLen + encodedPubKey.ECPointLen;
encodedKey = (uint8_t *)malloc(encodedKeyBufLen);
if (encodedKey == NULL)
{
fprintf(stderr, "Memory allocation failure\n");
ExitNow(res = false);
}
err = EncodeWeaveECPrivateKey(OIDToWeaveCurveId(curveOID), &encodedPubKey, encodedPrivKey, encodedKey, encodedKeyBufLen, encodedKeyLen);
if (err != WEAVE_NO_ERROR)
{
fprintf(stderr, "Failed to Weave encode EC private key: %s\n", nl::ErrorStr(err));
ExitNow(res = false);
}
exit:
if (encodedPrivKey.PrivKey != NULL)
free(encodedPrivKey.PrivKey);
if (encodedPubKey.ECPoint != NULL)
free(encodedPubKey.ECPoint);
if (encodedKey != NULL && !res)
{
free(encodedKey);
encodedKey = NULL;
}
return res;
}