Skip to content

Instantly share code, notes, and snippets.

@zhuowei
Created October 1, 2023 04:28
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zhuowei/5ba1d226effcb89dabb170e85c1455e3 to your computer and use it in GitHub Desktop.
Save zhuowei/5ba1d226effcb89dabb170e85c1455e3 to your computer and use it in GitHub Desktop.
Also see https://github.com/zhuowei/CoreTrustDemo/blob/main/littlemis.txt for my previous notes
first time X509ChainCheckPathWithOptions, param3 (options) is null
second time X509ChainCheckPathWithOptions, param3 (options) is set
-> This is the call out of CTEvaluateAMFICodeSignatureCMS_MaxDigestType, and is the one that sets the flags
struct ContentInfoSignedData {
int always4; // 0x0
void* someBufferFromCTParseContentInfoSignedDataArg6; // 0x8
// ?
int initially0; // 0x18
int* pointsToInitially0; // 0x20
// These are read by CMSParseSignerInfos
void* signerInfoBlobs; // 0x28, points to the root of the first signerInfo blob
size_t signerInfoBlobsSize; // 0x30?
SignerInfo* signerInfo; // 0x38 - points to fee20 above; the pointed SignerInfo is populated in CMSParseSignerInfos after validateSignerInfoAndChain callback
size_t signerInfoCount; // 0x40 - always set to 1 by CTParseSignerInfos
// more stuff
void* detachedOrAttachedData; // 0x48 // could be either attached or detached pkcs7
long detachedOrAttachedDataLen; // 0x50
// something
int param7FromCTParseContentInfoSignedData; // 0x60 - 0 for normal, 0x10 for PubKey - maxDigestType? compared with find_digest
uint64 something68FromValidateSignerInfo; // cached hash for detachedOrAttachedData
uint64 something70FromValidateSignerInfo; // hash length for detachedOrAttachedData
} // 0x78
// All of this is populated by CMSParseSignerInfos
struct SignerInfo {
int something; // 3 and 1 are checked in CMSBuildPath
CTAsn1Item partOfTheLastCertChainAuthorityMaybe; // 0x8
CTAsn1Item someOid; // 0x18
CTAsn1Item someOtherPartLastCert; // 0x28
CTAsn1Item someOid; // 0x38
// 0x10 blank 0x48 - ????
CTAsn1Item anotherCertificatePart; // 0x58
int hashTypeMaybe; // 0x68 - 4; written inside validateSigner
CTAsn1Item theHash; // 0x70
// 0x10 blank 0x80; written at end of validateSignerInfo
// 0x80, 0x88: signerHash, signerHashLength
// NEWLY ADDED BY PATCH - moved from thePath
// struct Certificate* pathChain; // 0x90
// struct Certificate* pathChainEnd; // 0x90
// int lastError; // 0xa0
}; // 0x90
struct ThePath {
struct Certificate* certificate; // 0x0 // REMOVED BY PATCH
struct Certificate* certificateEnd; // 0x8 // REMOVED BY PATCH
int lastError; // 0x10 // REMOVED BY PATCH
X509Policy* x509Policy; // 0x18
int validateSignerInfoFlagsSomething; // 0x20
}; // 0x28
CTParseContentInfoSignedData(struct ContentInfoSignedData* arg1, void* cmsData, size_t cmsLen, void* detachedData, size_t detachedDataLen, void* someBuffer, int arg7);
0x70002: CMSAttributeParseMessageDigest actual data hash and SignedData hash don't match
validateSignerInfoAndChain actually returns 0 and only sets the lastError on thePath
CMSParseSignerInfos also returns 0
CTParseSignerInfos is the only one that actually returns the error
but it always overwrites with the LAST one, eh.
0 is only written to the lastError if X509CertificateCheckSignatureDigest returns 0
CTParseSignerInfos(struct ContentInfoSignedData* something, SignerInfo* signerInfoOutput, ThePath* thePath, X509Policy* policyForValidateSignerInfoAndChain, int flagsMaybeForValidateSignerInfo);
somethingelse is size 0x90, memset to 0 - SignerInfoBuf - output of CMSParseSignerInfos, I guess?
This ThePath is passed into validateSignerInfoAndChain as param1 and is the path in that one's X509ChainCheckPath
ThePath's certificate is written in X509ChainResetChain <- CMSBuildPath <- validateSignerInfo, X509ChainBuildPathPartial
-> so validateSignerInfo calls builds the final path that we use to get flags, and then validateSignerInfoAndChain uses the path immediately?
validateSignerInfo is the only thing that actually checks if the blob is actually signed: validateSignerInfo via the ccdigest call.
-> So if you can have validateSignerInfo call X509ChainCheckPathWithOptions with one set of certs (since this doesn't check policy at all), but use something different for the other X509ChainCheckPathWithOptions call?
validateSignerInfoAndChain(ThePath* param1, struct ContentInfoSignedData* something, int theArg3);
... so we need validateSignerInfo to populate param1 wrong?
signerInfoOutput is actually populated by
validateSignerInfoAndChain(ThePath* thePath, struct ContentInfoSignedData* something, SignerInfo* what); // ?????
If validateSignerInfoAndChain succeeds, this signerInfo is then copied back to CTParseSignerInfos's signerInfoOutput by CTParseSignerInfos
patch moves the certificates + lastError into SignerInfo
_validateSignerInfo can return 0x5001e (digest length invalid) which will skip to the next one
if none of them worked, CTParseSignerInfos returns 0x70001 (the error set initially)
CMSEncoderAddSigners
last signer wins
openssl cms -cmsout -in ../cmsblob.der -inform der -print
X509CertificateCheckSignatureDigest(int something, struct Certificate* theCert, CTAsn1Item theHash, CTAsn1Item someOidFromSignerInfo, CTAsn1Item anotherCertificatePartFromSignerInfo);
theHash here is the hash of the signed data (the cdblob)
so:
- last SignerInfo wins for both the output cert chain and the SignerInfo output struct
- each SignerInfo needs to validate correctly
- except for the lastError: this one gets overwritten so as long as the last one validates correctly, it's fine
- CMSBuildPath writes directly to the output cert chain
- CMSBuildPath and X509CertificateCheckSignatureDigest is run every time, so if I want to have it set lastError to 0 it needs to rebuild the path and validate the digest to be equal
0x80008 and 0x80009 are in X509ChainBuildPathPartial
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment