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.

517 lines
16 KiB

  1. //+-------------------------------------------------------------------------
  2. // Microsoft Windows
  3. //
  4. // Copyright (C) Microsoft Corporation, 2001 - 2001
  5. //
  6. // File: asn1util.cpp
  7. //
  8. // Contents: Minimal ASN.1 utility helper functions.
  9. //
  10. // Functions: MinAsn1DecodeLength
  11. // MinAsn1ExtractContent
  12. // MinAsn1ExtractValues
  13. //
  14. // MinAsn1FindExtension
  15. // MinAsn1FindAttribute
  16. // MinAsn1ExtractParsedCertificatesFromSignedData
  17. //
  18. // History: 15-Jan-01 philh created
  19. //--------------------------------------------------------------------------
  20. #include "global.hxx"
  21. //+-------------------------------------------------------------------------
  22. // Get the number of contents octets in a definite-length BER-encoding.
  23. //
  24. // Parameters:
  25. // pcbContent - receives the number of contents octets
  26. // pbLength - points to the first length octet
  27. // cbBER - number of bytes remaining in the BER encoding
  28. //
  29. // Returns:
  30. // success - the number of bytes in the length field, > 0
  31. // failure - < 0
  32. //
  33. // One of the following failure values can be returned:
  34. // MINASN1_LENGTH_TOO_LARGE
  35. // MINASN1_INSUFFICIENT_DATA
  36. // MINASN1_UNSUPPORTED_INDEFINITE_LENGTH
  37. //--------------------------------------------------------------------------
  38. LONG
  39. WINAPI
  40. MinAsn1DecodeLength(
  41. OUT DWORD *pcbContent,
  42. IN const BYTE *pbLength,
  43. IN DWORD cbBER)
  44. {
  45. long i;
  46. BYTE cbLength;
  47. const BYTE *pb;
  48. if (cbBER < 1)
  49. goto TooLittleData;
  50. if (0x80 == *pbLength)
  51. goto IndefiniteLength;
  52. // determine the number of length octets and contents octets
  53. if ((cbLength = *pbLength) & 0x80) {
  54. cbLength &= ~0x80; // low 7 bits have number of bytes
  55. if (cbLength > 4)
  56. goto LengthTooLargeError;
  57. if (cbLength >= cbBER)
  58. goto TooLittleData;
  59. *pcbContent = 0;
  60. for (i=cbLength, pb=pbLength+1; i>0; i--, pb++)
  61. *pcbContent = (*pcbContent << 8) + (const DWORD)*pb;
  62. i = cbLength + 1;
  63. } else {
  64. *pcbContent = (DWORD)cbLength;
  65. i = 1;
  66. }
  67. CommonReturn:
  68. return i; // how many bytes there were in the length field
  69. LengthTooLargeError:
  70. i = MINASN1_LENGTH_TOO_LARGE;
  71. goto CommonReturn;
  72. IndefiniteLength:
  73. i = MINASN1_UNSUPPORTED_INDEFINITE_LENGTH;
  74. goto CommonReturn;
  75. TooLittleData:
  76. i = MINASN1_INSUFFICIENT_DATA;
  77. goto CommonReturn;
  78. }
  79. //+-------------------------------------------------------------------------
  80. // Point to the content octets in a definite-length BER-encoded blob.
  81. //
  82. // Returns:
  83. // success - the number of bytes skipped, > 0
  84. // failure - < 0
  85. //
  86. // One of the following failure values can be returned:
  87. // MINASN1_LENGTH_TOO_LARGE
  88. // MINASN1_INSUFFICIENT_DATA
  89. // MINASN1_UNSUPPORTED_INDEFINITE_LENGTH
  90. //
  91. // Assumption: pbData points to a definite-length BER-encoded blob.
  92. // If *pcbContent isn't within cbBER, MINASN1_INSUFFICIENT_DATA
  93. // is returned.
  94. //--------------------------------------------------------------------------
  95. LONG
  96. WINAPI
  97. MinAsn1ExtractContent(
  98. IN const BYTE *pbBER,
  99. IN DWORD cbBER,
  100. OUT DWORD *pcbContent,
  101. OUT const BYTE **ppbContent)
  102. {
  103. #define TAG_MASK 0x1f
  104. DWORD cbIdentifier;
  105. DWORD cbContent;
  106. LONG cbLength;
  107. LONG lHeader;
  108. const BYTE *pb = pbBER;
  109. if (0 == cbBER--)
  110. goto TooLittleData;
  111. // Skip over the identifier octet(s)
  112. if (TAG_MASK == (*pb++ & TAG_MASK)) {
  113. // high-tag-number form
  114. cbIdentifier = 2;
  115. while (TRUE) {
  116. if (0 == cbBER--)
  117. goto TooLittleData;
  118. if (0 == (*pb++ & 0x80))
  119. break;
  120. cbIdentifier++;
  121. }
  122. } else {
  123. // low-tag-number form
  124. cbIdentifier = 1;
  125. }
  126. if (0 > (cbLength = MinAsn1DecodeLength( &cbContent, pb, cbBER))) {
  127. lHeader = cbLength;
  128. goto CommonReturn;
  129. }
  130. if (cbContent > (cbBER - cbLength))
  131. goto TooLittleData;
  132. pb += cbLength;
  133. *pcbContent = cbContent;
  134. *ppbContent = pb;
  135. lHeader = cbLength + cbIdentifier;
  136. CommonReturn:
  137. return lHeader;
  138. TooLittleData:
  139. lHeader = MINASN1_INSUFFICIENT_DATA;
  140. goto CommonReturn;
  141. }
  142. typedef struct _STEP_INTO_STACK_ENTRY {
  143. const BYTE *pb;
  144. DWORD cb;
  145. BOOL fSkipIntoValues;
  146. } STEP_INTO_STACK_ENTRY, *PSTEP_INTO_STACK_ENTRY;
  147. #define MAX_STEP_INTO_DEPTH 8
  148. //+-------------------------------------------------------------------------
  149. // Extract one or more tagged values from the ASN.1 encoded byte array.
  150. //
  151. // Either steps into the value's content octets (MINASN1_STEP_INTO_VALUE_OP or
  152. // MINASN1_OPTIONAL_STEP_INTO_VALUE_OP) or steps over the value's tag,
  153. // length and content octets (MINASN1_STEP_OVER_VALUE_OP or
  154. // MINASN1_OPTIONAL_STEP_OVER_VALUE_OP).
  155. //
  156. // You can step out of a stepped into sequence via MINASN1_STEP_OUT_VALUE_OP.
  157. //
  158. // For tag matching, only supports single byte tags.
  159. //
  160. // Only definite-length ASN.1 is supported.
  161. //
  162. // *pcValue is updated with the number of values successfully extracted.
  163. //
  164. // Returns:
  165. // success - >= 0 => length of all bytes consumed through the last value
  166. // extracted. For STEP_INTO, only the tag and length
  167. // octets.
  168. // failure - < 0 => negative (offset + 1) of first bad tagged value
  169. //
  170. // A non-NULL rgValueBlob[] is updated with the pointer to and length of the
  171. // tagged value or its content octets. The rgValuePara[].dwIndex is used to
  172. // index into rgValueBlob[]. For OPTIONAL_STEP_OVER or
  173. // OPTIONAL_STEP_INTO, if no more bytes in the outer SEQUENCE or if the tag
  174. // isn't found, pbData and cbData are set to 0. Additioanlly, for
  175. // OPTIONAL_STEP_INTO, all subsequent values are skipped and their
  176. // rgValueBlob[] entries zeroed until a STEP_OUT is encountered.
  177. //
  178. // If MINASN1_RETURN_VALUE_BLOB_FLAG is set, pbData points to
  179. // the tag. cbData includes the tag, length and content octets.
  180. //
  181. // If MINASN1_RETURN_CONTENT_BLOB_FLAG is set, pbData points to the content
  182. // octets. cbData includes only the content octets.
  183. //
  184. // If neither BLOB_FLAG is set, rgValueBlob[] isn't updated.
  185. //
  186. // For MINASN1_RETURN_CONTENT_BLOB_FLAG of a BITSTRING, pbData is
  187. // advanced past the first contents octet containing the number of
  188. // unused bits and cbData has been decremented by 1. If cbData > 0, then,
  189. // *(pbData - 1) will contain the number of unused bits.
  190. //--------------------------------------------------------------------------
  191. LONG
  192. WINAPI
  193. MinAsn1ExtractValues(
  194. IN const BYTE *pbEncoded,
  195. IN DWORD cbEncoded,
  196. IN OUT DWORD *pcValuePara,
  197. IN const MINASN1_EXTRACT_VALUE_PARA *rgValuePara,
  198. IN DWORD cValueBlob,
  199. OUT OPTIONAL PCRYPT_DER_BLOB rgValueBlob
  200. )
  201. {
  202. DWORD cValue = *pcValuePara;
  203. const BYTE *pb = pbEncoded;
  204. DWORD cb = cbEncoded;
  205. BOOL fSkipIntoValues = FALSE;
  206. DWORD iValue;
  207. LONG lAllValues;
  208. STEP_INTO_STACK_ENTRY rgStepIntoStack[MAX_STEP_INTO_DEPTH];
  209. DWORD dwStepIntoDepth = 0;
  210. for (iValue = 0; iValue < cValue; iValue++) {
  211. DWORD dwParaFlags = rgValuePara[iValue].dwFlags;
  212. DWORD dwOp = dwParaFlags & MINASN1_MASK_VALUE_OP;
  213. const BYTE *pbParaTag = rgValuePara[iValue].rgbTag;
  214. DWORD dwIndex = rgValuePara[iValue].dwIndex;
  215. BOOL fValueBlob = (dwParaFlags & (MINASN1_RETURN_VALUE_BLOB_FLAG |
  216. MINASN1_RETURN_CONTENT_BLOB_FLAG)) && rgValueBlob &&
  217. (dwIndex < cValueBlob);
  218. BOOL fSkipValue = FALSE;
  219. LONG lTagLength;
  220. DWORD cbContent;
  221. const BYTE *pbContent;
  222. DWORD cbValue;
  223. if (MINASN1_STEP_OUT_VALUE_OP == dwOp) {
  224. // Unstack and advance past the last STEP_INTO
  225. if (0 == dwStepIntoDepth)
  226. goto InvalidStepOutOp;
  227. dwStepIntoDepth--;
  228. pb = rgStepIntoStack[dwStepIntoDepth].pb;
  229. cb = rgStepIntoStack[dwStepIntoDepth].cb;
  230. fSkipIntoValues = rgStepIntoStack[dwStepIntoDepth].fSkipIntoValues;
  231. continue;
  232. }
  233. if (fSkipIntoValues) {
  234. // For an omitted OPTIONAL_STEP_INTO, all of its included values
  235. // are also omitted.
  236. fSkipValue = TRUE;
  237. } else if (0 == cb) {
  238. if (!(MINASN1_OPTIONAL_STEP_INTO_VALUE_OP == dwOp ||
  239. MINASN1_OPTIONAL_STEP_OVER_VALUE_OP == dwOp))
  240. goto TooLittleData;
  241. fSkipValue = TRUE;
  242. } else if (pbParaTag) {
  243. // Assumption: single byte tag for doing comparison
  244. // Check if the encoded tag matches one of the expected tags
  245. BYTE bEncodedTag;
  246. BYTE bParaTag;
  247. bEncodedTag = *pb;
  248. while ((bParaTag = *pbParaTag) && bParaTag != bEncodedTag)
  249. pbParaTag++;
  250. if (0 == bParaTag) {
  251. if (!(MINASN1_OPTIONAL_STEP_INTO_VALUE_OP == dwOp ||
  252. MINASN1_OPTIONAL_STEP_OVER_VALUE_OP == dwOp))
  253. goto InvalidTag;
  254. fSkipValue = TRUE;
  255. }
  256. }
  257. if (fSkipValue) {
  258. if (fValueBlob) {
  259. rgValueBlob[dwIndex].pbData = NULL;
  260. rgValueBlob[dwIndex].cbData = 0;
  261. }
  262. if (MINASN1_STEP_INTO_VALUE_OP == dwOp ||
  263. MINASN1_OPTIONAL_STEP_INTO_VALUE_OP == dwOp) {
  264. // Stack this skipped STEP_INTO
  265. if (MAX_STEP_INTO_DEPTH <= dwStepIntoDepth)
  266. goto ExceededStepIntoDepth;
  267. rgStepIntoStack[dwStepIntoDepth].pb = pb;
  268. rgStepIntoStack[dwStepIntoDepth].cb = cb;
  269. rgStepIntoStack[dwStepIntoDepth].fSkipIntoValues =
  270. fSkipIntoValues;
  271. dwStepIntoDepth++;
  272. fSkipIntoValues = TRUE;
  273. }
  274. continue;
  275. }
  276. lTagLength = MinAsn1ExtractContent(
  277. pb,
  278. cb,
  279. &cbContent,
  280. &pbContent
  281. );
  282. if (0 >= lTagLength)
  283. goto InvalidTagOrLength;
  284. cbValue = cbContent + lTagLength;
  285. if (fValueBlob) {
  286. if (dwParaFlags & MINASN1_RETURN_CONTENT_BLOB_FLAG) {
  287. rgValueBlob[dwIndex].pbData = (BYTE *) pbContent;
  288. rgValueBlob[dwIndex].cbData = cbContent;
  289. if (MINASN1_TAG_BITSTRING == *pb) {
  290. if (0 < cbContent) {
  291. // Advance past the first contents octet containing
  292. // the number of unused bits
  293. rgValueBlob[dwIndex].pbData += 1;
  294. rgValueBlob[dwIndex].cbData -= 1;
  295. }
  296. }
  297. } else if (dwParaFlags & MINASN1_RETURN_VALUE_BLOB_FLAG) {
  298. rgValueBlob[dwIndex].pbData = (BYTE *) pb;
  299. rgValueBlob[dwIndex].cbData = cbValue;
  300. }
  301. }
  302. switch (dwOp) {
  303. case MINASN1_STEP_INTO_VALUE_OP:
  304. case MINASN1_OPTIONAL_STEP_INTO_VALUE_OP:
  305. // Stack this STEP_INTO
  306. if (MAX_STEP_INTO_DEPTH <= dwStepIntoDepth)
  307. goto ExceededStepIntoDepth;
  308. rgStepIntoStack[dwStepIntoDepth].pb = pb + cbValue;
  309. rgStepIntoStack[dwStepIntoDepth].cb = cb - cbValue;
  310. assert(!fSkipIntoValues);
  311. rgStepIntoStack[dwStepIntoDepth].fSkipIntoValues = FALSE;
  312. dwStepIntoDepth++;
  313. pb = pbContent;
  314. cb = cbContent;
  315. break;
  316. case MINASN1_STEP_OVER_VALUE_OP:
  317. case MINASN1_OPTIONAL_STEP_OVER_VALUE_OP:
  318. pb += cbValue;
  319. cb -= cbValue;
  320. break;
  321. default:
  322. goto InvalidArg;
  323. }
  324. }
  325. lAllValues = (LONG)(pb - pbEncoded);
  326. assert((DWORD) lAllValues <= cbEncoded);
  327. CommonReturn:
  328. *pcValuePara = iValue;
  329. return lAllValues;
  330. InvalidStepOutOp:
  331. TooLittleData:
  332. InvalidTag:
  333. ExceededStepIntoDepth:
  334. InvalidTagOrLength:
  335. InvalidArg:
  336. lAllValues = -((LONG)(pb - pbEncoded)) - 1;
  337. goto CommonReturn;
  338. }
  339. //+-------------------------------------------------------------------------
  340. // Find an extension identified by its Encoded Object Identifier.
  341. //
  342. // Searches the list of parsed extensions returned by
  343. // MinAsn1ParseExtensions().
  344. //
  345. // If found, returns pointer to the rgExtBlob[MINASN1_EXT_BLOB_CNT].
  346. // Otherwise, returns NULL.
  347. //--------------------------------------------------------------------------
  348. PCRYPT_DER_BLOB
  349. WINAPI
  350. MinAsn1FindExtension(
  351. IN PCRYPT_DER_BLOB pEncodedOIDBlob,
  352. IN DWORD cExt,
  353. IN CRYPT_DER_BLOB rgrgExtBlob[][MINASN1_EXT_BLOB_CNT]
  354. )
  355. {
  356. DWORD i;
  357. DWORD cbOID = pEncodedOIDBlob->cbData;
  358. const BYTE *pbOID = pEncodedOIDBlob->pbData;
  359. for (i = 0; i < cExt; i++) {
  360. if (cbOID == rgrgExtBlob[i][MINASN1_EXT_OID_IDX].cbData
  361. &&
  362. 0 == memcmp(pbOID, rgrgExtBlob[i][MINASN1_EXT_OID_IDX].pbData,
  363. cbOID))
  364. return rgrgExtBlob[i];
  365. }
  366. return NULL;
  367. }
  368. //+-------------------------------------------------------------------------
  369. // Find the first attribute identified by its Encoded Object Identifier.
  370. //
  371. // Searches the list of parsed attributes returned by
  372. // MinAsn1ParseAttributes().
  373. //
  374. // If found, returns pointer to the rgAttrBlob[MINASN1_ATTR_BLOB_CNT].
  375. // Otherwise, returns NULL.
  376. //--------------------------------------------------------------------------
  377. PCRYPT_DER_BLOB
  378. WINAPI
  379. MinAsn1FindAttribute(
  380. IN PCRYPT_DER_BLOB pEncodedOIDBlob,
  381. IN DWORD cAttr,
  382. IN CRYPT_DER_BLOB rgrgAttrBlob[][MINASN1_ATTR_BLOB_CNT]
  383. )
  384. {
  385. DWORD i;
  386. DWORD cbOID = pEncodedOIDBlob->cbData;
  387. const BYTE *pbOID = pEncodedOIDBlob->pbData;
  388. for (i = 0; i < cAttr; i++) {
  389. if (cbOID == rgrgAttrBlob[i][MINASN1_ATTR_OID_IDX].cbData
  390. &&
  391. 0 == memcmp(pbOID, rgrgAttrBlob[i][MINASN1_ATTR_OID_IDX].pbData,
  392. cbOID))
  393. return rgrgAttrBlob[i];
  394. }
  395. return NULL;
  396. }
  397. //+-------------------------------------------------------------------------
  398. // Parses an ASN.1 encoded PKCS #7 Signed Data Message to extract and
  399. // parse the X.509 certificates it contains.
  400. //
  401. // Assumes the PKCS #7 message is definite length encoded.
  402. // Assumes PKCS #7 version 1.5, ie, not the newer CMS version.
  403. //
  404. // Upon input, *pcCert contains the maximum number of parsed certificates
  405. // that can be returned. Updated with the number of certificates processed.
  406. //
  407. // If the encoded message was successfully parsed, TRUE is returned
  408. // with *pcCert updated with the number of parsed certificates. Otherwise,
  409. // FALSE is returned for a parse error.
  410. // Returns:
  411. // success - >= 0 => bytes skipped, length of the encoded certificates
  412. // processed.
  413. // failure - < 0 => negative (offset + 1) of first bad tagged value
  414. // from beginning of message.
  415. //
  416. // The rgrgCertBlob[][] is updated with pointer to and length of the
  417. // fields in the encoded certificate. See MinAsn1ParseCertificate for the
  418. // field definitions.
  419. //--------------------------------------------------------------------------
  420. LONG
  421. WINAPI
  422. MinAsn1ExtractParsedCertificatesFromSignedData(
  423. IN const BYTE *pbEncoded,
  424. IN DWORD cbEncoded,
  425. IN OUT DWORD *pcCert,
  426. OUT CRYPT_DER_BLOB rgrgCertBlob[][MINASN1_CERT_BLOB_CNT]
  427. )
  428. {
  429. LONG lSkipped;
  430. CRYPT_DER_BLOB rgSignedDataBlob[MINASN1_SIGNED_DATA_BLOB_CNT];
  431. lSkipped = MinAsn1ParseSignedData(
  432. pbEncoded,
  433. cbEncoded,
  434. rgSignedDataBlob
  435. );
  436. if (0 >= lSkipped)
  437. goto ParseError;
  438. lSkipped = MinAsn1ParseSignedDataCertificates(
  439. &rgSignedDataBlob[MINASN1_SIGNED_DATA_CERTS_IDX],
  440. pcCert,
  441. rgrgCertBlob
  442. );
  443. if (0 > lSkipped) {
  444. assert(rgSignedDataBlob[MINASN1_SIGNED_DATA_CERTS_IDX].pbData >
  445. pbEncoded);
  446. lSkipped -= rgSignedDataBlob[MINASN1_SIGNED_DATA_CERTS_IDX].pbData -
  447. pbEncoded;
  448. goto ParseError;
  449. }
  450. CommonReturn:
  451. return lSkipped;
  452. ParseError:
  453. *pcCert = 0;
  454. goto CommonReturn;
  455. }