Check-in [ab26dec401]
Overview
Comment:Updated to use BER-TLV decoding to extract responses rather than hard-coded offsets
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | piv
Files: files | file ages | folders
SHA1:ab26dec40100d598ef4cc55901387418cb477fb7
User & Date: rkeene on 2013-01-15 15:59:23
Other Links: manifest | tags
Context
2013-01-15
18:44
Updated to build a certificate chain to a root (atleast one) and also not have loops in chains check-in: 1847ef7af2 user: rkeene tags: piv
15:59
Updated to use BER-TLV decoding to extract responses rather than hard-coded offsets check-in: ab26dec401 user: rkeene tags: piv
14:12
Merged in trunk check-in: 8f27214611 user: rkeene tags: piv
Changes

Modified cackey.c from [ddd6381c90] to [cc302eaf9b].

1697
1698
1699
1700
1701
1702
1703
1704




















































































1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730

1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768

1769
1770
1771
1772
1773
1774
1775

1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791

1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802

1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
....
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363

2364
2365
2366
2367
2368
2369
2370
....
2469
2470
2471
2472
2473
2474
2475
2476

2477






2478







2479
2480
2481
2482
2483
2484
2485
....
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
....
2839
2840
2841
2842
2843
2844
2845


2846
2847
2848
2849
2850
2851
2852
2853









2854

2855
2856
2857
2858
2859
2860
2861
	}


	CACKEY_DEBUG_PRINTF("APDU Returned an error, returning in failure");

	return(CACKEY_PCSC_E_GENERIC);
}





















































































/*
 * SYNPOSIS
 *     ssize_t cackey_get_data(struct cackey_slot *slot, unsigned char *buffer, size_t count, unsigned char oid[3]);
 *
 * ARGUMENTS
 *     struct cackey_slot *slot
 *         Slot to send commands to
 *
 *     unsigned char *buffer
 *         [OUT] Buffer
 *
 *     size_t count
 *         Number of bytes to attempt to read
 *
 *     unsigned char oid[3]
 *         3-byte OID to read
 *
 *
 * RETURN VALUE
 *     This function returns the number of bytes actually read, or -1 on error.
 *
 * NOTES
 *     None
 *
 */
static ssize_t cackey_get_data(struct cackey_slot *slot, unsigned char *buffer, size_t count, unsigned char oid[3]) {

	unsigned char *buffer_p;
	size_t init_count;

	size_t offset = 0, size;
	unsigned char cmd[] = {0x5C, 0x03, 0x00, 0x00, 0x00};
	uint16_t respcode;
	int send_ret;
	int idx;

	CACKEY_DEBUG_PRINTF("Called.");

	init_count = count;

	cmd[2] = oid[0];
	cmd[3] = oid[1];
	cmd[4] = oid[2];

	/* 256 to indicate the largest message size -- not clear if this will work with all messages */
	send_ret = cackey_send_apdu(slot, GSCIS_CLASS_ISO7816, NISTSP800_73_3_INSTR_GET_DATA, 0x3F, 0xFF, sizeof(cmd), cmd, 256, &respcode, buffer, &count);

	if (send_ret == CACKEY_PCSC_E_RETRY) {
		CACKEY_DEBUG_PRINTF("ADPU Sending failed, retrying read buffer");

		return(cackey_get_data(slot, buffer, init_count, oid));
	}

	if (send_ret != CACKEY_PCSC_S_OK) {
		CACKEY_DEBUG_PRINTF("cackey_send_apdu() failed, returning in failure");

		return(-1);
	}

	offset += count;

#ifdef CACKEY_PARANOID
#  ifdef _POSIX_SSIZE_MAX
	if (offset > _POSIX_SSIZE_MAX) {
		CACKEY_DEBUG_PRINTF("Offset exceeds maximum value, returning in failure. (max = %li, offset = %lu)", (long) _POSIX_SSIZE_MAX, (unsigned long) offset);


		return(-1);
	}
#  endif
#endif

	if (offset < 2) {

		CACKEY_DEBUG_PRINTF("APDU GET DATA returned %lu bytes, which is too short for a BER-TLV response", (unsigned long) offset);

		return(-1);
	}

	buffer_p = buffer;
	if (*buffer_p != 0x53) {
		CACKEY_DEBUG_PRINTF("APDU GET DATA did not return a BER-TLV with tag 53.  Tag = %02x", (unsigned int) *buffer_p);

		return(-1);
	}
	buffer_p++;

	if ((*buffer_p & 0x80) == 0x80) {
		size = 0;
		idx = (*buffer_p & 0x7f);


		if (((buffer_p - buffer) + idx) >= offset) {
			CACKEY_DEBUG_PRINTF("Malformed BER value -- not enough bytes available to read length");

			return(-1);
		}

		for (; idx > 0; idx--) {
			buffer_p++;
			size <<= 8;
			size |= *buffer_p;

		}
	} else {
		size = *buffer_p;
	}
	buffer_p++;

	if (((buffer_p - buffer) + size) != offset) {
		CACKEY_DEBUG_PRINTF("Entire buffer is not consumed!");

		if (((buffer_p - buffer) + size) > offset) {
			CACKEY_DEBUG_PRINTF("Encoded size is greater than the amount of data read, dropping");

			return(-1);
		}
	}

	memmove(buffer, buffer_p, size);

	CACKEY_DEBUG_PRINTBUF("GET DATA result", buffer, size);

	CACKEY_DEBUG_PRINTF("Returning in success, read %lu bytes", (unsigned long) size);

	return(size);
}
................................................................................
 */
