Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated ECDSA Signature Representation in the CHIP Certificate. #7384

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
emargolis marked this conversation as resolved.
Show resolved Hide resolved

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. */
emargolis marked this conversation as resolved.
Show resolved Hide resolved

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)));
woody-apple marked this conversation as resolved.
Show resolved Hide resolved
}
ASN1_EXIT_SEQUENCE;

Expand Down
Loading