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

[TOB] DEV-3784: ID-5 #22

Open
wants to merge 6 commits into
base: development
Choose a base branch
from
Open
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
157 changes: 131 additions & 26 deletions src/helpers/X509CRLHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
uint256 validityNotBefore;
uint256 validityNotAfter;
uint256[] serialNumbersRevoked;
bytes authorityKeyIdentifier;
// for signature verification in the cert chain
bytes signature;
bytes tbs;
Expand All @@ -33,6 +34,8 @@

// 2.5.29.20
bytes constant CRL_NUMBER_OID = hex"551d14";
// 2.5.29.35
bytes constant AUTHORITY_KEY_IDENTIFIER_OID = hex"551D23";

/// =================================================================================
/// USE THE GETTERS BELOW IF YOU DON'T WANT TO PARSE THE ENTIRE X509 CRL
Expand Down Expand Up @@ -83,8 +86,25 @@
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);
uint256[] memory ret = _getRevokedSerialNumbers(der, tbsPtr, true, serialNumber);
revoked = ret[0] == serialNumber;
if (bytes1(der[tbsPtr.ixs()]) == 0x30) {
uint256[] memory ret = _getRevokedSerialNumbers(
der,
tbsPtr,
true,
serialNumber
);
revoked = ret[0] == serialNumber;
}
}

/// @dev according to RFC 5280, the Authority Key Identifier is mandatory for CA certificates
/// @dev if not present, this method returns 0x00
function getAuthorityKeyIdentifier(bytes calldata der) external pure returns (bytes memory akid) {
uint256 extensionPtr = _getExtensionPtr(der);
uint256 extnValuePtr = _findExtensionValuePtr(der, extensionPtr, AUTHORITY_KEY_IDENTIFIER_OID);
if (extnValuePtr != 0) {
akid = _getAuthorityKeyIdentifier(der, extnValuePtr);
}
}

/// x509 CRL generally contain a sequence of elements in the following order:
Expand Down Expand Up @@ -127,7 +147,25 @@
tbsPtr = der.nextSiblingOf(tbsPtr);
tbsPtr = der.nextSiblingOf(tbsPtr);

crl.serialNumbersRevoked = _getRevokedSerialNumbers(der, tbsPtr, false, 0);
if (bytes1(der[tbsPtr.ixs()]) == 0x30) {
// the revoked certificates field is present
crl.serialNumbersRevoked = _getRevokedSerialNumbers(
der,
tbsPtr,
false,
0
);
tbsPtr = der.nextSiblingOf(tbsPtr);
}

if (bytes1(der[tbsPtr.ixs()]) == 0xA0) {
uint256 authorityKeyIdentifierPtr = _findExtensionValuePtr(der, tbsPtr, AUTHORITY_KEY_IDENTIFIER_OID);
if (authorityKeyIdentifierPtr != 0) {
crl.authorityKeyIdentifier = _getAuthorityKeyIdentifier(der, authorityKeyIdentifierPtr);
}
} else {
revert("Extension is missing");
}

// tbs iteration completed
// now we just need to look for the signature
Expand Down Expand Up @@ -159,37 +197,36 @@
notAfter = DateTimeUtils.fromDERToTimestamp(der.bytesAt(notAfterPtr));
}

