blob: eb9e3a0b0640caa2355df1ff9cd2d95833f51e5e [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");
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)
{
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");
}
}
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);
}
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;
}
}
#endif // WEAVE_CONFIG_ENABLE_SECURITY_DEBUG_FUNCS
} // namespace Security
} // namespace Profiles
} // namespace Weave
} // namespace nl