Skip to content

Commit

Permalink
Updated ECDSA Signature Representation in the CHIP Certificate. (#7384)
Browse files Browse the repository at this point in the history
  • Loading branch information
emargolis authored and pull[bot] committed Jul 21, 2021
1 parent c682721 commit 5757bf9
Show file tree
Hide file tree
Showing 7 changed files with 385 additions and 290 deletions.
165 changes: 124 additions & 41 deletions src/credentials/CHIPCert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,47 +374,20 @@ CHIP_ERROR ChipCertificateSet::FindValidCert(const ChipDN & subjectDN, const Cer

CHIP_ERROR ChipCertificateSet::VerifySignature(const ChipCertificateData * cert, const ChipCertificateData * caCert)
{
static constexpr size_t kMaxBytesForDeferredLenList = sizeof(uint8_t *) + // size of a single pointer in the deferred list
4 + // extra memory allocated for the deferred length field (kLengthFieldReserveSize - 1)
3; // the deferred length list is alligned to 32bit boundary

CHIP_ERROR err;
P256PublicKey caPublicKey;
P256ECDSASignature signature;
uint8_t tmpBuf[signature.Capacity() + kMaxBytesForDeferredLenList];
ASN1Writer writer;
uint16_t derSigLen;

writer.Init(tmpBuf, static_cast<uint32_t>(sizeof(tmpBuf)));
ReturnErrorOnFailure(ConvertECDSASignatureRawToDER(cert->mSignature, cert->mSignatureLen, signature,
static_cast<uint16_t>(signature.Capacity()), derSigLen));

// Ecdsa-Sig-Value ::= SEQUENCE
ASN1_START_SEQUENCE
{
// r INTEGER
err = writer.PutValue(kASN1TagClass_Universal, kASN1UniversalTag_Integer, false, cert->mSignature.R, cert->mSignature.RLen);
SuccessOrExit(err);

// s INTEGER
err = writer.PutValue(kASN1TagClass_Universal, kASN1UniversalTag_Integer, false, cert->mSignature.S, cert->mSignature.SLen);
SuccessOrExit(err);
}
ASN1_END_SEQUENCE;

err = writer.Finalize();
SuccessOrExit(err);

VerifyOrExit(writer.GetLengthWritten() <= signature.Capacity(), err = CHIP_ERROR_BUFFER_TOO_SMALL);

memcpy(signature, tmpBuf, writer.GetLengthWritten());
err = signature.SetLength(writer.GetLengthWritten());
SuccessOrExit(err);
ReturnErrorOnFailure(signature.SetLength(derSigLen));

memcpy(caPublicKey, caCert->mPublicKey, caCert->mPublicKeyLen);

err = caPublicKey.ECDSA_validate_hash_signature(cert->mTBSHash, chip::Crypto::kSHA256_Hash_Length, signature);
SuccessOrExit(err);
ReturnErrorOnFailure(caPublicKey.ECDSA_validate_hash_signature(cert->mTBSHash, chip::Crypto::kSHA256_Hash_Length, signature));

exit:
return err;
return CHIP_NO_ERROR;
}

CHIP_ERROR ChipCertificateSet::ValidateCert(const ChipCertificateData * cert, ValidationContext & context,
Expand Down Expand Up @@ -607,10 +580,8 @@ void ChipCertificateData::Clear()
mKeyUsageFlags.ClearAll();
mKeyPurposeFlags.ClearAll();
mPathLenConstraint = 0;
mSignature.R = nullptr;
mSignature.RLen = 0;
mSignature.S = nullptr;
mSignature.SLen = 0;
mSignature = nullptr;
mSignatureLen = 0;
memset(mTBSHash, 0, sizeof(mTBSHash));
}

Expand All @@ -624,10 +595,8 @@ bool ChipCertificateData::IsEqual(const ChipCertificateData & other) const
(mPubKeyCurveOID == other.mPubKeyCurveOID) && (mPubKeyAlgoOID == other.mPubKeyAlgoOID) &&
(mSigAlgoOID == other.mSigAlgoOID) && (mCertFlags.Raw() == other.mCertFlags.Raw()) &&
(mKeyUsageFlags.Raw() == other.mKeyUsageFlags.Raw()) && (mKeyPurposeFlags.Raw() == other.mKeyPurposeFlags.Raw()) &&
(mPathLenConstraint == other.mPathLenConstraint) && (mSignature.RLen == other.mSignature.RLen) &&
(memcmp(mSignature.R, other.mSignature.R, mSignature.RLen) == 0) && (mSignature.SLen == other.mSignature.SLen) &&
(memcmp(mSignature.S, other.mSignature.S, mSignature.SLen) == 0) &&
(memcmp(mTBSHash, other.mTBSHash, sizeof(mTBSHash)) == 0);
(mPathLenConstraint == other.mPathLenConstraint) && (mSignatureLen == other.mSignatureLen) &&
(memcmp(mSignature, other.mSignature, mSignatureLen) == 0) && (memcmp(mTBSHash, other.mTBSHash, sizeof(mTBSHash)) == 0);
}

void ValidationContext::Reset()
Expand Down Expand Up @@ -873,5 +842,119 @@ DLL_EXPORT CHIP_ERROR ChipEpochToASN1Time(uint32_t epochTime, chip::ASN1::ASN1Un
return CHIP_NO_ERROR;
}

CHIP_ERROR ConvertIntegerDERToRaw(const uint8_t * derInt, uint16_t derIntLen, uint8_t * rawInt, const uint16_t rawIntLen)
{
VerifyOrReturnError(derInt != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(derIntLen > 0, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(rawInt != nullptr, CHIP_ERROR_INVALID_ARGUMENT);

/* one leading zero is allowed for positive integer in ASN1 DER format */
if (*derInt == 0)
{
derInt++;
derIntLen--;
}

VerifyOrReturnError(derIntLen <= rawIntLen, CHIP_ERROR_INVALID_ARGUMENT);

if (derIntLen > 0)
{
VerifyOrReturnError(*derInt != 0, CHIP_ERROR_INVALID_ARGUMENT);
}

memset(rawInt, 0, (rawIntLen - derIntLen));
memcpy(rawInt + (rawIntLen - derIntLen), derInt, derIntLen);

return CHIP_NO_ERROR;
}

CHIP_ERROR ConvertIntegerRawToDER(const uint8_t * rawInt, uint16_t rawIntLen, uint8_t * derInt, const uint16_t derIntBufSize,
uint16_t & derIntLen)
{
VerifyOrReturnError(rawInt != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(rawIntLen > 0, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(derInt != nullptr, CHIP_ERROR_INVALID_ARGUMENT);

while (*rawInt == 0)
{
rawInt++;
rawIntLen--;
}

if (*rawInt & 0x80) /* Need Leading Zero */
{
VerifyOrReturnError(rawIntLen <= UINT16_MAX - 1, CHIP_ERROR_BUFFER_TOO_SMALL);
VerifyOrReturnError(derIntBufSize >= rawIntLen + 1, CHIP_ERROR_BUFFER_TOO_SMALL);

*derInt++ = 0;
derIntLen = static_cast<uint16_t>(rawIntLen + 1);
}
else
{
VerifyOrReturnError(derIntBufSize >= rawIntLen, CHIP_ERROR_BUFFER_TOO_SMALL);

derIntLen = rawIntLen;
}

memcpy(derInt, rawInt, rawIntLen);

return CHIP_NO_ERROR;
}

CHIP_ERROR ConvertECDSASignatureRawToDER(const uint8_t * rawSig, uint16_t rawSigLen, uint8_t * derSig, const uint16_t derSigBufSize,
uint16_t & derSigLen)
{
static constexpr size_t kMaxBytesForDeferredLenList = sizeof(uint8_t *) + // size of a single pointer in the deferred list
4 + // extra memory allocated for the deferred length field (kLengthFieldReserveSize - 1)
3; // the deferred length list is alligned to 32bit boundary

uint8_t localDERSigBuf[kMax_ECDSA_Signature_Length + kMaxBytesForDeferredLenList];
ASN1Writer writer;

VerifyOrReturnError(rawSig != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(rawSigLen > 0, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(derSig != nullptr, CHIP_ERROR_INVALID_ARGUMENT);

writer.Init(localDERSigBuf, sizeof(localDERSigBuf));

ReturnErrorOnFailure(ConvertECDSASignatureRawToDER(rawSig, rawSigLen, writer));

ReturnErrorOnFailure(writer.Finalize());

derSigLen = writer.GetLengthWritten();

VerifyOrReturnError(derSigLen <= derSigBufSize, CHIP_ERROR_BUFFER_TOO_SMALL);

memcpy(derSig, localDERSigBuf, derSigLen);

return CHIP_NO_ERROR;
}

CHIP_ERROR ConvertECDSASignatureRawToDER(const uint8_t * rawSig, uint16_t rawSigLen, ASN1Writer & writer)
{
CHIP_ERROR err = CHIP_NO_ERROR;
uint8_t derInt[kP256_FE_Length + 1];
uint16_t derIntLen;

VerifyOrReturnError(rawSig != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(rawSigLen == kP256_ECDSA_Signature_Length_Raw, CHIP_ERROR_INVALID_ARGUMENT);

// Ecdsa-Sig-Value ::= SEQUENCE
ASN1_START_SEQUENCE
{
// r INTEGER
ReturnErrorOnFailure(ConvertIntegerRawToDER(rawSig, kP256_FE_Length, derInt, sizeof(derInt), derIntLen));
ReturnErrorOnFailure(writer.PutValue(kASN1TagClass_Universal, kASN1UniversalTag_Integer, false, derInt, derIntLen));

// s INTEGER
ReturnErrorOnFailure(ConvertIntegerRawToDER(rawSig + kP256_FE_Length, kP256_FE_Length, derInt, sizeof(derInt), derIntLen));
ReturnErrorOnFailure(writer.PutValue(kASN1TagClass_Universal, kASN1UniversalTag_Integer, false, derInt, derIntLen));
}
ASN1_END_SEQUENCE;

exit:
return err;
}

} // namespace Credentials
} // namespace chip
82 changes: 69 additions & 13 deletions src/credentials/CHIPCert.h
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ enum
kTag_EllipticCurveIdentifier = 8, /**< [ unsigned int ] For EC certs, identifies the elliptic curve used. */
kTag_EllipticCurvePublicKey = 9, /**< [ byte string ] The elliptic curve public key, in X9.62 encoded format. */
kTag_Extensions = 10, /**< [ list ] Certificate extensions. */
kTag_ECDSASignature = 11, /**< [ structure ] The ECDSA signature for the certificate. */
kTag_ECDSASignature = 11, /**< [ byte string ] The ECDSA signature for the certificate. */

// ---- Context-specific Tags for certificate extensions ----
kTag_BasicConstraints = 1, /**< [ structure ] Identifies whether the subject of the certificate is a CA. */
Expand All @@ -73,10 +73,6 @@ enum
kTag_AuthorityKeyIdentifier = 5, /**< [ byte string ] Identifier of the public key used to sign the certificate. */
kTag_FutureExtension = 6, /**< [ byte string ] Arbitrary extention. DER encoded SEQUENCE as in X.509 form. */

// ---- Context-specific Tags for ECDSASignature Structure ----
kTag_ECDSASignature_r = 1, /**< [ byte string ] ECDSA r value, in ASN.1 integer encoding. */
kTag_ECDSASignature_s = 2, /**< [ byte string ] ECDSA s value, in ASN.1 integer encoding. */

// ---- Context-specific Tags for BasicConstraints Structure ----
kTag_BasicConstraints_IsCA = 1, /**< [ boolean ] True if the certificate can be used to verify certificate
signatures. */
Expand Down Expand Up @@ -310,6 +306,7 @@ struct ChipCertificateData
void Clear();
bool IsEqual(const ChipCertificateData & other) const;

// TODO: Review and consider replacing some data pointer/len pairs with ByteSpan and FixedByteSpan types.
ByteSpan mCertificate; /**< Original raw buffer data. */
ChipDN mSubjectDN; /**< Certificate Subject DN. */
ChipDN mIssuerDN; /**< Certificate Issuer DN. */
Expand All @@ -326,14 +323,10 @@ struct ChipCertificateData
BitFlags<KeyUsageFlags> mKeyUsageFlags; /**< Certificate key usage extensions flags. */
BitFlags<KeyPurposeFlags> mKeyPurposeFlags; /**< Certificate extended key usage extensions flags. */
uint8_t mPathLenConstraint; /**< Basic constraint: path length. */
struct
{
const uint8_t * R; /**< Pointer to the R element of the signature, encoded as ASN.1 DER Integer. */
uint8_t RLen; /**< Length of R. */
const uint8_t * S; /**< Pointer to the S element of the signature, encoded as ASN.1 DER Integer. */
uint8_t SLen; /**< Length of R. */
} mSignature; /**< Certificate signature structure. */
uint8_t mTBSHash[chip::Crypto::kSHA256_Hash_Length]; /**< Certificate TBS hash. */
const uint8_t * mSignature; /**< Pointer to the certificate signature. */
uint8_t mSignatureLen; /**< Certificate signature length. */

uint8_t mTBSHash[Crypto::kSHA256_Hash_Length]; /**< Certificate TBS hash. */
};

/**
Expand Down Expand Up @@ -789,5 +782,68 @@ inline bool IsChipDNAttr(chip::ASN1::OID oid)
return (IsChip64bitDNAttr(oid) || IsChip32bitDNAttr(oid));
}

/**
* @brief Convert an ASN.1 DER encoded integer to a raw big-endian integer.
*
* @param derInt Buffer that holds ASN.1 DER encoded integer.
* @param derIntLen The length of the ASN.1 DER encoded integer.
* @param rawInt Buffer to store converted raw integer.
* @param rawIntLen The length of the converted raw integer.
*
* @retval #CHIP_NO_ERROR If the integer value was successfully converted.
*/
CHIP_ERROR ConvertIntegerDERToRaw(const uint8_t * derInt, uint16_t derIntLen, uint8_t * rawInt, const uint16_t rawIntLen);

/**
* @brief Convert a raw integer in big-endian form to an ASN.1 DER encoded integer.
*
* @param rawInt Buffer that holds raw integer.
* @param rawIntLen The length of the raw integer.
* @param derInt Buffer to store converted ASN.1 DER encoded integer.
* @param derIntBufSize The size of the buffer to store ASN.1 DER encoded integer.
* @param derIntLen The length of the ASN.1 DER encoded integer.
*
* @retval #CHIP_NO_ERROR If the integer value was successfully converted.
*/
CHIP_ERROR ConvertIntegerRawToDER(const uint8_t * rawInt, uint16_t rawIntLen, uint8_t * derInt, const uint16_t derIntBufSize,
uint16_t & derIntLen);

/**
* @brief Convert a raw CHIP signature to an ASN.1 DER encoded signature structure.
*
* @param rawSig Buffer that holds raw CHIP signature.
* @param rawSigLen The length of the raw CHIP signature.
* @param derSig Buffer to store converted ASN.1 DER encoded signature.
* @param derSigBufSize The size of the buffer to store ASN.1 DER encoded signature.
* @param derSigLen The length of the ASN.1 DER encoded signature.
*
* @retval #CHIP_NO_ERROR If the signature value was successfully converted.
*/
CHIP_ERROR ConvertECDSASignatureRawToDER(const uint8_t * rawSig, uint16_t rawSigLen, uint8_t * derSig, const uint16_t derSigBufSize,
uint16_t & derSigLen);

/**
* @brief Convert a raw CHIP ECDSA signature to an ASN.1 DER encoded signature structure.
*
* @param rawSig Buffer that holds raw CHIP signature.
* @param rawSigLen The length of the raw CHIP signature.
* @param writer A reference to the ASN1Writer to store ASN.1 DER encoded signature.
*
* @retval #CHIP_NO_ERROR If the signature value was successfully converted.
*/
CHIP_ERROR ConvertECDSASignatureRawToDER(const uint8_t * rawSig, uint16_t rawSigLen, ASN1::ASN1Writer & writer);

/**
* @brief Convert an ASN.1 DER encoded ECDSA signature to a raw CHIP signature.
*
* @param reader A reference to the ASN1Reader positioned at the beginning of the
* DER encoded ECDSA signature.
* @param writer A reference to the TLVWriter to store TLV encoded ECDSA signature element.
* @param tag Tag to use for TLV encoded signature.
*
* @retval #CHIP_NO_ERROR If the signature value was successfully converted.
*/
CHIP_ERROR ConvertECDSASignatureDERToRaw(ASN1::ASN1Reader & reader, chip::TLV::TLVWriter & writer, uint64_t tag);

} // namespace Credentials
} // namespace chip
62 changes: 35 additions & 27 deletions src/credentials/CHIPCertFromX509.cpp
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ namespace Credentials {
using namespace chip::ASN1;
using namespace chip::TLV;
using namespace chip::Protocols;
using namespace chip::Crypto;

static ASN1_ERROR ParseChipAttribute(ASN1Reader & reader, uint64_t & chipAttrOut)
{
Expand Down Expand Up @@ -516,6 +517,39 @@ static CHIP_ERROR ConvertExtensions(ASN1Reader & reader, TLVWriter & writer)
return err;
}

CHIP_ERROR ConvertECDSASignatureDERToRaw(ASN1Reader & reader, TLVWriter & writer, uint64_t tag)
{
CHIP_ERROR err = CHIP_NO_ERROR;
uint8_t rawSig[kP256_ECDSA_Signature_Length_Raw];

// Per RFC3279, the ECDSA signature value is encoded in DER encapsulated in the signatureValue BIT STRING.
ASN1_ENTER_ENCAPSULATED(kASN1TagClass_Universal, kASN1UniversalTag_BitString)
{
// Ecdsa-Sig-Value ::= SEQUENCE
ASN1_PARSE_ENTER_SEQUENCE
{
// r INTEGER
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_Integer);
VerifyOrReturnError(reader.GetValueLen() <= UINT16_MAX, CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorOnFailure(
ConvertIntegerDERToRaw(reader.GetValue(), static_cast<uint16_t>(reader.GetValueLen()), rawSig, kP256_FE_Length));

// s INTEGER
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_Integer);
VerifyOrReturnError(reader.GetValueLen() <= UINT16_MAX, CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorOnFailure(ConvertIntegerDERToRaw(reader.GetValue(), static_cast<uint16_t>(reader.GetValueLen()),
rawSig + kP256_FE_Length, kP256_FE_Length));
}
ASN1_EXIT_SEQUENCE;
}
ASN1_EXIT_ENCAPSULATED;

ReturnErrorOnFailure(writer.PutBytes(tag, rawSig, kP256_ECDSA_Signature_Length_Raw));

exit:
return err;
}

static CHIP_ERROR ConvertCertificate(ASN1Reader & reader, TLVWriter & writer)
{
CHIP_ERROR err;
Expand Down Expand Up @@ -633,33 +667,7 @@ static CHIP_ERROR ConvertCertificate(ASN1Reader & reader, TLVWriter & writer)
// signatureValue BIT STRING
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_BitString);

// Per RFC3279, the ECDSA signature value is encoded in DER encapsulated in the signatureValue BIT STRING.
ASN1_ENTER_ENCAPSULATED(kASN1TagClass_Universal, kASN1UniversalTag_BitString)
{
TLVType outerContainer;

err = writer.StartContainer(ContextTag(kTag_ECDSASignature), kTLVType_Structure, outerContainer);
SuccessOrExit(err);

// Ecdsa-Sig-Value ::= SEQUENCE
ASN1_PARSE_ENTER_SEQUENCE
{
// r INTEGER
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_Integer);
err = writer.PutBytes(ContextTag(kTag_ECDSASignature_r), reader.GetValue(), reader.GetValueLen());
SuccessOrExit(err);

// s INTEGER
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_Integer);
err = writer.PutBytes(ContextTag(kTag_ECDSASignature_s), reader.GetValue(), reader.GetValueLen());
SuccessOrExit(err);
}
ASN1_EXIT_SEQUENCE;

err = writer.EndContainer(outerContainer);
SuccessOrExit(err);
}
ASN1_EXIT_ENCAPSULATED;
ReturnErrorOnFailure(ConvertECDSASignatureDERToRaw(reader, writer, ContextTag(kTag_ECDSASignature)));
}
ASN1_EXIT_SEQUENCE;

Expand Down
Loading

0 comments on commit 5757bf9

Please sign in to comment.