|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 2001 - 2001
//
// File: testutil.cpp
// Contents: Test Utility API Prototypes and Definitions
//
// History: 29-Jan-01 philh created
//
//--------------------------------------------------------------------------
#include <windows.h>
#include <assert.h>
#include "testutil.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <time.h>
#include <stddef.h>
#define TESTUTIL_MAX_EXT_CNT 30
#define TESTUTIL_MAX_ATTR_CNT 30
//+-------------------------------------------------------------------------
// Error output routines
//--------------------------------------------------------------------------
VOID PrintErr( IN LPCSTR pszMsg, IN LONG lErr ) { printf("%s failed => 0x%x (%d) \n", pszMsg, lErr, lErr); }
VOID PrintLastError( IN LPCSTR pszMsg ) { DWORD dwErr = GetLastError(); printf("%s failed => 0x%x (%d) \n", pszMsg, dwErr, dwErr); }
//+-------------------------------------------------------------------------
// Test allocation and free routines
//--------------------------------------------------------------------------
LPVOID TestAlloc( IN size_t cbBytes ) { LPVOID pv; pv = malloc(cbBytes); if (pv == NULL) PrintErr("TestAlloc", (LONG) GetLastError()); return pv; }
VOID TestFree( IN LPVOID pv ) { if (pv) free(pv); }
CRYPT_DECODE_PARA TestDecodePara = { offsetof(CRYPT_DECODE_PARA, pfnFree) + sizeof(TestDecodePara.pfnFree), TestAlloc, TestFree };
//+-------------------------------------------------------------------------
// Allocate and convert a multi-byte string to a wide string. TestFree()
// must be called to free the returned wide string.
//--------------------------------------------------------------------------
LPWSTR AllocAndSzToWsz( IN LPCSTR psz ) { size_t cb; LPWSTR pwsz = NULL;
if (-1 == (cb = mbstowcs( NULL, psz, strlen(psz)))) goto bad_param; cb += 1; // terminating NULL
if (NULL == (pwsz = (LPWSTR)TestAlloc( cb * sizeof(WCHAR)))) { PrintLastError("AllocAndSzToWsz"); goto failed; } if (-1 == mbstowcs( pwsz, psz, cb)) goto bad_param; goto common_return;
bad_param: printf("failed => Bad AllocAndSzToWsz"); failed: if (pwsz) { TestFree(pwsz); pwsz = NULL; } common_return: return pwsz; }
//+-------------------------------------------------------------------------
// Conversions functions between encoded OID and the dot string
// representation
//--------------------------------------------------------------------------
#define MAX_OID_STRING_LEN 0x80
#define MAX_ENCODED_OID_LEN 0x80
// Encoded Attribute
//
// Attribute ::= SEQUENCE {
// type EncodedObjectID,
// values AttributeSetValue
// } --#public--
//
// AttributeSetValue ::= SET OF NOCOPYANY
BOOL EncodedOIDToDot( IN PCRYPT_DER_BLOB pEncodedOIDBlob, OUT CHAR rgszOID[MAX_OID_STRING_LEN] ) { BOOL fResult; DWORD cbOID = pEncodedOIDBlob->cbData; const BYTE *pbOID = pEncodedOIDBlob->pbData; BYTE rgbEncodedAttr[MAX_ENCODED_OID_LEN]; PCRYPT_ATTRIBUTE pAttr = NULL; DWORD cbAttr;
// Convert the OID into an encoded Attribute that we can
// decode to get the OID string.
if (0 == cbOID || MAX_OID_STRING_LEN - 6 < cbOID) { strcpy(rgszOID, "Invalid OID length"); return FALSE; }
rgbEncodedAttr[0] = MINASN1_TAG_SEQ; rgbEncodedAttr[1] = (BYTE) (2 + cbOID + 2); rgbEncodedAttr[2] = MINASN1_TAG_OID; rgbEncodedAttr[3] = (BYTE) cbOID; memcpy(&rgbEncodedAttr[4], pbOID, cbOID); rgbEncodedAttr[4 + cbOID + 0] = MINASN1_TAG_SET; rgbEncodedAttr[4 + cbOID + 1] = 0;
if (!CryptDecodeObjectEx( X509_ASN_ENCODING, PKCS_ATTRIBUTE, rgbEncodedAttr, 2 + 2 + cbOID + 2, CRYPT_DECODE_NOCOPY_FLAG | CRYPT_DECODE_ALLOC_FLAG, &TestDecodePara, (void *) &pAttr, &cbAttr )) { strcpy(rgszOID, "Decode OID failed"); return FALSE; }
if (strlen(pAttr->pszObjId) >= MAX_OID_STRING_LEN) { strcpy(rgszOID, "Invalid OID length"); fResult = FALSE; } else { strcpy(rgszOID, pAttr->pszObjId); fResult = TRUE; }
TestFree(pAttr); return fResult; }
const BYTE rgbSeqTag[] = {MINASN1_TAG_SEQ, 0}; const BYTE rgbOIDTag[] = {MINASN1_TAG_OID, 0};
const MINASN1_EXTRACT_VALUE_PARA rgExtractAttrPara[] = { // 0 - Attribute ::= SEQUENCE {
MINASN1_STEP_INTO_VALUE_OP, 0, rgbSeqTag, // 1 - type EncodedObjectID,
MINASN1_RETURN_CONTENT_BLOB_FLAG | MINASN1_STEP_OVER_VALUE_OP, 0, rgbOIDTag, };
#define ATTR_VALUE_COUNT \
(sizeof(rgExtractAttrPara) / sizeof(rgExtractAttrPara[0]))
BOOL DotToEncodedOID( IN LPCSTR pszOID, OUT BYTE rgbEncodedOID[MAX_ENCODED_OID_LEN], OUT DWORD *pcbEncodedOID ) { BOOL fResult; CRYPT_ATTRIBUTE Attr; BYTE rgbEncoded[512]; DWORD cbEncoded; CRYPT_DER_BLOB rgValueBlob[1]; DWORD cValue; DWORD i; BYTE *pb; DWORD cb;
// Encode an Attribute that only has the OID.
Attr.pszObjId = (LPSTR) pszOID; Attr.cValue = 0; Attr.rgValue = NULL;
cbEncoded = sizeof(rgbEncoded); if (!CryptEncodeObject( X509_ASN_ENCODING, PKCS_ATTRIBUTE, &Attr, rgbEncoded, &cbEncoded )) { printf("\n"); printf("Asn1Encode(%s)", pszOID); PrintLastError(""); goto ErrorReturn; }
cValue = ATTR_VALUE_COUNT; if (0 >= MinAsn1ExtractValues( rgbEncoded, cbEncoded, &cValue, rgExtractAttrPara, 1, rgValueBlob )) { printf("Unable to encode OID: %s\n", pszOID); goto ErrorReturn; }
pb = rgValueBlob[0].pbData; cb = rgValueBlob[0].cbData;
if (0 == cb || MAX_ENCODED_OID_LEN < cb) { printf("Invalid length for OID: %s\n", pszOID); goto ErrorReturn; } memcpy(rgbEncodedOID, pb, cb); *pcbEncodedOID = cb;
fResult = TRUE; CommonReturn: return fResult;
ErrorReturn: fResult = FALSE; *pcbEncodedOID = 0; goto CommonReturn; }
//+-------------------------------------------------------------------------
// Functions to print bytes
//--------------------------------------------------------------------------
VOID PrintBytes( IN PCRYPT_DER_BLOB pBlob ) { DWORD cb = pBlob->cbData; BYTE *pb = pBlob->pbData;
if (0 == cb) { printf(" No Bytes\n"); return; }
for (; 0 < cb; cb--, pb++) printf(" %02X", *pb);
printf("\n"); }
#define CROW 16
VOID PrintMultiLineBytes( IN LPCSTR pszHdr, IN PCRYPT_DER_BLOB pBlob ) { DWORD cbSize = pBlob->cbData; BYTE *pb = pBlob->pbData; DWORD cb, i;
if (cbSize == 0) { printf("%s No Bytes\n", pszHdr); return; }
while (cbSize > 0) { printf("%s", pszHdr); cb = min(CROW, cbSize); cbSize -= cb; for (i = 0; i<cb; i++) printf(" %02X", pb[i]); for (i = cb; i<CROW; i++) printf(" "); printf(" '"); for (i = 0; i<cb; i++) if (pb[i] >= 0x20 && pb[i] <= 0x7f) printf("%c", pb[i]); else printf("."); pb += cb; printf("'\n"); } }
//+-------------------------------------------------------------------------
// Allocate and read an encoded DER blob from a file
//--------------------------------------------------------------------------
BOOL ReadDERFromFile( IN LPCSTR pszFileName, OUT PBYTE *ppbDER, OUT PDWORD pcbDER ) { BOOL fRet; HANDLE hFile = 0; PBYTE pbDER = NULL; DWORD cbDER; DWORD cbRead;
if( INVALID_HANDLE_VALUE == (hFile = CreateFile( pszFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL))) { printf( "can't open %s\n", pszFileName); goto ErrorReturn; }
cbDER = GetFileSize( hFile, NULL); if (cbDER == 0) { printf( "empty file %s\n", pszFileName); goto ErrorReturn; } if (NULL == (pbDER = (PBYTE)TestAlloc(cbDER))) { printf( "can't alloc %d bytes\n", cbDER); goto ErrorReturn; } if (!ReadFile( hFile, pbDER, cbDER, &cbRead, NULL) || (cbRead != cbDER)) { printf( "can't read %s\n", pszFileName); goto ErrorReturn; }
*ppbDER = pbDER; *pcbDER = cbDER; fRet = TRUE; CommonReturn: if (hFile) CloseHandle(hFile); return fRet; ErrorReturn: if (pbDER) TestFree(pbDER); *ppbDER = NULL; *pcbDER = 0; fRet = FALSE; goto CommonReturn; }
//+-------------------------------------------------------------------------
// Write an encoded DER blob to a file
//--------------------------------------------------------------------------
BOOL WriteDERToFile( IN LPCSTR pszFileName, IN PBYTE pbDER, IN DWORD cbDER ) { BOOL fResult;
// Write the Encoded Blob to the file
HANDLE hFile; hFile = CreateFile(pszFileName, GENERIC_WRITE, 0, // fdwShareMode
NULL, // lpsa
CREATE_ALWAYS, 0, // fdwAttrsAndFlags
0); // TemplateFile
if (INVALID_HANDLE_VALUE == hFile) { fResult = FALSE; PrintLastError("WriteDERToFile::CreateFile"); } else { DWORD dwBytesWritten; if (!(fResult = WriteFile( hFile, pbDER, cbDER, &dwBytesWritten, NULL // lpOverlapped
))) PrintLastError("WriteDERToFile::WriteFile"); CloseHandle(hFile); } return fResult; }
//+-------------------------------------------------------------------------
// Display functions
//--------------------------------------------------------------------------
VOID DisplayCert( IN CRYPT_DER_BLOB rgCertBlob[MINASN1_CERT_BLOB_CNT], IN BOOL fVerbose ) { if (0 != rgCertBlob[MINASN1_CERT_VERSION_IDX].cbData) { printf("Version:"); PrintBytes(&rgCertBlob[MINASN1_CERT_VERSION_IDX]); }
printf("Subject:"); DisplayName(&rgCertBlob[MINASN1_CERT_SUBJECT_IDX]);
printf("Issuer:"); DisplayName(&rgCertBlob[MINASN1_CERT_ISSUER_IDX]); printf("SerialNumber:"); PrintBytes(&rgCertBlob[MINASN1_CERT_SERIAL_NUMBER_IDX]);
printf("NotBefore:\n"); PrintMultiLineBytes(" ", &rgCertBlob[MINASN1_CERT_NOT_BEFORE_IDX]); printf("NotAfter:\n"); PrintMultiLineBytes(" ", &rgCertBlob[MINASN1_CERT_NOT_AFTER_IDX]);
if (0 != rgCertBlob[MINASN1_CERT_ISSUER_UNIQUE_ID_IDX].cbData) { printf("IssuerUniqueId:\n"); PrintMultiLineBytes(" ", &rgCertBlob[MINASN1_CERT_ISSUER_UNIQUE_ID_IDX]); }
if (0 != rgCertBlob[MINASN1_CERT_SUBJECT_UNIQUE_ID_IDX].cbData) { printf("SubjectUniqueId:\n"); PrintMultiLineBytes(" ", &rgCertBlob[MINASN1_CERT_SUBJECT_UNIQUE_ID_IDX]); }
if (fVerbose) { CRYPT_DER_BLOB rgPubKeyInfoBlob[MINASN1_PUBKEY_INFO_BLOB_CNT]; CRYPT_DER_BLOB rgPubKeyAlgIdBlob[MINASN1_ALGID_BLOB_CNT]; CRYPT_DER_BLOB rgSignAlgIdBlob[MINASN1_ALGID_BLOB_CNT];
if (0 >= MinAsn1ParsePublicKeyInfo( &rgCertBlob[MINASN1_CERT_PUBKEY_INFO_IDX], rgPubKeyInfoBlob ) || 0 >= MinAsn1ParseAlgorithmIdentifier( &rgPubKeyInfoBlob[MINASN1_PUBKEY_INFO_ALGID_IDX], rgPubKeyAlgIdBlob )) printf("PublicKeyInfo: parse failed\n"); else { CHAR rgszOID[MAX_OID_STRING_LEN]; CRYPT_DER_BLOB rgRSAPubKeyBlob[MINASN1_RSA_PUBKEY_BLOB_CNT];
EncodedOIDToDot(&rgPubKeyAlgIdBlob[MINASN1_ALGID_OID_IDX], rgszOID);
printf("PublicKeyInfo.Algorithm: %s\n", rgszOID);
if (0 != rgPubKeyAlgIdBlob[MINASN1_ALGID_PARA_IDX].cbData) { printf("PublicKeyInfo.Algorithm.Parameters:\n"); PrintMultiLineBytes(" ", &rgPubKeyAlgIdBlob[MINASN1_ALGID_PARA_IDX]); }
if (0 >= MinAsn1ParseRSAPublicKey( &rgPubKeyInfoBlob[MINASN1_PUBKEY_INFO_PUBKEY_IDX], rgRSAPubKeyBlob )) { printf("PublicKeyInfo.PublicKey:\n"); PrintMultiLineBytes(" ", &rgPubKeyInfoBlob[MINASN1_PUBKEY_INFO_PUBKEY_IDX]); } else { DWORD dwByteLen;
dwByteLen = rgRSAPubKeyBlob[MINASN1_RSA_PUBKEY_MODULUS_IDX].cbData; if (0 < dwByteLen && 0 == rgRSAPubKeyBlob[ MINASN1_RSA_PUBKEY_MODULUS_IDX].pbData[0]) dwByteLen--; printf("PublicKeyInfo.RSAPublicKey.BitLength: %d\n", dwByteLen * 8);
printf("PublicKeyInfo.RSAPublicKey.Modulus:\n"); PrintMultiLineBytes(" ", &rgRSAPubKeyBlob[MINASN1_RSA_PUBKEY_MODULUS_IDX]); printf("PublicKeyInfo.RSAPublicKey.Exponent:"); PrintBytes(&rgRSAPubKeyBlob[MINASN1_RSA_PUBKEY_EXPONENT_IDX]); } }
if ( 0 >= MinAsn1ParseAlgorithmIdentifier( &rgCertBlob[MINASN1_CERT_SIGN_ALGID_IDX], rgSignAlgIdBlob )) printf("SignatureAlgorithm: parse failed\n"); else { CHAR rgszOID[MAX_OID_STRING_LEN]; EncodedOIDToDot(&rgSignAlgIdBlob[MINASN1_ALGID_OID_IDX], rgszOID);
printf("Signature.Algorithm: %s\n", rgszOID); if (0 != rgSignAlgIdBlob[MINASN1_ALGID_PARA_IDX].cbData) { printf("Signature.Algorithm.Parameters:\n"); PrintMultiLineBytes(" ", &rgSignAlgIdBlob[MINASN1_ALGID_PARA_IDX]); } }
printf("Signature.Content\n"); PrintMultiLineBytes(" ", &rgCertBlob[MINASN1_CERT_SIGNATURE_IDX]);
DisplayExts(&rgCertBlob[MINASN1_CERT_EXTS_IDX]); } }
VOID DisplayName( IN PCRYPT_DER_BLOB pNameValueBlob ) { WCHAR wszName[512];
CertNameToStrW( X509_ASN_ENCODING, pNameValueBlob, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, wszName, 512 );
printf(" <%S>\n", wszName); }
VOID DisplayExts( IN PCRYPT_DER_BLOB pExtsValueBlob ) { DWORD cExt; DWORD i; CRYPT_DER_BLOB rgrgExtBlob[TESTUTIL_MAX_EXT_CNT][MINASN1_EXT_BLOB_CNT];
if (0 == pExtsValueBlob->cbData) return;
cExt = TESTUTIL_MAX_EXT_CNT; if (0 >= MinAsn1ParseExtensions( pExtsValueBlob, &cExt, rgrgExtBlob )) { printf("Extensions: parse failed\n"); return; }
for (i = 0; i < cExt; i++) { CHAR rgszOID[MAX_OID_STRING_LEN];
EncodedOIDToDot(&rgrgExtBlob[i][MINASN1_EXT_OID_IDX], rgszOID); printf("Extension[%d] %s Critical: ", i, rgszOID); if (0 != rgrgExtBlob[i][MINASN1_EXT_CRITICAL_IDX].cbData && 0 != rgrgExtBlob[i][MINASN1_EXT_CRITICAL_IDX].pbData[0]) printf("TRUE\n"); else printf("FALSE\n");
PrintMultiLineBytes(" ", &rgrgExtBlob[i][MINASN1_EXT_VALUE_IDX]); } }
VOID DisplayCTL( IN PCRYPT_DER_BLOB pEncodedContentBlob, IN BOOL fVerbose ) { CRYPT_DER_BLOB rgCTLBlob[MINASN1_CTL_BLOB_CNT];
if (0 >= MinAsn1ParseCTL( pEncodedContentBlob, rgCTLBlob )) { printf("CTL: parse failed\n"); return; }
if (0 != rgCTLBlob[MINASN1_CTL_VERSION_IDX].cbData) { printf("Version:"); PrintBytes(&rgCTLBlob[MINASN1_CTL_VERSION_IDX]); }
printf("SubjectUsage:\n"); PrintMultiLineBytes(" ", &rgCTLBlob[MINASN1_CTL_SUBJECT_USAGE_IDX]);
if (0 != rgCTLBlob[MINASN1_CTL_LIST_ID_IDX].cbData) { printf("ListIdentifier:\n"); PrintMultiLineBytes(" ", &rgCTLBlob[MINASN1_CTL_LIST_ID_IDX]); }
if (0 != rgCTLBlob[MINASN1_CTL_SEQUENCE_NUMBER_IDX].cbData) { printf("SequenceNumber:"); PrintBytes(&rgCTLBlob[MINASN1_CTL_SEQUENCE_NUMBER_IDX]); }
printf("ThisUpdate:\n"); PrintMultiLineBytes(" ", &rgCTLBlob[MINASN1_CTL_THIS_UPDATE_IDX]); if (0 != rgCTLBlob[MINASN1_CTL_NEXT_UPDATE_IDX].cbData) { printf("NextUpdate:\n"); PrintMultiLineBytes(" ", &rgCTLBlob[MINASN1_CTL_NEXT_UPDATE_IDX]); }
printf("SubjectAlgorithmIdentifier:\n"); PrintMultiLineBytes(" ", &rgCTLBlob[MINASN1_CTL_SUBJECT_ALGID_IDX]);
if (fVerbose) DisplayExts(&rgCTLBlob[MINASN1_CTL_EXTS_IDX]);
if (0 != rgCTLBlob[MINASN1_CTL_SUBJECTS_IDX].cbData) { DWORD cbEncoded; const BYTE *pbEncoded; DWORD i;
printf("\n");
// Advance past the Subjects' outer tag and length
if (0 >= MinAsn1ExtractContent( rgCTLBlob[MINASN1_CTL_SUBJECTS_IDX].pbData, rgCTLBlob[MINASN1_CTL_SUBJECTS_IDX].cbData, &cbEncoded, &pbEncoded )) { printf("Subjects: parse failed\n"); return; }
// Loop through the encoded subjects
for (i = 0; 0 != cbEncoded; i++) { LONG cbSubject; CRYPT_DER_BLOB rgCTLSubjectBlob[MINASN1_CTL_SUBJECT_BLOB_CNT];
printf("---- Subject[%d] ----\n", i);
cbSubject = MinAsn1ParseCTLSubject( pbEncoded, cbEncoded, rgCTLSubjectBlob ); if (0 >= cbSubject) { printf("Subject: parse failed\n"); return; }
printf("Identifier:\n"); PrintMultiLineBytes(" ", &rgCTLSubjectBlob[MINASN1_CTL_SUBJECT_ID_IDX]);
if (fVerbose) { DisplayAttrs("", &rgCTLSubjectBlob[MINASN1_CTL_SUBJECT_ATTRS_IDX]); printf("\n"); }
pbEncoded += cbSubject; cbEncoded -= cbSubject; } } }
VOID DisplayAttrs( IN LPCSTR pszHdr, IN PCRYPT_DER_BLOB pAttrsValueBlob ) { DWORD cAttr; DWORD i; CRYPT_DER_BLOB rgrgAttrBlob[TESTUTIL_MAX_ATTR_CNT][MINASN1_ATTR_BLOB_CNT];
if (0 == pAttrsValueBlob->cbData) return;
cAttr = TESTUTIL_MAX_ATTR_CNT; if (0 >= MinAsn1ParseAttributes( pAttrsValueBlob, &cAttr, rgrgAttrBlob )) { printf("%sAttributes: parse failed\n", pszHdr); return; }
for (i = 0; i < cAttr; i++) { CHAR rgszOID[MAX_OID_STRING_LEN];
EncodedOIDToDot(&rgrgAttrBlob[i][MINASN1_ATTR_OID_IDX], rgszOID); printf("%sAttribute[%d] %s:\n", pszHdr, i, rgszOID); PrintMultiLineBytes(" ", &rgrgAttrBlob[i][MINASN1_ATTR_VALUE_IDX]); } }
|