Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

361 lines
12 KiB

  1. //+-------------------------------------------------------------------------
  2. // Microsoft Windows
  3. //
  4. // Copyright (C) Microsoft Corporation, 2001 - 2001
  5. //
  6. // File: verdata.cpp
  7. //
  8. // Contents: Minimal Cryptographic functions to verify PKCS #7 Signed Data
  9. // message
  10. //
  11. //
  12. // Functions: MinCryptVerifySignedData
  13. //
  14. // History: 19-Jan-01 philh created
  15. //--------------------------------------------------------------------------
  16. #include "global.hxx"
  17. #define MAX_SIGNED_DATA_CERT_CNT 10
  18. #define MAX_SIGNED_DATA_AUTH_ATTR_CNT 10
  19. // #define szOID_RSA_signedData "1.2.840.113549.1.7.2"
  20. const BYTE rgbOID_RSA_signedData[] =
  21. {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02};
  22. // #define szOID_RSA_messageDigest "1.2.840.113549.1.9.4"
  23. const BYTE rgbOID_RSA_messageDigest[] =
  24. {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x04};
  25. const CRYPT_DER_BLOB RSA_messageDigestEncodedOIDBlob = {
  26. sizeof(rgbOID_RSA_messageDigest),
  27. (BYTE *) rgbOID_RSA_messageDigest
  28. };
  29. PCRYPT_DER_BLOB
  30. WINAPI
  31. I_MinCryptFindSignerCertificateByIssuerAndSerialNumber(
  32. IN PCRYPT_DER_BLOB pIssuerNameValueBlob,
  33. IN PCRYPT_DER_BLOB pIssuerSerialNumberContentBlob,
  34. IN DWORD cCert,
  35. IN CRYPT_DER_BLOB rgrgCertBlob[][MINASN1_CERT_BLOB_CNT]
  36. )
  37. {
  38. DWORD i;
  39. const BYTE *pbName = pIssuerNameValueBlob->pbData;
  40. DWORD cbName = pIssuerNameValueBlob->cbData;
  41. const BYTE *pbSerial = pIssuerSerialNumberContentBlob->pbData;
  42. DWORD cbSerial = pIssuerSerialNumberContentBlob->cbData;
  43. if (0 == cbName || 0 == cbSerial)
  44. return NULL;
  45. for (i = 0; i < cCert; i++) {
  46. PCRYPT_DER_BLOB rgCert = rgrgCertBlob[i];
  47. if (cbName == rgCert[MINASN1_CERT_ISSUER_IDX].cbData &&
  48. cbSerial == rgCert[MINASN1_CERT_SERIAL_NUMBER_IDX].cbData
  49. &&
  50. 0 == memcmp(pbSerial,
  51. rgCert[MINASN1_CERT_SERIAL_NUMBER_IDX].pbData,
  52. cbSerial)
  53. &&
  54. 0 == memcmp(pbName,
  55. rgCert[MINASN1_CERT_ISSUER_IDX].pbData,
  56. cbName))
  57. return rgCert;
  58. }
  59. return NULL;
  60. }
  61. // Verifies that the input hash matches the
  62. // szOID_RSA_messageDigest ("1.2.840.113549.1.9.4") authenticated attribute.
  63. //
  64. // Replaces the input hash with a hash of the authenticated attributes.
  65. LONG
  66. WINAPI
  67. I_MinCryptVerifySignerAuthenticatedAttributes(
  68. IN ALG_ID HashAlgId,
  69. IN OUT BYTE rgbHash[MINCRYPT_MAX_HASH_LEN],
  70. IN OUT DWORD *pcbHash,
  71. IN PCRYPT_DER_BLOB pAttrsValueBlob
  72. )
  73. {
  74. LONG lErr;
  75. DWORD cAttr;
  76. CRYPT_DER_BLOB rgrgAttrBlob[MAX_SIGNED_DATA_AUTH_ATTR_CNT][MINASN1_ATTR_BLOB_CNT];
  77. PCRYPT_DER_BLOB rgDigestAuthAttr;
  78. const BYTE *pbDigestAuthValue;
  79. DWORD cbDigestAuthValue;
  80. CRYPT_DER_BLOB rgAuthHashBlob[2];
  81. const BYTE bTagSet = MINASN1_TAG_SET;
  82. // Parse the authenticated attributes
  83. cAttr = MAX_SIGNED_DATA_AUTH_ATTR_CNT;
  84. if (0 >= MinAsn1ParseAttributes(
  85. pAttrsValueBlob,
  86. &cAttr,
  87. rgrgAttrBlob) || 0 == cAttr)
  88. goto MissingAuthAttrs;
  89. // Find the szOID_RSA_messageDigest ("1.2.840.113549.1.9.4")
  90. // attribute value
  91. rgDigestAuthAttr = MinAsn1FindAttribute(
  92. (PCRYPT_DER_BLOB) &RSA_messageDigestEncodedOIDBlob,
  93. cAttr,
  94. rgrgAttrBlob
  95. );
  96. if (NULL == rgDigestAuthAttr)
  97. goto MissingDigestAuthAttr;
  98. // Skip past the digest's outer OCTET tag and length octets
  99. if (0 >= MinAsn1ExtractContent(
  100. rgDigestAuthAttr[MINASN1_ATTR_VALUE_IDX].pbData,
  101. rgDigestAuthAttr[MINASN1_ATTR_VALUE_IDX].cbData,
  102. &cbDigestAuthValue,
  103. &pbDigestAuthValue
  104. ))
  105. goto InvalidDigestAuthAttr;
  106. // Check that the authenticated digest bytes match the input
  107. // content hash.
  108. if (*pcbHash != cbDigestAuthValue ||
  109. 0 != memcmp(rgbHash, pbDigestAuthValue, cbDigestAuthValue))
  110. goto InvalidContentHash;
  111. // Hash the authenticated attributes. This hash will be compared against
  112. // the decrypted signature.
  113. // Note, the authenticated attributes "[0] Implicit" tag needs to be changed
  114. // to a "SET OF" tag before doing the hash.
  115. rgAuthHashBlob[0].pbData = (BYTE *) &bTagSet;
  116. rgAuthHashBlob[0].cbData = 1;
  117. assert(0 < pAttrsValueBlob->cbData);
  118. rgAuthHashBlob[1].pbData = pAttrsValueBlob->pbData + 1;
  119. rgAuthHashBlob[1].cbData = pAttrsValueBlob->cbData - 1;
  120. lErr = MinCryptHashMemory(
  121. HashAlgId,
  122. 2, // cBlob
  123. rgAuthHashBlob,
  124. rgbHash,
  125. pcbHash
  126. );
  127. CommonReturn:
  128. return lErr;
  129. MissingAuthAttrs:
  130. MissingDigestAuthAttr:
  131. InvalidDigestAuthAttr:
  132. lErr = CRYPT_E_AUTH_ATTR_MISSING;
  133. goto CommonReturn;
  134. InvalidContentHash:
  135. lErr = CRYPT_E_HASH_VALUE;
  136. goto CommonReturn;
  137. }
  138. //+-------------------------------------------------------------------------
  139. // Function: MinCryptVerifySignedData
  140. //
  141. // Verifies an ASN.1 encoded PKCS #7 Signed Data Message.
  142. //
  143. // Assumes the PKCS #7 message is definite length encoded.
  144. // Assumes PKCS #7 version 1.5, ie, not the newer CMS version.
  145. // We only look at the first signer.
  146. //
  147. // The Signed Data message is parsed. Its signature is verified. Its
  148. // signer certificate chain is verified to a baked in root public key.
  149. //
  150. // If the Signed Data was successfully verified, ERROR_SUCCESS is returned.
  151. // Otherwise, a nonzero error code is returned.
  152. //
  153. // Here are some interesting errors that can be returned:
  154. // CRYPT_E_BAD_MSG - unable to ASN1 parse as a signed data message
  155. // ERROR_NO_DATA - the content is empty
  156. // CRYPT_E_NO_SIGNER - not signed or unable to find signer cert
  157. // CRYPT_E_UNKNOWN_ALGO- unknown MD5 or SHA1 ASN.1 algorithm identifier
  158. // CERT_E_UNTRUSTEDROOT- the signer chain's root wasn't baked in
  159. // CERT_E_CHAINING - unable to build signer chain to a root
  160. // CRYPT_E_AUTH_ATTR_MISSING - missing digest authenticated attribute
  161. // CRYPT_E_HASH_VALUE - content hash != authenticated digest attribute
  162. // NTE_BAD_ALGID - unsupported hash or public key algorithm
  163. // NTE_BAD_PUBLIC_KEY - not a valid RSA public key
  164. // NTE_BAD_SIGNATURE - bad PKCS #7 or signer chain signature
  165. //
  166. // The rgVerSignedDataBlob[] is updated with pointer to and length of the
  167. // following fields in the encoded PKCS #7 message.
  168. //--------------------------------------------------------------------------
  169. LONG
  170. WINAPI
  171. MinCryptVerifySignedData(
  172. IN const BYTE *pbEncoded,
  173. IN DWORD cbEncoded,
  174. OUT CRYPT_DER_BLOB rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_BLOB_CNT]
  175. )
  176. {
  177. LONG lErr;
  178. CRYPT_DER_BLOB rgParseSignedDataBlob[MINASN1_SIGNED_DATA_BLOB_CNT];
  179. DWORD cCert;
  180. CRYPT_DER_BLOB rgrgCertBlob[MAX_SIGNED_DATA_CERT_CNT][MINASN1_CERT_BLOB_CNT];
  181. PCRYPT_DER_BLOB rgSignerCert;
  182. ALG_ID HashAlgId;
  183. BYTE rgbHash[MINCRYPT_MAX_HASH_LEN];
  184. DWORD cbHash;
  185. CRYPT_DER_BLOB ContentBlob;
  186. memset(rgVerSignedDataBlob, 0,
  187. sizeof(CRYPT_DER_BLOB) * MINCRYPT_VER_SIGNED_DATA_BLOB_CNT);
  188. // Parse the message and verify that it's ASN.1 PKCS #7 SignedData
  189. if (0 >= MinAsn1ParseSignedData(
  190. pbEncoded,
  191. cbEncoded,
  192. rgParseSignedDataBlob
  193. ))
  194. goto ParseSignedDataError;
  195. // Only support szOID_RSA_signedData - "1.2.840.113549.1.7.2"
  196. if (sizeof(rgbOID_RSA_signedData) !=
  197. rgParseSignedDataBlob[MINASN1_SIGNED_DATA_OUTER_OID_IDX].cbData
  198. ||
  199. 0 != memcmp(rgbOID_RSA_signedData,
  200. rgParseSignedDataBlob[MINASN1_SIGNED_DATA_OUTER_OID_IDX].pbData,
  201. sizeof(rgbOID_RSA_signedData)))
  202. goto NotSignedDataOID;
  203. // Verify this isn't an empty SignedData message
  204. if (0 == rgParseSignedDataBlob[MINASN1_SIGNED_DATA_CONTENT_OID_IDX].cbData
  205. ||
  206. 0 == rgParseSignedDataBlob[MINASN1_SIGNED_DATA_CONTENT_DATA_IDX].cbData)
  207. goto NoContent;
  208. rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_CONTENT_OID_IDX] =
  209. rgParseSignedDataBlob[MINASN1_SIGNED_DATA_CONTENT_OID_IDX];
  210. rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_CONTENT_DATA_IDX] =
  211. rgParseSignedDataBlob[MINASN1_SIGNED_DATA_CONTENT_DATA_IDX];
  212. // Check that the message has a signer
  213. if (0 == rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_ENCODED_IDX].cbData)
  214. goto NoSigner;
  215. // Get the message's bag of certs
  216. cCert = MAX_SIGNED_DATA_CERT_CNT;
  217. if (0 >= MinAsn1ParseSignedDataCertificates(
  218. &rgParseSignedDataBlob[MINASN1_SIGNED_DATA_CERTS_IDX],
  219. &cCert,
  220. rgrgCertBlob
  221. ) || 0 == cCert)
  222. goto NoCerts;
  223. // Get the signer certificate
  224. rgSignerCert = I_MinCryptFindSignerCertificateByIssuerAndSerialNumber(
  225. &rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_ISSUER_IDX],
  226. &rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_SERIAL_NUMBER_IDX],
  227. cCert,
  228. rgrgCertBlob
  229. );
  230. if (NULL == rgSignerCert)
  231. goto NoSignerCert;
  232. rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_SIGNER_CERT_IDX] =
  233. rgSignerCert[MINASN1_CERT_ENCODED_IDX];
  234. rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_AUTH_ATTRS_IDX] =
  235. rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_AUTH_ATTRS_IDX];
  236. rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_UNAUTH_ATTRS_IDX] =
  237. rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_UNAUTH_ATTRS_IDX];
  238. // Verify the signer certificate up to a baked in, trusted root
  239. lErr = MinCryptVerifyCertificate(
  240. rgSignerCert,
  241. cCert,
  242. rgrgCertBlob
  243. );
  244. if (ERROR_SUCCESS != lErr)
  245. goto ErrorReturn;
  246. // Hash the message's content octets according to the signer's hash
  247. // algorithm
  248. HashAlgId = MinCryptDecodeHashAlgorithmIdentifier(
  249. &rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_DIGEST_ALGID_IDX]
  250. );
  251. if (0 == HashAlgId)
  252. goto UnknownHashAlgId;
  253. // Note, the content's tag and length octets aren't included in the hash
  254. if (0 >= MinAsn1ExtractContent(
  255. rgParseSignedDataBlob[MINASN1_SIGNED_DATA_CONTENT_DATA_IDX].pbData,
  256. rgParseSignedDataBlob[MINASN1_SIGNED_DATA_CONTENT_DATA_IDX].cbData,
  257. &ContentBlob.cbData,
  258. (const BYTE **) &ContentBlob.pbData
  259. ))
  260. goto InvalidContent;
  261. lErr = MinCryptHashMemory(
  262. HashAlgId,
  263. 1, // cBlob
  264. &ContentBlob,
  265. rgbHash,
  266. &cbHash
  267. );
  268. if (ERROR_SUCCESS != lErr)
  269. goto ErrorReturn;
  270. // If we have authenticated attributes, then, need to compare the
  271. // above hash with the szOID_RSA_messageDigest ("1.2.840.113549.1.9.4")
  272. // attribute value. After a successful comparison, the above hash
  273. // is replaced with a hash of the authenticated attributes.
  274. if (0 != rgParseSignedDataBlob[
  275. MINASN1_SIGNED_DATA_SIGNER_INFO_AUTH_ATTRS_IDX].cbData) {
  276. lErr = I_MinCryptVerifySignerAuthenticatedAttributes(
  277. HashAlgId,
  278. rgbHash,
  279. &cbHash,
  280. &rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_AUTH_ATTRS_IDX]
  281. );
  282. if (ERROR_SUCCESS != lErr)
  283. goto ErrorReturn;
  284. }
  285. // Verify the signature using either the authenticated attributes hash
  286. // or the content hash
  287. lErr = MinCryptVerifySignedHash(
  288. HashAlgId,
  289. rgbHash,
  290. cbHash,
  291. &rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_ENCYRPT_DIGEST_IDX],
  292. &rgSignerCert[MINASN1_CERT_PUBKEY_INFO_IDX]
  293. );
  294. ErrorReturn:
  295. CommonReturn:
  296. return lErr;
  297. ParseSignedDataError:
  298. NotSignedDataOID:
  299. InvalidContent:
  300. lErr = CRYPT_E_BAD_MSG;
  301. goto CommonReturn;
  302. NoContent:
  303. lErr = ERROR_NO_DATA;
  304. goto CommonReturn;
  305. NoSigner:
  306. NoCerts:
  307. NoSignerCert:
  308. lErr = CRYPT_E_NO_SIGNER;
  309. goto CommonReturn;
  310. UnknownHashAlgId:
  311. lErr = CRYPT_E_UNKNOWN_ALGO;
  312. goto CommonReturn;
  313. }