| /* |
| * |
| * 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; |
| } |