blob: e71ba643386091c527e2a998fadcc0cf5b278fe4 [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 OpenSSL, base-64
* encoding and decoding, date and time parsing, EUI64 parsing,
* OID translation, and file reading.
*
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <stdint.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#include <time.h>
#include "weave-tool.h"
#include <Weave/Support/Base64.h>
using namespace nl::Weave::Profiles::Security;
using namespace nl::Weave::ASN1;
int gNIDWeaveDeviceId;
int gNIDWeaveServiceEndpointId;
int gNIDWeaveCAId;
int gNIDWeaveSoftwarePublisherId;
// OBJ_length() and OBJ_get0_data() 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 size_t OBJ_length(const ASN1_OBJECT *obj)
{
if (obj == NULL)
return 0;
return obj->length;
}
static const unsigned char *OBJ_get0_data(const ASN1_OBJECT *obj)
{
if (obj == NULL)
return NULL;
return obj->data;
}
#endif
bool InitOpenSSL()
{
bool res = true;
// OPENSSL_malloc_init() is a new method, which was first introduced
// in OpenSSL version 1.1.0. Older versions of OpenSSL implement
// equivalent method CRYPTO_malloc_init().
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
OPENSSL_malloc_init();
#else
CRYPTO_malloc_init();
#endif
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
gNIDWeaveDeviceId = OBJ_create("1.3.6.1.4.1.41387.1.1", "WeaveDeviceId", "WeaveDeviceId");
if (gNIDWeaveDeviceId == 0)
ReportOpenSSLErrorAndExit("OBJ_create", res = false);
gNIDWeaveServiceEndpointId = OBJ_create("1.3.6.1.4.1.41387.1.2", "WeaveServiceEndpointId", "WeaveServiceEndpointId");
if (gNIDWeaveServiceEndpointId == 0)
ReportOpenSSLErrorAndExit("OBJ_create", res = false);
gNIDWeaveCAId = OBJ_create("1.3.6.1.4.1.41387.1.3", "WeaveCAId", "WeaveCAId");
if (gNIDWeaveCAId == 0)
ReportOpenSSLErrorAndExit("OBJ_create", res = false);
gNIDWeaveSoftwarePublisherId = OBJ_create("1.3.6.1.4.1.41387.1.4", "WeaveSoftwarePublisherId", "WeaveSoftwarePublisherId");
if (gNIDWeaveSoftwarePublisherId == 0)
ReportOpenSSLErrorAndExit("OBJ_create", res = false);
ASN1_STRING_TABLE_add(gNIDWeaveDeviceId, 16, 16, B_ASN1_UTF8STRING, 0);
ASN1_STRING_TABLE_add(gNIDWeaveServiceEndpointId, 16, 16, B_ASN1_UTF8STRING, 0);
ASN1_STRING_TABLE_add(gNIDWeaveCAId, 16, 16, B_ASN1_UTF8STRING, 0);
ASN1_STRING_TABLE_add(gNIDWeaveSoftwarePublisherId, 16, 16, B_ASN1_UTF8STRING, 0);
exit:
return res;
}
OID NIDToWeaveOID(int nid)
{
const ASN1_OBJECT *encodedOID;
encodedOID = OBJ_nid2obj(nid);
if (encodedOID == NULL)
return kOID_Unknown;
return ParseObjectID(OBJ_get0_data(encodedOID), OBJ_length(encodedOID));
}
char *Base64Encode(const uint8_t *inData, uint32_t inDataLen)
{
uint32_t outDataLen;
return (char *)Base64Encode(inData, inDataLen, NULL, 0, outDataLen);
}
uint8_t *Base64Encode(const uint8_t *inData, uint32_t inDataLen, uint8_t *outBuf, uint32_t outBufSize, uint32_t& outDataLen)
{
uint8_t *res = NULL;
outDataLen = BASE64_ENCODED_LEN(inDataLen);
if (outBuf != NULL && (outDataLen + 1) > outBufSize)
{
fprintf(stderr, "Buffer overflow\n");
ExitNow(res = NULL);
}
res = (uint8_t *)malloc(outDataLen + 1);
if (res == NULL)
{
fprintf(stderr, "Memory allocation error\n");
ExitNow(res = NULL);
}
memset(res, 0xAB, outDataLen + 1);
outDataLen = nl::Base64Encode32(inData, inDataLen, (char *)res);
res[outDataLen] = 0;
if (outBuf != NULL)
{
memcpy(outBuf, res, outDataLen + 1);
free(res);
res = outBuf;
}
exit:
return res;
}
uint8_t *Base64Decode(const uint8_t *inData, uint32_t inDataLen, uint8_t *outBuf, uint32_t outBufSize, uint32_t& outDataLen)
{
uint8_t *res = NULL;
outDataLen = BASE64_MAX_DECODED_LEN(inDataLen);
if (outBuf != NULL)
{
if (outDataLen > outBufSize)
{
fprintf(stderr, "Buffer overflow\n");
ExitNow();
}
res = outBuf;
}
else
{
res = (uint8_t *)malloc(outDataLen);
if (res == NULL)
{
fprintf(stderr, "Memory allocation error\n");
ExitNow();
}
}
outDataLen = nl::Base64Decode32((const char *)inData, inDataLen, res);
if (outDataLen == UINT32_MAX)
{
fprintf(stderr, "Base-64 decode error\n");
if (res != outBuf)
free(res);
ExitNow(res = NULL);
}
exit:
return res;
}
bool ContainsPEMMarker(const char *marker, const uint8_t *data, uint32_t dataLen)
{
size_t markerLen = strlen(marker);
if (dataLen > markerLen)
for (uint32_t i = 0; i <= dataLen - markerLen; i++)
if (strncmp((char *)data + i, marker, markerLen) == 0)
return true;
return false;
}
bool ParseEUI64(const char *str, uint64_t& nodeId)
{
char *parseEnd;
errno = 0;
nodeId = strtoull(str, &parseEnd, 16);
return parseEnd > str && *parseEnd == 0 && (nodeId != ULLONG_MAX || errno == 0);
}
bool ParseDateTime(const char *str, struct tm& date)
{
const char *p;
memset(&date, 0, sizeof(date));
if ((p = strptime(str, "%Y-%m-%d %H:%M:%S", &date)) == NULL &&
(p = strptime(str, "%Y/%m/%d %H:%M:%S", &date)) == NULL &&
(p = strptime(str, "%Y%m%d%H%M%SZ", &date)) == NULL &&
(p = strptime(str, "%Y-%m-%d", &date)) == NULL &&
(p = strptime(str, "%Y/%m/%d", &date)) == NULL &&
(p = strptime(str, "%Y%m%d", &date)) == NULL)
{
return false;
}
if (*p != 0)
{
return false;
}
return true;
}
bool ReadFileIntoMem(const char *fileName, uint8_t *& data, uint32_t &dataLen)
{
bool res = true;
FILE *file = NULL;
long int fileLen;
size_t readRes;
data = NULL;
dataLen = 0;
file = fopen(fileName, "r");
if (file == NULL)
{
fprintf(stderr, "weave: Unable to open %s: %s\n", fileName, strerror(errno));
ExitNow(res = false);
}
fseek(file, 0, SEEK_END);
fileLen = ftell(file);
fseek(file, 0, SEEK_SET);
if (fileLen < 0 || ferror(file))
{
fprintf(stderr, "weave: Error reading %s: %s\n", fileName, strerror(errno));
ExitNow(res = false);
}
dataLen = (uint32_t)fileLen;
data = (uint8_t *)malloc((size_t)dataLen);
if (data == NULL)
{
fprintf(stderr, "weave: Error reading %s: out of memory\n", fileName);
ExitNow(res = false);
}
readRes = fread(data, 1, (size_t)dataLen, file);
if (readRes < (size_t)dataLen || ferror(file))
{
fprintf(stderr, "weave: Error reading %s: %s\n", fileName, strerror(errno));
ExitNow(res = false);
}
exit:
if (file != NULL)
fclose(file);
if (!res && data != NULL)
{
free(data);
data = NULL;
}
return res;
}