function _getRevokedSerialNumbers(bytes calldata der, uint256 revokedParentPtr, bool breakIfFound, uint256 filter)
function _getRevokedSerialNumbers(
bytes calldata der,
uint256 revokedParentPtr,
bool breakIfFound,
uint256 filter
)
private
pure
returns (uint256[] memory serialNumbers)
{
uint256 revokedPtr = der.firstChildOf(revokedParentPtr);

if (der[revokedPtr.ixs()] == 0xA0) {
uint256 crlExtensionPtr = der.firstChildOf(revokedPtr);
require(BytesUtils.compareBytes(der.bytesAt(crlExtensionPtr), CRL_NUMBER_OID), "invalid CRL");
} else {
bytes memory serials;
while (revokedPtr.ixl() <= revokedParentPtr.ixl()) {
uint256 serialPtr = der.firstChildOf(revokedPtr);
bytes memory serialBytes = der.bytesAt(serialPtr);
uint256 serialNumber = _parseSerialNumber(serialBytes);
serials = abi.encodePacked(serials, serialNumber);
if (breakIfFound && filter == serialNumber) {
serialNumbers = new uint256[](1);
serialNumbers[0] = filter;
return serialNumbers;
}
revokedPtr = der.nextSiblingOf(revokedPtr);
bytes memory serials;
while (revokedPtr.ixl() <= revokedParentPtr.ixl()) {
uint256 serialPtr = der.firstChildOf(revokedPtr);
bytes memory serialBytes = der.bytesAt(serialPtr);
uint256 serialNumber = _parseSerialNumber(serialBytes);
serials = abi.encodePacked(serials, serialNumber);
if (breakIfFound && filter == serialNumber) {
serialNumbers = new uint256[](1);
serialNumbers[0] = filter;
return serialNumbers;
}
uint256 count = serials.length / 32;
// ABI encoding format for a dynamic uint256[] value
serials = abi.encodePacked(abi.encode(0x20), abi.encode(count), serials);
serialNumbers = new uint256[](count);
serialNumbers = abi.decode(serials, (uint256[]));
revokedPtr = der.nextSiblingOf(revokedPtr);
}
uint256 count = serials.length / 32;
// ABI encoding format for a dynamic uint256[] value
serials = abi.encodePacked(abi.encode(0x20), abi.encode(count), serials);
serialNumbers = new uint256[](count);
serialNumbers = abi.decode(serials, (uint256[]));
}

Check failure

Code scanning / Slither

ABI encodePacked Collision High


function _parseSerialNumber(bytes memory serialBytes) private pure returns (uint256 serial) {
uint256 shift = 8 * (32 - serialBytes.length);
Expand All @@ -208,6 +245,34 @@
sig = abi.encodePacked(r, s);
}

function _getAuthorityKeyIdentifier(bytes calldata der, uint256 extnValuePtr) private pure returns (bytes memory akid) {
bytes memory extValue = der.bytesAt(extnValuePtr);

// The AUTHORITY_KEY_IDENTIFIER consists of a SEQUENCE with the following elements
// [0] - keyIdentifier (ESSENTIAL, but OPTIONAL as per RFC 5280)
// [1] - authorityCertIssuer (OPTIONAL as per RFC 5280)
// [2] - authorityCertSerialNumber (OPTIONAL as per RFC 5280)
// since we are interested in only the key identifier
// we iterate through the sequence until we find a tag matches with [0]

uint256 parentPtr = extValue.root();
uint256 ptr = extValue.firstChildOf(parentPtr);
bytes1 contextTag = 0x80;
while (true) {
bytes1 tag = bytes1(extValue[ptr.ixs()]);
if (tag == contextTag) {
akid = extValue.bytesAt(ptr);
break;
}

if (ptr.ixl() < parentPtr.ixl()) {
ptr = extValue.nextSiblingOf(ptr);
} else {
break;
}
}
}

/// @dev remove unnecessary prefix from the input
function _trimBytes(bytes memory input, uint256 expectedLength) private pure returns (bytes memory output) {
uint256 n = input.length;
Expand All @@ -224,4 +289,44 @@
output = input.substring(lengthDiff, expectedLength);
}
}

function _getExtensionPtr(bytes calldata der) private pure returns (uint256 extensionPtr) {
uint256 root = der.root();
uint256 tbsParentPtr = der.firstChildOf(root);
extensionPtr = der.firstChildOf(tbsParentPtr);
// iterate through the sequence until we find the extension tag (0xA3)
while (extensionPtr.ixl() <= tbsParentPtr.ixl()) {
bytes1 tag = bytes1(der[extensionPtr.ixs()]);
if (tag == 0xA0) {
return extensionPtr;
} else {
if (extensionPtr.ixl() == tbsParentPtr.ixl()) {
revert("Extension is missing");
} else {
extensionPtr = der.nextSiblingOf(extensionPtr);
}
}
}
}

function _findExtensionValuePtr(bytes calldata der, uint256 extensionPtr, bytes memory oid) private pure returns (uint256) {
uint256 parentPtr = der.firstChildOf(extensionPtr);
uint256 ptr = der.firstChildOf(parentPtr);

while (ptr != 0) {
uint256 oidPtr = der.firstChildOf(ptr);
if (der[oidPtr.ixs()] != 0x06) {
revert("Missing OID");
}
if (BytesUtils.compareBytes(der.bytesAt(oidPtr), oid)) {
return der.nextSiblingOf(oidPtr);
}

if (ptr.ixl() < parentPtr.ixl()) {
ptr = der.nextSiblingOf(ptr);
}
}

return 0; // not found
}
}
127 changes: 126 additions & 1 deletion src/helpers/X509Helper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,18 @@ struct X509CertObj {
bytes subjectPublicKey;
// the extension needs to be parsed further for PCK Certificates
uint256 extensionPtr;
bytes authorityKeyIdentifier;
bytes subjectKeyIdentifier;
// for signature verification in the cert chain
bytes signature;
bytes tbs;
}