static struct cackey_pcsc_identity *cackey_read_certs(struct cackey_slot *slot, struct cackey_pcsc_identity *certs, unsigned long *count) {
	struct cackey_pcsc_identity *curr_id;
	struct cackey_tlv_entity *ccc_tlv, *ccc_curr, *app_tlv, *app_curr;
	unsigned char ccc_aid[] = {GSCIS_AID_CCC}, piv_aid[] = {NISTSP800_73_3_PIV_AID};
	unsigned char *piv_oid, piv_oid_pivauth[] = {NISTSP800_73_3_OID_PIVAUTH}, piv_oid_signature[] = {NISTSP800_73_3_OID_SIGNATURE}, piv_oid_keymgt[] = {NISTSP800_73_3_OID_KEYMGT};
	unsigned char curr_aid[7];
	unsigned char buffer[8192];
	unsigned long outidx = 0;
	cackey_ret transaction_ret;
	ssize_t read_ret;

	int certs_resizable;
	int send_ret, select_ret;
	int piv_key, piv = 0;
	int idx;

	CACKEY_DEBUG_PRINTF("Called.");

................................................................................

			curr_id->keysize = -1;
			curr_id->id_type = CACKEY_ID_TYPE_PIV;
			curr_id->card.piv.key_id = piv_key;

			curr_id->certificate_len = read_ret;
			curr_id->certificate = malloc(curr_id->certificate_len);
			memcpy(curr_id->certificate, buffer + 4, curr_id->certificate_len - 4); /* XXX TODO PIV (-4 header, -5 trailer == why ?) */

			curr_id->certificate_len -= 4;






			curr_id->certificate_len -= 5;







		}
	} else {
		/* Read all the applets from the CCC's TLV */
		ccc_tlv = cackey_read_tlv(slot);

		/* Look for CARDURLs that coorespond to PKI applets */
		for (ccc_curr = ccc_tlv; ccc_curr; ccc_curr = ccc_curr->_next) {
................................................................................
 * NOTES
 *     ...
 *
 */
static ssize_t cackey_signdecrypt(struct cackey_slot *slot, struct cackey_identity *identity, unsigned char *buf, size_t buflen, unsigned char *outbuf, size_t outbuflen, int padInput, int unpadOutput) {
	cackey_pcsc_id_type id_type;
	unsigned char dyn_auth_template[10];
	unsigned char *tmpbuf, *tmpbuf_s, *outbuf_s;
	unsigned char bytes_to_send, p1, class;
	unsigned char blocktype;
	cackey_ret send_ret;
	uint16_t respcode;
	ssize_t retval = 0, unpadoffset;
	size_t tmpbuflen, padlen, tmpoutbuflen;
	int free_tmpbuf = 0;
	int le;

	CACKEY_DEBUG_PRINTF("Called.");

	if (slot == NULL) {
		CACKEY_DEBUG_PRINTF("Error.  slot is NULL");
................................................................................
	}
#  endif
#endif

	/* We must remove the "7C" tag to get to the signature */
	switch (id_type) {
		case CACKEY_ID_TYPE_PIV:


			if (outbuf[0] != 0x7C) {
				CACKEY_DEBUG_PRINTF("Response from PIV for GENERATE AUTHENTICATION was not a 0x7C tag, returning in failure");

				return(-1);
			}

			/* XXX TODO PIV */
			memmove(outbuf, outbuf + 8, retval - 8);









			retval -= 8;

			break;
		case CACKEY_ID_TYPE_CAC:
		case CACKEY_ID_TYPE_CERT_ONLY:
			break;
	}

	/* Unpad reply */








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


|








|













|
>

|
<
<
<


<



|






|




|








<
<


|
<
>






<
>
|




<
<
<
<
<
<
<
<
<
|
<
>

<
<
<
<
<
<
<
|
<
<
>
|
<
<
<
<
<
<
<
<
<
<
<
|
|
<
<
<







 







|



>







 







<
>
|
>
>
>
>
>
>
|
>
>
>
>
>
>
>







 







|





|







 







>
>
|





|
<
>
>
>
>
>
>
>
>
>
|
>







1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817



1818
1819

1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843


1844
1845
1846

1847
1848
1849
1850
1851
1852
1853

1854
1855
1856
1857
1858
1859









1860

1861
1862







1863


1864
1865











1866
1867



1868
1869
1870
1871
1872
1873
1874
....
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
....
2518
2519
2520
2521
2522
2523
2524

2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
....
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
....
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916

2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
	}


	CACKEY_DEBUG_PRINTF("APDU Returned an error, returning in failure");

	return(CACKEY_PCSC_E_GENERIC);
}

static unsigned char *cackey_read_bertlv_tag(unsigned char *buffer, size_t *buffer_len_p, unsigned char tag, unsigned char *outbuffer, size_t *outbuffer_len_p) {
	unsigned char *buffer_p;
	size_t outbuffer_len, buffer_len;
	size_t size;
	int idx;

	CACKEY_DEBUG_PRINTF("Called.");

	if (buffer_len_p == NULL) {
		CACKEY_DEBUG_PRINTF("buffer_len_p is NULL.  Returning in failure.");

		return(NULL);
	}

	if (outbuffer_len_p == NULL) {
		CACKEY_DEBUG_PRINTF("outbuffer_len_p is NULL.  Returning in failure.");

		return(NULL);
	}

	buffer_len = *outbuffer_len_p;
	outbuffer_len = *outbuffer_len_p;

	buffer_p = buffer;
	if (buffer_p[0] != tag) {
		CACKEY_DEBUG_PRINTF("Tag found was not tag expected.  Tag = %02x, Expected = %02x.  Returning in failure.", (unsigned int) buffer_p[0], tag);

		return(NULL);
	}

	buffer_p++;
	buffer_len--;

	if ((buffer_p[0] & 0x80) == 0x80) {
		size = 0;
		idx = (buffer_p[0] & 0x7f);

		if (idx > buffer_len) {
			CACKEY_DEBUG_PRINTF("Malformed BER value -- not enough bytes available to read length (idx = %i, buffer_len = %lu)", idx, (unsigned long) buffer_len);

			return(NULL);
		}

		for (; idx > 0; idx--) {
			buffer_p++;
			buffer_len--;

			size <<= 8;
			size |= buffer_p[0];
		}
	} else {
		size = buffer_p[0];
	}

	buffer_p++;
	buffer_len--;

	if (size > outbuffer_len) {
		CACKEY_DEBUG_PRINTF("Unable to copy value buffer to outbuffer, not enough room.  Value buffer length = %lu, out buffer length = %lu", (unsigned long) size, (unsigned long) outbuffer_len);

		return(NULL);
	}

	*outbuffer_len_p = size;
	if (outbuffer) {
		memcpy(outbuffer, buffer_p, size);
		buffer_p += size;
		buffer_len -= size;

		*buffer_len_p = buffer_len;

		CACKEY_DEBUG_PRINTBUF("BER-TLV results:", outbuffer, size);
	} else {
		memmove(buffer, buffer_p, size);
		buffer_p = buffer;

		CACKEY_DEBUG_PRINTBUF("BER-TLV results:", buffer, size);
	}

	CACKEY_DEBUG_PRINTF("Returning in success.  Size of contents for tag %02x is %lu", (unsigned int) tag, (unsigned long) size);

	return(buffer_p);
}

/*
 * SYNPOSIS
 *     ssize_t cackey_get_data(struct cackey_slot *slot, unsigned char *buffer, size_t buffer_len, unsigned char oid[3]);
 *
 * ARGUMENTS
 *     struct cackey_slot *slot
 *         Slot to send commands to
 *
 *     unsigned char *buffer
 *         [OUT] Buffer
 *
 *     size_t buffer_len
 *         Number of bytes to attempt to read
 *
 *     unsigned char oid[3]
 *         3-byte OID to read
 *
 *
 * RETURN VALUE
 *     This function returns the number of bytes actually read, or -1 on error.
 *
 * NOTES
 *     None
 *
 */
static ssize_t cackey_get_data(struct cackey_slot *slot, unsigned char *buffer, size_t buffer_len, unsigned char oid[3]) {
	unsigned char cmd[] = {0x5C, 0x03, 0x00, 0x00, 0x00};
	unsigned char *buffer_p;
	size_t init_buffer_len, size;



	uint16_t respcode;
	int send_ret;


	CACKEY_DEBUG_PRINTF("Called.");

	init_buffer_len = buffer_len;

	cmd[2] = oid[0];
	cmd[3] = oid[1];
	cmd[4] = oid[2];

	/* 256 to indicate the largest message size -- not clear if this will work with all messages */
	send_ret = cackey_send_apdu(slot, GSCIS_CLASS_ISO7816, NISTSP800_73_3_INSTR_GET_DATA, 0x3F, 0xFF, sizeof(cmd), cmd, 256, &respcode, buffer, &buffer_len);

	if (send_ret == CACKEY_PCSC_E_RETRY) {
		CACKEY_DEBUG_PRINTF("ADPU Sending failed, retrying read buffer");

		return(cackey_get_data(slot, buffer, init_buffer_len, oid));
	}

	if (send_ret != CACKEY_PCSC_S_OK) {
		CACKEY_DEBUG_PRINTF("cackey_send_apdu() failed, returning in failure");

		return(-1);
	}



#ifdef CACKEY_PARANOID
#  ifdef _POSIX_SSIZE_MAX
	if (buffer_len > _POSIX_SSIZE_MAX) {

		CACKEY_DEBUG_PRINTF("Read bytes (buffer_len) exceeds maximum value, returning in failure. (max = %li, buffer_len = %lu)", (long) _POSIX_SSIZE_MAX, (unsigned long) buffer_len);

		return(-1);
	}
#  endif
#endif


	if (buffer_len < 2) {
		CACKEY_DEBUG_PRINTF("APDU GET DATA returned %lu bytes, which is too short for a BER-TLV response", (unsigned long) buffer_len);

		return(-1);
	}










	size = buffer_len;

	buffer_p = cackey_read_bertlv_tag(buffer, &buffer_len, 0x53, NULL, &size);








	if (buffer_p == NULL) {


		CACKEY_DEBUG_PRINTF("Tag decoding failed, returning in error.");












		return(-1);
	}




	CACKEY_DEBUG_PRINTBUF("GET DATA result", buffer, size);

	CACKEY_DEBUG_PRINTF("Returning in success, read %lu bytes", (unsigned long) size);

	return(size);
}
................................................................................
 */
static struct cackey_pcsc_identity *cackey_read_certs(struct cackey_slot *slot, struct cackey_pcsc_identity *certs, unsigned long *count) {
	struct cackey_pcsc_identity *curr_id;
	struct cackey_tlv_entity *ccc_tlv, *ccc_curr, *app_tlv, *app_curr;
	unsigned char ccc_aid[] = {GSCIS_AID_CCC}, piv_aid[] = {NISTSP800_73_3_PIV_AID};
	unsigned char *piv_oid, piv_oid_pivauth[] = {NISTSP800_73_3_OID_PIVAUTH}, piv_oid_signature[] = {NISTSP800_73_3_OID_SIGNATURE}, piv_oid_keymgt[] = {NISTSP800_73_3_OID_KEYMGT};
	unsigned char curr_aid[7];
	unsigned char buffer[8192], *buffer_p;
	unsigned long outidx = 0;
	cackey_ret transaction_ret;
	ssize_t read_ret;
	size_t buffer_len;
	int certs_resizable;
	int send_ret, select_ret;
	int piv_key, piv = 0;
	int idx;

	CACKEY_DEBUG_PRINTF("Called.");

................................................................................

			curr_id->keysize = -1;
			curr_id->id_type = CACKEY_ID_TYPE_PIV;
			curr_id->card.piv.key_id = piv_key;

			curr_id->certificate_len = read_ret;
			curr_id->certificate = malloc(curr_id->certificate_len);


			CACKEY_DEBUG_PRINTBUF("Pre-shrink (-4header, -5trailer) == ", buffer, curr_id->certificate_len);

			buffer_len = sizeof(buffer);
			buffer_p = cackey_read_bertlv_tag(buffer, &buffer_len, 0x70, curr_id->certificate, &curr_id->certificate_len);

			if (buffer_p == NULL) {
				CACKEY_DEBUG_PRINTF("Reading certificate from BER-TLV response failed, skipping key %i", idx);
				free(curr_id->certificate);

				outidx--;

				continue;
			}

			CACKEY_DEBUG_PRINTBUF("Post-shrink (-4header, -5trailer) == ", curr_id->certificate, curr_id->certificate_len);
		}
	} else {
		/* Read all the applets from the CCC's TLV */
		ccc_tlv = cackey_read_tlv(slot);

		/* Look for CARDURLs that coorespond to PKI applets */
		for (ccc_curr = ccc_tlv; ccc_curr; ccc_curr = ccc_curr->_next) {
................................................................................
 * NOTES
 *     ...
 *
 */
static ssize_t cackey_signdecrypt(struct cackey_slot *slot, struct cackey_identity *identity, unsigned char *buf, size_t buflen, unsigned char *outbuf, size_t outbuflen, int padInput, int unpadOutput) {
	cackey_pcsc_id_type id_type;
	unsigned char dyn_auth_template[10];
	unsigned char *tmpbuf, *tmpbuf_s, *outbuf_s, *outbuf_p;
	unsigned char bytes_to_send, p1, class;
	unsigned char blocktype;
	cackey_ret send_ret;
	uint16_t respcode;
	ssize_t retval = 0, unpadoffset;
	size_t tmpbuflen, padlen, tmpoutbuflen, outbuf_len;
	int free_tmpbuf = 0;
	int le;

	CACKEY_DEBUG_PRINTF("Called.");

	if (slot == NULL) {
		CACKEY_DEBUG_PRINTF("Error.  slot is NULL");
................................................................................
	}
#  endif
#endif

	/* We must remove the "7C" tag to get to the signature */
	switch (id_type) {
		case CACKEY_ID_TYPE_PIV:
			outbuf_len = retval;
			outbuf_p = cackey_read_bertlv_tag(outbuf, &outbuf_len, 0x7C, NULL,  &outbuf_len);
			if (outbuf_p == NULL) {
				CACKEY_DEBUG_PRINTF("Response from PIV for GENERATE AUTHENTICATION was not a 0x7C tag, returning in failure");

				return(-1);
			}

			retval = outbuf_len;


			outbuf_len = retval;
			outbuf_p = cackey_read_bertlv_tag(outbuf, &outbuf_len, 0x82, NULL,  &outbuf_len);
			if (outbuf_p == NULL) {
				CACKEY_DEBUG_PRINTF("Response from PIV for GENERATE AUTHENTICATION was not a 0x82 with then 0x7C tag, returning in failure");

				return(-1);
			}

			retval = outbuf_len;

			break;
		case CACKEY_ID_TYPE_CAC:
		case CACKEY_ID_TYPE_CERT_ONLY:
			break;
	}

	/* Unpad reply */