Skip to content

Commit 33baea4

Browse files
committed
Merge branch 'DEV-3731' into development
2 parents 838a536 + 0209653 commit 33baea4

9 files changed

+156
-69
lines changed

src/bases/DaoBase.sol

+19
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import "../interfaces/IDaoAttestationResolver.sol";
1212
abstract contract DaoBase {
1313
IDaoAttestationResolver public immutable resolver;
1414

15+
// 72bd8361
16+
error Duplicate_Collateral();
17+
1518
constructor(address _resolver) {
1619
resolver = IDaoAttestationResolver(_resolver);
1720
}
@@ -58,6 +61,22 @@ abstract contract DaoBase {
5861
return _fetchDataFromResolver(key, hash);
5962
}
6063

64+
/**
65+
* @notice check whether the hash for the provided collateral already exists in the PCCS
66+
* @param key - the key to locate the collateral attestation
67+
* @param hash - the hash of the collateral
68+
*/
69+
function _checkCollateralDuplicate(bytes32 key, bytes32 hash) internal view {
70+
// if a matching hash is found, that means the caller is attempting to re-upsert duplicate collateral
71+
bytes memory existingHashData = _fetchDataFromResolver(key, true);
72+
if (existingHashData.length > 0) {
73+
bytes32 existingHash = abi.decode(existingHashData, (bytes32));
74+
if (existingHash == hash) {
75+
revert Duplicate_Collateral();
76+
}
77+
}
78+
}
79+
6180
/// @dev https://github.com/Vectorized/solady/blob/4964e3e2da1bc86b0394f63a90821f51d60a260b/src/utils/JSONParserLib.sol#L339-L364
6281
/// @dev Parses an unsigned integer from a string (in hexadecimal, i.e. base 16).
6382
/// Reverts if `s` is not a valid uint256 hex string matching the RegEx

src/bases/EnclaveIdentityDao.sol

+12-8
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,13 @@ abstract contract EnclaveIdentityDao is DaoBase, SigVerifyBase {
8989
external
9090
returns (bytes32 attestationId)
9191
{
92-
_validateQeIdentity(enclaveIdentityObj);
93-
(bytes32 key, bytes memory req) = _buildEnclaveIdentityAttestationRequest(id, version, enclaveIdentityObj);
92+
bytes32 key = ENCLAVE_ID_KEY(id, version);
9493
bytes32 hash = sha256(bytes(enclaveIdentityObj.identityStr));
94+
95+
_checkCollateralDuplicate(key, hash);
96+
97+
_validateQeIdentity(enclaveIdentityObj, hash);
98+
bytes memory req = _buildEnclaveIdentityAttestationRequest(id, version, key, enclaveIdentityObj);
9599
attestationId = _attestEnclaveIdentity(req, hash, key);
96100

97101
emit UpsertedEnclaveIdentity(id, version);
@@ -125,8 +129,9 @@ abstract contract EnclaveIdentityDao is DaoBase, SigVerifyBase {
125129
function _buildEnclaveIdentityAttestationRequest(
126130
uint256 id,
127131
uint256 version,
132+
bytes32 key,
128133
EnclaveIdentityJsonObj calldata enclaveIdentityObj
129-
) private view returns (bytes32 key, bytes memory reqData) {
134+
) private view returns (bytes memory reqData) {
130135
IdentityObj memory identity = EnclaveIdentityLib.parseIdentityString(enclaveIdentityObj.identityStr);
131136
if (id != uint256(identity.id)) {
132137
revert Enclave_Id_Mismatch();
@@ -141,13 +146,12 @@ abstract contract EnclaveIdentityDao is DaoBase, SigVerifyBase {
141146
}
142147

143148
// make sure new collateral is "newer"
144-
key = ENCLAVE_ID_KEY(id, version);
145-
bytes memory existingData = _onFetchDataFromResolver(key, false);
149+
bytes memory existingData = _fetchDataFromResolver(key, false);
146150
if (existingData.length > 0) {
147151
(IdentityObj memory existingIdentity, , ) =
148152
abi.decode(existingData, (IdentityObj, string, bytes));
149153
bool outOfDate = existingIdentity.tcbEvaluationDataNumber > identity.tcbEvaluationDataNumber ||
150-
existingIdentity.issueDateTimestamp > identity.issueDateTimestamp;
154+
existingIdentity.issueDateTimestamp >= identity.issueDateTimestamp;
151155
if (outOfDate) {
152156
revert Enclave_Id_Out_Of_Date();
153157
}
@@ -159,12 +163,12 @@ abstract contract EnclaveIdentityDao is DaoBase, SigVerifyBase {
159163
/**
160164
* @notice validates IdentityString is signed by Intel TCB Signing Cert
161165
*/
162-
function _validateQeIdentity(EnclaveIdentityJsonObj calldata enclaveIdentityObj) private view {
166+
function _validateQeIdentity(EnclaveIdentityJsonObj calldata enclaveIdentityObj, bytes32 hash) private view {
163167
bytes memory signingDer = _fetchDataFromResolver(Pcs.PCS_KEY(CA.SIGNING, false), false);
164168

165169
// Validate signature
166170
bool sigVerified =
167-
verifySignature(sha256(bytes(enclaveIdentityObj.identityStr)), enclaveIdentityObj.signature, signingDer);
171+
verifySignature(hash, enclaveIdentityObj.signature, signingDer);
168172

169173
if (!sigVerified) {
170174
revert Invalid_TCB_Cert_Signature();

src/bases/FmspcTcbDao.sol

+42-42
Original file line numberDiff line numberDiff line change
@@ -101,20 +101,40 @@ abstract contract FmspcTcbDao is DaoBase, SigVerifyBase {
101101
* @param tcbInfoObj See {FmspcTcbHelper.sol} to learn more about the structure definition
102102
*/
103103
function upsertFmspcTcb(TcbInfoJsonObj calldata tcbInfoObj) external returns (bytes32 attestationId) {
104-
_validateTcbInfo(tcbInfoObj);
105-
(
106-
bytes memory req,
107-
bytes32 key,
108-
uint8 tcbId,
109-
bytes6 fmspc,
110-
uint32 version,
111-
uint64 issueDateTimestamp,
112-
uint32 evaluationDataNumber
113-
) = _buildTcbAttestationRequest(tcbInfoObj);
114104
bytes32 hash = sha256(bytes(tcbInfoObj.tcbInfoStr));
105+
106+
// parse tcb info basic here so we can compute the key
107+
(
108+
TcbInfoBasic memory tcbInfo,
109+
string memory tcbLevelsString,
110+
string memory tdxModuleString,
111+
string memory tdxModuleIdentitiesString
112+
) = FmspcTcbLib.parseTcbString(tcbInfoObj.tcbInfoStr);
113+
bytes32 key = FMSPC_TCB_KEY(uint8(tcbInfo.id), tcbInfo.fmspc, tcbInfo.version);
114+
115+
_checkCollateralDuplicate(key, hash);
116+
_validateTcbInfo(tcbInfoObj);
117+
118+
bytes memory req = _buildTcbAttestationRequest(
119+
key,
120+
tcbInfoObj,
121+
tcbInfo,
122+
tcbLevelsString,
123+
tdxModuleString,
124+
tdxModuleIdentitiesString
125+
);
126+
115127
attestationId = _attestTcb(req, hash, key);
116-
_storeTcbInfoIssueEvaluation(key, issueDateTimestamp, evaluationDataNumber);
117-
emit UpsertedFmpscTcb(tcbId, fmspc, version);
128+
_storeTcbInfoIssueEvaluation(
129+
key,
130+
tcbInfo.issueDate,
131+
tcbInfo.evaluationDataNumber
132+
);
133+
emit UpsertedFmpscTcb(
134+
uint8(tcbInfo.id),
135+
tcbInfo.fmspc,
136+
tcbInfo.version
137+
);
118138
}
119139

120140
/**
@@ -142,55 +162,35 @@ abstract contract FmspcTcbDao is DaoBase, SigVerifyBase {
142162
/**
143163
* @notice constructs the TcbInfo.json attestation data
144164
*/
145-
function _buildTcbAttestationRequest(TcbInfoJsonObj calldata tcbInfoObj)
165+
function _buildTcbAttestationRequest(
166+
bytes32 key,
167+
TcbInfoJsonObj calldata tcbInfoObj,
168+
TcbInfoBasic memory tcbInfo,
169+
string memory tcbLevelsString,
170+
string memory tdxModuleString,
171+
string memory tdxModuleIdentitiesString
172+
)
146173
private
147174
view
148-
returns
149-
(
150-
bytes memory reqData,
151-
bytes32 key,
152-
uint8 id,
153-
bytes6 fmspc,
154-
uint32 version,
155-
uint64 issueDateTimestamp,
156-
uint32 evaluationDataNumber
157-
)
175+
returns (bytes memory reqData)
158176
{
159-
TcbInfoBasic memory tcbInfo;
160-
161-
string memory tcbLevelsString;
162-
string memory tdxModuleString;
163-
string memory tdxModuleIdentitiesString;
164-
(
165-
tcbInfo,
166-
tcbLevelsString,
167-
tdxModuleString,
168-
tdxModuleIdentitiesString
169-
) = FmspcTcbLib.parseTcbString(tcbInfoObj.tcbInfoStr);
170-
171177
// check expiration before continuing...
172178
if (block.timestamp < tcbInfo.issueDate || block.timestamp > tcbInfo.nextUpdate) {
173179
revert TCB_Expired();
174180
}
175181

176182
// Make sure new collateral is "newer"
177-
id = uint8(tcbInfo.id);
178-
fmspc = tcbInfo.fmspc;
179-
version = tcbInfo.version;
180-
key = FMSPC_TCB_KEY(id, fmspc, version);
181183
(uint64 existingIssueDate, uint32 existingEvaluationDataNumber) = _loadTcbInfoIssueEvaluation(key);
182184
if (existingIssueDate > 0) {
183185
/// I don't think there can be a scenario where an existing tcbinfo with a higher evaluation data number
184186
/// to be issued BEFORE a new tcbinfo with a lower evaluation data number
185187
bool outOfDate = tcbInfo.evaluationDataNumber < existingEvaluationDataNumber ||
186-
tcbInfo.issueDate < existingIssueDate;
188+
tcbInfo.issueDate <= existingIssueDate;
187189
if (outOfDate) {
188190
revert TCB_Out_Of_Date();
189191
}
190192
}
191193

192-
issueDateTimestamp = tcbInfo.issueDate;
193-
evaluationDataNumber = tcbInfo.evaluationDataNumber;
194194
TCBLevelsObj[] memory tcbLevels = FmspcTcbLib.parseTcbLevels(tcbInfo.version, tcbLevelsString);
195195
bytes memory encodedTcbLevels = _encodeTcbLevels(tcbLevels);
196196
if (tcbInfo.version < 3) {

src/bases/PckDao.sol

+13-10
Original file line numberDiff line numberDiff line change
@@ -277,27 +277,30 @@ abstract contract PckDao is DaoBase, SigVerifyBase {
277277
function _validatePck(CA ca, bytes memory der, bytes16 qeid, bytes2 pceid, bytes18 tcbm) internal view returns (bytes32 hash, bytes32 key) {
278278
X509CertObj memory pck = pckLib.parseX509DER(der);
279279

280-
// Step 0: Check whether the pck has expired
280+
hash = keccak256(pck.tbs);
281+
key = PCK_KEY(qeid, pceid, tcbm);
282+
283+
// Step 0: Check whether the certificate has been previously attested
284+
_checkCollateralDuplicate(key, hash);
285+
286+
// Step 1: Check whether the pck has expired
281287
bool notExpired = block.timestamp > pck.validityNotBefore && block.timestamp < pck.validityNotAfter;
282288
if (!notExpired) {
283289
revert Certificate_Expired();
284290
}
285291

286-
hash = keccak256(pck.tbs);
287-
key = PCK_KEY(qeid, pceid, tcbm);
288-
289-
// Step 1: Rollback prevention: new certificate should not have an issued date
292+
// Step 2: Rollback prevention: new certificate should not have an issued date
290293
// that is older than the existing certificate
291294
bytes memory existingData = _fetchDataFromResolver(key, false);
292295
if (existingData.length > 0) {
293296
(uint256 existingCertNotValidBefore, ) = pckLib.getCertValidity(existingData);
294-
bool outOfDate = existingCertNotValidBefore > pck.validityNotBefore;
297+
bool outOfDate = existingCertNotValidBefore >= pck.validityNotBefore;
295298
if (outOfDate) {
296299
revert Pck_Out_Of_Date();
297300
}
298301
}
299302

300-
// Step 2: Check Issuer and Subject names
303+
// Step 3: Check Issuer and Subject names
301304
string memory expectedIssuer;
302305
if (ca == CA.PLATFORM) {
303306
expectedIssuer = PCK_PLATFORM_CA_COMMON_NAME;
@@ -311,10 +314,10 @@ abstract contract PckDao is DaoBase, SigVerifyBase {
311314
revert Invalid_Subject_Name();
312315
}
313316

314-
// Step 3: validate PCEID and TCBm
317+
// Step 4: validate PCEID and TCBm
315318
_validatePckTcb(pceid, tcbm, der, pck.extensionPtr);
316319

317-
// Step 4: Check whether the pck has been revoked
320+
// Step 5: Check whether the pck has been revoked
318321
bytes memory crlData = _fetchDataFromResolver(Pcs.PCS_KEY(ca, true), false);
319322
if (crlData.length > 0) {
320323
bool revocable = crlLib.serialNumberIsRevoked(pck.serialNumber, crlData);
@@ -323,7 +326,7 @@ abstract contract PckDao is DaoBase, SigVerifyBase {
323326
}
324327
}
325328

326-
// Step 5: Check signature
329+
// Step 6: Check signature
327330
bytes memory issuerCert = _fetchDataFromResolver(Pcs.PCS_KEY(ca, false), false);
328331
if (issuerCert.length > 0) {
329332
bytes32 digest = sha256(pck.tbs);

src/bases/PcsDao.sol

+14-8
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ abstract contract PcsDao is DaoBase, SigVerifyBase {
142142

143143
function _upsertPcsCrl(CA ca, bytes calldata crl) private returns (bytes32 attestationId) {
144144
(bytes32 hash, bytes32 key) = _validatePcsCrl(ca, crl);
145+
145146
attestationId = _attestPcs(crl, hash, key);
146147

147148
emit UpsertedPCSCollateral(ca, true);
@@ -151,6 +152,12 @@ abstract contract PcsDao is DaoBase, SigVerifyBase {
151152
X509Helper x509Lib = X509Helper(x509);
152153
X509CertObj memory currentCert = x509Lib.parseX509DER(cert);
153154

155+
key = PCS_KEY(ca, false);
156+
hash = keccak256(currentCert.tbs);
157+
158+
// Step 0: Check whether the provided certificate has been previously attested
159+
_checkCollateralDuplicate(key, hash);
160+
154161
// Step 1: Check whether cert has expired
155162
bool validTimestamp =
156163
block.timestamp > currentCert.validityNotBefore &&
@@ -161,11 +168,10 @@ abstract contract PcsDao is DaoBase, SigVerifyBase {
161168

162169
// Step 2: Rollback prevention: new certificate should not have an issued date
163170
// that is older than the existing certificate
164-
key = PCS_KEY(ca, false);
165171
bytes memory existingData = _fetchDataFromResolver(key, false);
166172
if (existingData.length > 0) {
167173
(uint256 existingCertNotValidBefore, ) = x509Lib.getCertValidity(existingData);
168-
bool outOfDate = existingCertNotValidBefore > currentCert.validityNotBefore;
174+
bool outOfDate = existingCertNotValidBefore >= currentCert.validityNotBefore;
169175
if (outOfDate) {
170176
revert Certificate_Out_Of_Date();
171177
}
@@ -222,12 +228,15 @@ abstract contract PcsDao is DaoBase, SigVerifyBase {
222228
if (!sigVerified) {
223229
revert Invalid_Signature();
224230
}
225-
226-
hash = keccak256(currentCert.tbs);
227231
}
228232

229233
function _validatePcsCrl(CA ca, bytes calldata crl) private view returns (bytes32 hash, bytes32 key) {
230234
X509CRLObj memory currentCrl = crlLib.parseCRLDER(crl);
235+
236+
key = PCS_KEY(ca, true);
237+
hash = keccak256(currentCrl.tbs);
238+
239+
_checkCollateralDuplicate(key, hash);
231240

232241
// Step 1: Check whether CRL has expired
233242
bool validTimestamp =
@@ -239,11 +248,10 @@ abstract contract PcsDao is DaoBase, SigVerifyBase {
239248

240249
// Step 2: Rollback prevention: new CRL should not have an issued date
241250
// that is older than the existing CRL
242-
key = PCS_KEY(ca, true);
243251
bytes memory existingData = _fetchDataFromResolver(key, false);
244252
if (existingData.length > 0) {
245253
(uint256 existingCrlNotValidBefore, ) = crlLib.getCrlValidity(existingData);
246-
bool outOfDate = existingCrlNotValidBefore > currentCrl.validityNotBefore;
254+
bool outOfDate = existingCrlNotValidBefore >= currentCrl.validityNotBefore;
247255
if (outOfDate) {
248256
revert Certificate_Out_Of_Date();
249257
}
@@ -266,8 +274,6 @@ abstract contract PcsDao is DaoBase, SigVerifyBase {
266274
if (!sigVerified) {
267275
revert Invalid_Signature();
268276
}
269-
270-
hash = keccak256(currentCrl.tbs);
271277
}
272278

273279
function _getIssuer(CA ca) private view returns (bytes memory issuerCert) {

test/identity/AutomataEnclaveIdentityTest.t.sol

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pragma solidity ^0.8.0;
44
import "../pcs/PCSSetupBase.t.sol";
55
import "./IdentityConstants.t.sol";
66
import {AutomataEnclaveIdentityDao} from "../../src/automata_pccs/AutomataEnclaveIdentityDao.sol";
7+
import {DaoBase} from "../../src/bases/DaoBase.sol";
78

89
contract AutomataEnclaveIdentityDaoTest is PCSSetupBase, IdentityConstants {
910
function setUp() public override {
@@ -24,6 +25,12 @@ contract AutomataEnclaveIdentityDaoTest is PCSSetupBase, IdentityConstants {
2425
EnclaveIdentityJsonObj memory fetched = enclaveIdDao.getEnclaveIdentity(id, version);
2526
assertEq(fetched.signature, enclaveIdentityObj.signature);
2627
assertEq(keccak256(bytes(fetched.identityStr)), keccak256(bytes(enclaveIdentityObj.identityStr)));
28+
29+
// duplicate check
30+
vm.expectRevert(abi.encodeWithSelector(
31+
DaoBase.Duplicate_Collateral.selector
32+
));
33+
enclaveIdDao.upsertEnclaveIdentity(id, version, enclaveIdentityObj);
2734
}
2835

2936
function testTcbIssuerChain() public readAsAuthorizedCaller {

test/pcs/AutomataPckDaoTest.t.sol

+8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pragma solidity ^0.8.0;
33

44
import "../pcs/PCSSetupBase.t.sol";
55
import {AutomataPckDao} from "../../src/automata_pccs/AutomataPckDao.sol";
6+
import {DaoBase} from "../../src/bases/DaoBase.sol";
67

78
contract AutomataPckDaoTest is PCSSetupBase {
89
// TEMP: placeholder only, circle back on this to verify the inputs
@@ -38,6 +39,13 @@ contract AutomataPckDaoTest is PCSSetupBase {
3839
assertEq(keccak256(bytes(fetchedTcbm)), keccak256(bytes(tcbm)));
3940
}
4041

42+
function testDuplicatePckUpsert() public {
43+
vm.expectRevert(abi.encodeWithSelector(
44+
DaoBase.Duplicate_Collateral.selector
45+
));
46+
pck.upsertPckCert(CA.PLATFORM, qeid, pceid, tcbm, pckDer);
47+
}
48+
4149
function testPckIssuerChain() public readAsAuthorizedCaller {
4250
(bytes memory intermediateCert, bytes memory rootCert) = pck.getPckCertChain(CA.PLATFORM);
4351
assertEq(keccak256(platformDer), keccak256(intermediateCert));

0 commit comments

Comments
 (0)