// 2.5.29.35
bytes constant AUTHORITY_KEY_IDENTIFIER_OID = hex"551D23";
// 2.5.29.14
bytes constant SUBJECT_KEY_IDENTIFIER_OID = hex"551D0E";

/**
* @title X509 Certificates Helper Contract
* @notice This is a standalone contract that can be used by off-chain applications and smart contracts
Expand Down Expand Up @@ -102,6 +109,34 @@ contract X509Helper {
pubKey = _getSubjectPublicKey(der, der.firstChildOf(tbsPtr));
}

function getExtensionPtr(bytes calldata der) external pure returns (uint256 extensionPtr) {
extensionPtr = _getExtensionPtr(der);
}

/// @dev according to RFC 5280, the Authority Key Identifier is mandatory for CA certificates
/// @dev if not present, this method returns 0x00
function getAuthorityKeyIdentifier(bytes calldata der) external pure returns (bytes memory akid) {
uint256 extensionPtr = _getExtensionPtr(der);
uint256 extnValuePtr = _findExtensionValuePtr(der, extensionPtr, AUTHORITY_KEY_IDENTIFIER_OID);
if (extnValuePtr != 0) {
akid = _getAuthorityKeyIdentifier(der, extnValuePtr);
}
}

/// @dev according to RFC 5280, the Subject Key Identifier is RECOMMENDED for CA certificates
/// @dev Intel DCAP attestation certificates contain this extension
/// @dev this value can be useful for checking CRLs without performing signature verification, which can be costly in terms of gas
/// @dev we can simply use this value to check against the CRL's authority key identifier
/// @dev if not present, this method returns 0x00
function getSubjectKeyIdentifier(bytes calldata der) external pure returns (bytes memory skid) {
uint256 extensionPtr = _getExtensionPtr(der);
uint256 extValuePtr = _findExtensionValuePtr(der, extensionPtr, SUBJECT_KEY_IDENTIFIER_OID);

if (extValuePtr != 0) {
skid = _getSubjectKeyIdentifier(der, extValuePtr);
}
}

/// x509 Certificates generally contain a sequence of elements in the following order:
/// 1. tbs
/// - 1a. version
Expand Down Expand Up @@ -151,7 +186,20 @@ contract X509Helper {
tbsPtr = der.nextSiblingOf(tbsPtr);
cert.subjectPublicKey = _getSubjectPublicKey(der, der.firstChildOf(tbsPtr));

cert.extensionPtr = der.nextSiblingOf(tbsPtr);
uint256 extensionPtr = der.nextSiblingOf(tbsPtr);
if (bytes1(der[extensionPtr.ixs()]) == 0xA3) {
cert.extensionPtr = extensionPtr;
uint256 authorityKeyIdentifierPtr = _findExtensionValuePtr(der, extensionPtr, AUTHORITY_KEY_IDENTIFIER_OID);
if (authorityKeyIdentifierPtr != 0) {
cert.authorityKeyIdentifier = _getAuthorityKeyIdentifier(der, authorityKeyIdentifierPtr);
}
uint256 subjectKeyIdentifierPtr = _findExtensionValuePtr(der, extensionPtr, SUBJECT_KEY_IDENTIFIER_OID);
if (subjectKeyIdentifierPtr != 0) {
cert.subjectKeyIdentifier = _getSubjectKeyIdentifier(der, subjectKeyIdentifierPtr);
}
} else {
revert("Extension is missing");
}

// tbs iteration completed
// now we just need to look for the signature
Expand Down Expand Up @@ -197,6 +245,43 @@ contract X509Helper {
pubKey = _trimBytes(pubKey, 64);
}

function _getAuthorityKeyIdentifier(bytes calldata der, uint256 extnValuePtr) private pure returns (bytes memory akid) {
bytes memory extValue = der.bytesAt(extnValuePtr);

// The AUTHORITY_KEY_IDENTIFIER consists of a SEQUENCE with the following elements
// [0] - keyIdentifier (ESSENTIAL, but OPTIONAL as per RFC 5280)
// [1] - authorityCertIssuer (OPTIONAL as per RFC 5280)
// [2] - authorityCertSerialNumber (OPTIONAL as per RFC 5280)
// since we are interested in only the key identifier
// we iterate through the sequence until we find a tag matches with [0]

uint256 parentPtr = extValue.root();
uint256 ptr = extValue.firstChildOf(parentPtr);
bytes1 contextTag = 0x80;
while (true) {
bytes1 tag = bytes1(extValue[ptr.ixs()]);
if (tag == contextTag) {
akid = extValue.bytesAt(ptr);
break;
}

if (ptr.ixl() < parentPtr.ixl()) {
ptr = extValue.nextSiblingOf(ptr);
} else {
break;
}
}
}

function _getSubjectKeyIdentifier(bytes calldata der, uint256 extValuePtr) private pure returns (bytes memory skid) {
// The SUBJECT_KEY_IDENTIFIER simply consists of the KeyIdentifier of Octet String type (0x04)
// so we can return the value as it is

// check octet string tag
require(der[extValuePtr.ixf()] == 0x04, "keyIdentifier must be of OctetString type");
skid = der[extValuePtr.ixf() + 2 : extValuePtr.ixf() + 2 + 20];
}

function _parseSerialNumber(bytes memory serialBytes) private pure returns (uint256 serial) {
uint256 shift = 8 * (32 - serialBytes.length);
serial = uint256(bytes32(serialBytes) >> shift);
Expand Down Expand Up @@ -230,4 +315,44 @@ contract X509Helper {
output = input.substring(lengthDiff, expectedLength);
}
}

function _getExtensionPtr(bytes calldata der) private pure returns (uint256 extensionPtr) {
uint256 root = der.root();
uint256 tbsParentPtr = der.firstChildOf(root);
extensionPtr = der.firstChildOf(tbsParentPtr);
// iterate through the sequence until we find the extension tag (0xA3)
while (extensionPtr.ixl() <= tbsParentPtr.ixl()) {
bytes1 tag = bytes1(der[extensionPtr.ixs()]);
if (tag == 0xA3) {
return extensionPtr;
} else {
if (extensionPtr.ixl() == tbsParentPtr.ixl()) {
revert("Extension is missing");
} else {
extensionPtr = der.nextSiblingOf(extensionPtr);
}
}
}
}

function _findExtensionValuePtr(bytes calldata der, uint256 extensionPtr, bytes memory oid) private pure returns (uint256) {
uint256 parentPtr = der.firstChildOf(extensionPtr);
uint256 ptr = der.firstChildOf(parentPtr);

while (ptr != 0) {
uint256 oidPtr = der.firstChildOf(ptr);
if (der[oidPtr.ixs()] != 0x06) {
revert("Missing OID");
}
if (BytesUtils.compareBytes(der.bytesAt(oidPtr), oid)) {
return der.nextSiblingOf(oidPtr);
}

if (ptr.ixl() < parentPtr.ixl()) {
ptr = der.nextSiblingOf(ptr);
}
}

return 0; // not found
}
}
Loading
Loading