blob: 436755d27388b03c079885aa592e3340673e968c [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 outputting
* information related to Weave security.
*
* @note These function symbols are only available when
* WEAVE_CONFIG_ENABLE_SECURITY_DEBUG_FUNCS has been
* asserted.
*
*/
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>
#include <stdio.h>
#include <Weave/Core/WeaveCore.h>
#include <Weave/Profiles/security/WeaveSecurityDebug.h>
#include <Weave/Support/ErrorStr.h>
namespace nl {
namespace Weave {
namespace Profiles {
namespace Security {
using namespace nl::Weave::ASN1;
using namespace nl::Weave::TLV;
using namespace nl::Weave::Profiles;
using namespace nl::Weave::Crypto;
#if WEAVE_CONFIG_ENABLE_SECURITY_DEBUG_FUNCS
static void Indent(FILE *out, uint16_t count)
{
while (count--)
fputc(' ', out);
}
static void PrintHexField(FILE *out, const char *name, uint16_t indent, uint16_t count, const uint8_t *data)
{
Indent(out, indent);
fprintf(out, "%s: ", name);
for (uint16_t i = 0; i < count; i++)
{
if (i % 16 == 0)
{
fprintf(out, "\n");
Indent(out, indent + 2);
}
fprintf(out, "%02X ", data[i]);
}
fprintf(out, "\n");
}
static void PrintCertType(FILE *out, uint8_t certType)
{
const char *certTypeStr;
switch (certType)
{
case kCertType_NotSpecified:
certTypeStr = "Not specified";
break;
case kCertType_General:
certTypeStr = "General";
break;
case kCertType_Device:
certTypeStr = "Device";
break;
case kCertType_ServiceEndpoint:
certTypeStr = "Service Endpoint";
break;
case kCertType_FirmwareSigning:
certTypeStr = "Firmware Signing";
break;
case kCertType_AccessToken:
certTypeStr = "Access Token";
break;
case kCertType_CA:
certTypeStr = "CA";
break;
default:
if (certType < kCertType_AppDefinedBase)
fprintf(out, "Unknown (0x%02X)", certType);
else
fprintf(out, "Application Defined (0x%02X)", certType);
return;
}
fputs(certTypeStr, out);
}
NL_DLL_EXPORT void PrintCert(FILE *out, const WeaveCertificateData& cert, const WeaveCertificateSet *certSet, uint16_t indent, bool verbose)
{
Indent(out, indent);
fprintf(out, "Subject: ");
if ((cert.CertFlags & kCertFlag_UnsupportedSubjectDN) == 0)
PrintWeaveDN(out, cert.SubjectDN);
else
fprintf(out, "(unsupported DN format)");
fprintf(out, "\n");
Indent(out, indent);
fprintf(out, "Issuer: ");
if ((cert.CertFlags & kCertFlag_UnsupportedIssuerDN) == 0)
PrintWeaveDN(out, cert.IssuerDN);
else
fprintf(out, "(unsupported DN format)");
fprintf(out, "\n");
if (cert.CertFlags & kCertFlag_ExtPresent_SubjectKeyId)
{
Indent(out, indent);
fprintf(out, "Subject Key Id: ");
for (uint16_t i = 0; i < cert.SubjectKeyId.Len; i++)
fprintf(out, "%02X", cert.SubjectKeyId.Id[i]);
fprintf(out, "\n");
}
if (cert.CertFlags & kCertFlag_ExtPresent_AuthKeyId)
{
Indent(out, indent);
fprintf(out, "Authority Key Id: ");
for (uint16_t i = 0; i < cert.AuthKeyId.Len; i++)
fprintf(out, "%02X", cert.AuthKeyId.Id[i]);
if (certSet != NULL)
{
WeaveCertificateData *authCert = certSet->FindCert(cert.AuthKeyId);
if (authCert != NULL)
fprintf(out, " (Cert %u)", (unsigned)(authCert - certSet->Certs));
else
fprintf(out, " (no match)");
}
fprintf(out, "\n");
}
Indent(out, indent);
fprintf(out, "Validity:\n");
Indent(out, indent + 2);
fprintf(out, "Not Before: "); PrintPackedDate(out, cert.NotBeforeDate); fprintf(out, "\n");
Indent(out, indent + 2);
fprintf(out, "Not After: "); PrintPackedDate(out, cert.NotAfterDate); fprintf(out, "\n");
if (cert.CertType != kCertType_NotSpecified)
{
Indent(out, indent);
fprintf(out, "Type: ");
PrintCertType(out, cert.CertType);
fprintf(out, "\n");
}
if (cert.CertFlags & kCertFlag_IsCA)
{
Indent(out, indent);
fprintf(out, "Is CA: true\n");
}
if (cert.CertFlags & kCertFlag_PathLenConstPresent)
{
Indent(out, indent);
fprintf(out, "Path Length Constraint: %u\n", (unsigned)cert.PathLenConstraint);
}
if (cert.CertFlags & kCertFlag_IsTrusted)
{
Indent(out, indent);
fprintf(out, "Is Trusted: true\n");
}
if (cert.CertFlags & kCertFlag_ExtPresent_KeyUsage)
{
Indent(out, indent);
fprintf(out, "Key Usage: ");
if (cert.KeyUsageFlags & kKeyUsageFlag_DigitalSignature)
fprintf(out, "DigitalSignature ");
if (cert.KeyUsageFlags & kKeyUsageFlag_NonRepudiation)
fprintf(out, "NonRepudiation ");
if (cert.KeyUsageFlags & kKeyUsageFlag_KeyEncipherment)
fprintf(out, "KeyEncipherment ");
if (cert.KeyUsageFlags & kKeyUsageFlag_DataEncipherment)
fprintf(out, "DataEncipherment ");
if (cert.KeyUsageFlags & kKeyUsageFlag_KeyAgreement)
fprintf(out, "KeyAgreement ");
if (cert.KeyUsageFlags & kKeyUsageFlag_KeyCertSign)
fprintf(out, "KeyCertSign ");
if (cert.KeyUsageFlags & kKeyUsageFlag_CRLSign)
fprintf(out, "CRLSign ");
if (cert.KeyUsageFlags & kKeyUsageFlag_EncipherOnly)
fprintf(out, "EncipherOnly ");
if (cert.KeyUsageFlags & kKeyUsageFlag_DecipherOnly)
fprintf(out, "DecipherOnly ");
fprintf(out, "\n");
}
if (cert.CertFlags & kCertFlag_ExtPresent_ExtendedKeyUsage)
{
Indent(out, indent);
fprintf(out, "Key Purpose: ");
if (cert.KeyPurposeFlags & kKeyPurposeFlag_ServerAuth)
fprintf(out, "ServerAuth ");
if (cert.KeyPurposeFlags & kKeyPurposeFlag_ClientAuth)
fprintf(out, "ClientAuth ");
if (cert.KeyPurposeFlags & kKeyPurposeFlag_CodeSigning)
fprintf(out, "CodeSigning ");
if (cert.KeyPurposeFlags & kKeyPurposeFlag_EmailProtection)
fprintf(out, "EmailProtection ");
if (cert.KeyPurposeFlags & kKeyPurposeFlag_TimeStamping)
fprintf(out, "TimeStamping ");
if (cert.KeyPurposeFlags & kKeyPurposeFlag_OCSPSigning)
fprintf(out, "OCSPSigning ");
fprintf(out, "\n");
}
Indent(out, indent);
fprintf(out, "Public Key Algorithm: %s\n", GetOIDName(cert.PubKeyAlgoOID));
Indent(out, indent);
fprintf(out, "Signature Algorithm: %s\n", GetOIDName(cert.SigAlgoOID));
if (cert.PubKeyAlgoOID == kOID_PubKeyAlgo_ECPublicKey ||
cert.PubKeyAlgoOID == kOID_PubKeyAlgo_ECDH ||
cert.PubKeyAlgoOID == kOID_PubKeyAlgo_ECMQV)
{
Indent(out, indent);
fprintf(out, "Curve Id: %s\n", GetOIDName(WeaveCurveIdToOID(cert.PubKeyCurveId)));
if (verbose)
{
PrintHexField(out, "Public Key", indent, cert.PublicKey.EC.ECPointLen, cert.PublicKey.EC.ECPoint);
Indent(out, indent);
fprintf(out, "Signature:\n");
PrintHexField(out, "r", indent + 2, cert.Signature.EC.RLen, cert.Signature.EC.R);
PrintHexField(out, "s", indent + 2, cert.Signature.EC.SLen, cert.Signature.EC.S);
}
}
}
NL_DLL_EXPORT void PrintCertValidationResults(FILE *out, const WeaveCertificateSet& certSet, const ValidationContext& validContext, uint16_t indent)
{
#if WEAVE_CONFIG_DEBUG_CERT_VALIDATION
WEAVE_ERROR *certValidRes = validContext.CertValidationResults;
for (uint8_t i = 0; i < certSet.CertCount && i < validContext.CertValidationResultsLen; i++)
{
const WeaveCertificateData& cert = certSet.Certs[i];
Indent(out, indent);
if (certValidRes[i] == WEAVE_NO_ERROR)
printf("Cert %d: Validation successful\n", i);
else if (certValidRes[i] == WEAVE_CERT_NOT_USED)
printf("Cert %d: Not used during validation\n", i);
else
printf("Cert %d: %s\n", i, nl::ErrorStr(certValidRes[i]));
PrintCert(out, cert, &certSet, indent + 2, false);
if (&cert == validContext.TrustAnchor)
{
Indent(out, indent + 2);
printf("Is Trust Anchor: true\n");
}
printf("\n");
}
#endif // WEAVE_CONFIG_DEBUG_CERT_VALIDATION
}
void PrintWeaveDN(FILE *out, const WeaveDN& dn)
{
char valueStr[1024];
const char *certDesc = NULL;
if (IsWeaveIdX509Attr(dn.AttrOID))
{
snprintf(valueStr, sizeof(valueStr), "%016" PRIX64, dn.AttrValue.WeaveId);
certDesc = DescribeWeaveCertId(dn.AttrOID, dn.AttrValue.WeaveId);
}
else
{
uint32_t len = dn.AttrValue.String.Len;
if (len > sizeof(valueStr) - 1)
len = sizeof(valueStr) - 1;
memcpy(valueStr, dn.AttrValue.String.Value, len);
valueStr[len] = 0;
}
fprintf(out, "%s=%s", nl::Weave::ASN1::GetOIDName((OID)dn.AttrOID), valueStr);
if (certDesc != NULL)
fprintf(out, " (%s)", certDesc);
}
WEAVE_ERROR PrintWeaveDN(FILE *out, TLVReader & reader)
{
WEAVE_ERROR err;
WeaveDN dn;
err = DecodeWeaveDN(reader, dn);
SuccessOrExit(err);
PrintWeaveDN(out, dn);
exit:
return err;
}
void PrintPackedTime(FILE *out, uint32_t t)
{
nl::Weave::ASN1::ASN1UniversalTime asn1Time;
UnpackCertTime(t, asn1Time);
fprintf(out, "%04" PRId16 "/%02" PRId8 "/%02" PRId8 " %02" PRId8 ":%02" PRId8 ":%02" PRId8 "",
asn1Time.Year, asn1Time.Month, asn1Time.Day,
asn1Time.Hour, asn1Time.Minute, asn1Time.Second);
}
void PrintPackedDate(FILE *out, uint16_t t)
{
nl::Weave::ASN1::ASN1UniversalTime asn1Time;
UnpackCertTime(PackedCertDateToTime(t), asn1Time);
fprintf(out, "%04" PRId16 "/%02" PRId8 "/%02" PRId8,
asn1Time.Year, asn1Time.Month, asn1Time.Day);
}
const char *DescribeWeaveCertId(OID attrOID, uint64_t weaveCertId)
{
switch (attrOID)
{
case kOID_AttributeType_WeaveCAId:
if (weaveCertId == 0x18B430EE00000001ULL)
return "Nest Production Root";
if (weaveCertId == 0x18B430EE00000002ULL)
return "Nest Production Device CA";
if (weaveCertId == 0x18B430EE00000003ULL)
return "Nest Production Service Endpoint CA";
if (weaveCertId == 0x18B430EE00000004ULL)
return "Nest Production Firmware Signing CA";
if (weaveCertId == 0x18B430EEEE000001ULL)
return "Nest Development Root";
if (weaveCertId == 0x18B430EEEE000002ULL)
return "Nest Development Device CA";
if (weaveCertId == 0x18B430EEEE000003ULL)
return "Nest Development Service Endpoint CA";
if (weaveCertId == 0x18B430EEEE000004ULL)
return "Nest Development Firmware Signing CA";
return NULL;
case kOID_AttributeType_WeaveDeviceId:
return "Device";
case kOID_AttributeType_WeaveServiceEndpointId:
if (weaveCertId == 0x18B4300200000001ULL)
return "Nest Directory Endpoint";
if (weaveCertId == 0x18B4300200000002ULL)
return "Nest Software Update Endpoint";
if (weaveCertId == 0x18B4300200000003ULL)
return "Nest Data Management Endpoint";
if (weaveCertId == 0x18B4300200000004ULL)
return "Nest Log Upload Endpoint";
if (weaveCertId == 0x18B4300200000005ULL)
return "Nest Time Service Endpoint";
if (weaveCertId == 0x18B4300200000010ULL)
return "Nest Service Provisioning Endpoint";
if (weaveCertId == 0x18B4300200000011ULL)
return "Nest Weave Tunnel Endpoint";
if (weaveCertId == 0x18B4300200000012ULL)
return "Nest Service Router Endpoint";
if (weaveCertId == 0x18B4300200000013ULL)
return "Nest File Download Endpoint";
if (weaveCertId == 0x18B4300200000014ULL)
return "Nest Bastion Service Endpoint";
return NULL;
case kOID_AttributeType_WeaveSoftwarePublisherId:
if (weaveCertId == 0x18B4300301000001ULL)
return "Nest Production Firmware Signing";
if (weaveCertId == 0x18B4300302000001ULL)
return "Nest Development Firmware Signing";
default:
return NULL;
}
}
WEAVE_ERROR PrintCertArray(FILE * out, TLVReader & reader, uint16_t indent)
{
WEAVE_ERROR err;
TLVType outerContainerType;
uint32_t certNum = 1;
WeaveCertificateData cert;
if (reader.GetType() == kTLVType_NotSpecified)
{
err = reader.Next();
SuccessOrExit(err);
}
VerifyOrExit(reader.GetType() == kTLVType_Array, err = WEAVE_ERROR_INVALID_ARGUMENT);
err = reader.EnterContainer(outerContainerType);
SuccessOrExit(err);
while ((err = reader.Next()) == WEAVE_NO_ERROR)
{
VerifyOrExit(reader.GetType() == kTLVType_Structure, err = WEAVE_ERROR_INVALID_ARGUMENT);
Indent(out, indent);
fprintf(out, "Certificate %" PRId32 ":\n", certNum);
err = DecodeWeaveCert(reader, cert);
SuccessOrExit(err);
err = DetermineCertType(cert);
SuccessOrExit(err);
PrintCert(out, cert, NULL, indent + 2, true);
certNum++;
}
VerifyOrExit(err == WEAVE_END_OF_TLV, /**/);
err = reader.ExitContainer(kTLVType_Array);
SuccessOrExit(err);
exit:
return err;
}
WEAVE_ERROR PrintECDSASignature(FILE * out, TLVReader & reader, uint16_t indent)
{
WEAVE_ERROR err;
TLVType outerContainerType;
if (reader.GetType() == kTLVType_NotSpecified)
{
err = reader.Next();
SuccessOrExit(err);
}
// Verify the start of the ECDSASignture structure.
VerifyOrExit(reader.GetType() == kTLVType_Structure, err = WEAVE_ERROR_INVALID_ARGUMENT);
err = reader.EnterContainer(outerContainerType);
SuccessOrExit(err);
while ((err = reader.Next()) == WEAVE_NO_ERROR)
{
uint64_t tag;
uint8_t tagNum;
const char * label;
const uint8_t * data;
tag = reader.GetTag();
if (!IsContextTag(tag))
{
continue;
}
tagNum = TagNumFromTag(tag);
switch (tagNum)
{
case kTag_ECDSASignature_r:
label = "r";
break;
case kTag_ECDSASignature_s:
label = "s";
break;
default:
continue;
}
VerifyOrExit(reader.GetType() == kTLVType_ByteString, err = WEAVE_ERROR_INVALID_ARGUMENT);
err = reader.GetDataPtr(data);
SuccessOrExit(err);
PrintHexField(out, label, indent, reader.GetLength(), data);
}
VerifyOrExit(err == WEAVE_END_OF_TLV, /**/);
err = reader.ExitContainer(kTLVType_Structure);
SuccessOrExit(err);
exit:
return err;
}
WEAVE_ERROR PrintCertReference(FILE * out, TLVReader & reader, uint16_t indent)
{
WEAVE_ERROR err;
TLVType outerContainerType;
if (reader.GetType() == kTLVType_NotSpecified)
{
err = reader.Next();
SuccessOrExit(err);
}
// Verify the start of the WeaveECDSASignture structure.
VerifyOrExit(reader.GetType() == kTLVType_Structure, err = WEAVE_ERROR_INVALID_ARGUMENT);
err = reader.EnterContainer(outerContainerType);
SuccessOrExit(err);
while ((err = reader.Next()) == WEAVE_NO_ERROR)
{
uint64_t tag;
uint8_t tagNum;
const uint8_t * data;
uint32_t dataLen;
tag = reader.GetTag();
if (!IsContextTag(tag))
{
continue;
}
tagNum = TagNumFromTag(tag);
switch (tagNum)
{
case kTag_WeaveCertificateRef_Subject:
Indent(out, indent);
fprintf(out, "Subject DN: ");
err = PrintWeaveDN(out, reader);
SuccessOrExit(err);
break;
case kTag_WeaveCertificateRef_PublicKeyId:
VerifyOrExit(reader.GetType() == kTLVType_ByteString, err = WEAVE_ERROR_INVALID_ARGUMENT);
err = reader.GetDataPtr(data);
SuccessOrExit(err);
dataLen = reader.GetLength();
Indent(out, indent);
fprintf(out, "Public Key Id: ");
for (uint32_t i = 0; i < dataLen; i++)
fprintf(out, "%02X", data[i]);
fprintf(out, "\n");
break;
default:
break;
}
}
VerifyOrExit(err == WEAVE_END_OF_TLV, /**/);
err = reader.ExitContainer(kTLVType_Structure);
SuccessOrExit(err);
exit:
return err;
}
WEAVE_ERROR PrintWeaveSignature(FILE * out, TLVReader & reader, uint16_t indent)
{
WEAVE_ERROR err;
TLVType outerContainerType;
bool sigAlgoPrinted = false;
if (reader.GetType() == kTLVType_NotSpecified)
{
err = reader.Next();
SuccessOrExit(err);
}
// Verify the start of the WeaveSignature structure.
VerifyOrExit(reader.GetType() == kTLVType_Structure, err = WEAVE_ERROR_INVALID_ARGUMENT);
err = reader.EnterContainer(outerContainerType);
SuccessOrExit(err);
while ((err = reader.Next()) == WEAVE_NO_ERROR)
{
uint64_t tag = reader.GetTag();
uint8_t tagNum;
if (!IsContextTag(tag))
{
continue;
}
tagNum = TagNumFromTag(tag);
if (!sigAlgoPrinted && tagNum != kTag_WeaveSignature_SignatureAlgorithm)
{
Indent(out, indent);
fprintf(out, "Signature Algorithm: ECDSAWithSHA1 (implicit)\n");
sigAlgoPrinted = true;
}
switch (tagNum)
{
case kTag_WeaveSignature_ECDSASignatureData:
Indent(out, indent);
fprintf(out, "ECDSA Signature:\n");
err = PrintECDSASignature(out, reader, indent + 2);
SuccessOrExit(err);
break;
case kTag_WeaveSignature_SigningCertificateRef:
Indent(out, indent);
fprintf(out, "Signing Certificate Reference:\n");
err = PrintCertReference(out, reader, indent + 2);
SuccessOrExit(err);
break;
case kTag_WeaveSignature_RelatedCertificates:
Indent(out, indent);
fprintf(out, "Related Certificates:\n");
err = PrintCertArray(out, reader, indent + 2);
SuccessOrExit(err);
break;
case kTag_WeaveSignature_SignatureAlgorithm:
{
uint16_t sigAlgo;
VerifyOrExit(reader.GetType() == kTLVType_UnsignedInteger, err = WEAVE_ERROR_INVALID_ARGUMENT);
err = reader.Get(sigAlgo);
SuccessOrExit(err);
Indent(out, indent);
fprintf(out, "Signature Algorithm: %s\n", GetOIDName(sigAlgo));
sigAlgoPrinted = true;
break;
}
default:
break;
}
}
VerifyOrExit(err == WEAVE_END_OF_TLV, /**/);
err = reader.ExitContainer(kTLVType_Structure);
SuccessOrExit(err);
exit:
return err;
}
#endif // WEAVE_CONFIG_ENABLE_SECURITY_DEBUG_FUNCS
} // namespace Security
} // namespace Profiles
} // namespace Weave
} // namespace nl