]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTests/clxutils/anchorTest/anchorTest.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTests / clxutils / anchorTest / anchorTest.cpp
diff --git a/SecurityTests/clxutils/anchorTest/anchorTest.cpp b/SecurityTests/clxutils/anchorTest/anchorTest.cpp
new file mode 100644 (file)
index 0000000..8a3bde3
--- /dev/null
@@ -0,0 +1,1125 @@
+/*
+ * anchorTest.cpp - test cert encode/decode using known good system
+ *                  anchors
+ */
+#include <stdio.h>
+#include <string.h>
+#include <Security/cssm.h>
+#include <Security/x509defs.h>
+#include <Security/oidsattr.h>
+#include <Security/oidscert.h>
+#include <Security/certextensions.h>
+#include <Security/SecTrust.h>
+#include <Security/SecTrustSettingsPriv.h>
+#include <security_cdsa_utils/cuOidParser.h>
+#include <security_cdsa_utils/cuPrintCert.h>
+#include <utilLib/common.h>
+#include <utilLib/cspwrap.h>
+#include <security_cdsa_utils/cuFileIo.h>
+#include <clAppUtils/clutils.h>
+#include <clAppUtils/certVerify.h>
+#include <clAppUtils/tpUtils.h>
+#include <Security/SecAsn1Coder.h>
+#include <Security/X509Templates.h>
+
+#define ENC_TBS_BLOB   "encodedTbs.der"
+#define DEC_TBS_BLOB   "decodedTbs.der"
+
+static void usage(char **argv)
+{
+       printf("Usage: %s [options]\n", argv[0]);
+       printf("Options:\n");
+       printf("  w    -- writeBlobs\n");
+       printf("  e    -- allow expired roots\n");
+       printf("  t    -- use Trust Settings\n");
+       printf("  q    -- quiet\n");
+       printf("  v    -- verbose\n");
+       exit(1);
+}
+
+/*
+ * Certs for which we skip the "compare TBS blob" test, enumerated by 
+ * DER-encoded issuer name.
+ *
+ * Get this formatted data from the extractCertFields program. 
+ *
+ * All of these have non-standard KeyUsage encoding (legal but it's
+ * not the same as ours or everyone else's).
+ */
+/*
+   Country         : HU
+   Locality        : Budapest
+   Org             : NetLock Halozatbiztonsagi Kft.
+   OrgUnit         : Tanusitvanykiadok
+   Common Name     : NetLock Expressz (Class C) Tanusitvanykiado 
+ */
+static const uint8 anchor_46_derIssuer_bytes[] = {
+   0x30,  0x81,  0x9b,  0x31,  0x0b,  0x30,  0x09,  0x06,  
+   0x03,  0x55,  0x04,  0x06,  0x13,  0x02,  0x48,  0x55,  
+   0x31,  0x11,  0x30,  0x0f,  0x06,  0x03,  0x55,  0x04,  
+   0x07,  0x13,  0x08,  0x42,  0x75,  0x64,  0x61,  0x70,  
+   0x65,  0x73,  0x74,  0x31,  0x27,  0x30,  0x25,  0x06,  
+   0x03,  0x55,  0x04,  0x0a,  0x13,  0x1e,  0x4e,  0x65,  
+   0x74,  0x4c,  0x6f,  0x63,  0x6b,  0x20,  0x48,  0x61,  
+   0x6c,  0x6f,  0x7a,  0x61,  0x74,  0x62,  0x69,  0x7a,  
+   0x74,  0x6f,  0x6e,  0x73,  0x61,  0x67,  0x69,  0x20,  
+   0x4b,  0x66,  0x74,  0x2e,  0x31,  0x1a,  0x30,  0x18,  
+   0x06,  0x03,  0x55,  0x04,  0x0b,  0x13,  0x11,  0x54,  
+   0x61,  0x6e,  0x75,  0x73,  0x69,  0x74,  0x76,  0x61,  
+   0x6e,  0x79,  0x6b,  0x69,  0x61,  0x64,  0x6f,  0x6b,  
+   0x31,  0x34,  0x30,  0x32,  0x06,  0x03,  0x55,  0x04,  
+   0x03,  0x13,  0x2b,  0x4e,  0x65,  0x74,  0x4c,  0x6f,  
+   0x63,  0x6b,  0x20,  0x45,  0x78,  0x70,  0x72,  0x65,  
+   0x73,  0x73,  0x7a,  0x20,  0x28,  0x43,  0x6c,  0x61,  
+   0x73,  0x73,  0x20,  0x43,  0x29,  0x20,  0x54,  0x61,  
+   0x6e,  0x75,  0x73,  0x69,  0x74,  0x76,  0x61,  0x6e,  
+   0x79,  0x6b,  0x69,  0x61,  0x64,  0x6f
+};
+static const CSSM_DATA anchor_46_derIssuer = { 158, (uint8 *)anchor_46_derIssuer_bytes };
+
+/*
+   Country         : HU
+   State           : Hungary
+   Locality        : Budapest
+   Org             : NetLock Halozatbiztonsagi Kft.
+   OrgUnit         : Tanusitvanykiadok
+   Common Name     : NetLock Kozjegyzoi (Class A) Tanusitvanykiado
+*/
+static const uint8 anchor_53_derIssuer_bytes[] = {
+   0x30,  0x81,  0xaf,  0x31,  0x0b,  0x30,  0x09,  0x06,  
+   0x03,  0x55,  0x04,  0x06,  0x13,  0x02,  0x48,  0x55,  
+   0x31,  0x10,  0x30,  0x0e,  0x06,  0x03,  0x55,  0x04,  
+   0x08,  0x13,  0x07,  0x48,  0x75,  0x6e,  0x67,  0x61,  
+   0x72,  0x79,  0x31,  0x11,  0x30,  0x0f,  0x06,  0x03,  
+   0x55,  0x04,  0x07,  0x13,  0x08,  0x42,  0x75,  0x64,  
+   0x61,  0x70,  0x65,  0x73,  0x74,  0x31,  0x27,  0x30,  
+   0x25,  0x06,  0x03,  0x55,  0x04,  0x0a,  0x13,  0x1e,  
+   0x4e,  0x65,  0x74,  0x4c,  0x6f,  0x63,  0x6b,  0x20,  
+   0x48,  0x61,  0x6c,  0x6f,  0x7a,  0x61,  0x74,  0x62,  
+   0x69,  0x7a,  0x74,  0x6f,  0x6e,  0x73,  0x61,  0x67,  
+   0x69,  0x20,  0x4b,  0x66,  0x74,  0x2e,  0x31,  0x1a,  
+   0x30,  0x18,  0x06,  0x03,  0x55,  0x04,  0x0b,  0x13,  
+   0x11,  0x54,  0x61,  0x6e,  0x75,  0x73,  0x69,  0x74,  
+   0x76,  0x61,  0x6e,  0x79,  0x6b,  0x69,  0x61,  0x64,  
+   0x6f,  0x6b,  0x31,  0x36,  0x30,  0x34,  0x06,  0x03,  
+   0x55,  0x04,  0x03,  0x13,  0x2d,  0x4e,  0x65,  0x74,  
+   0x4c,  0x6f,  0x63,  0x6b,  0x20,  0x4b,  0x6f,  0x7a,  
+   0x6a,  0x65,  0x67,  0x79,  0x7a,  0x6f,  0x69,  0x20,  
+   0x28,  0x43,  0x6c,  0x61,  0x73,  0x73,  0x20,  0x41,  
+   0x29,  0x20,  0x54,  0x61,  0x6e,  0x75,  0x73,  0x69,  
+   0x74,  0x76,  0x61,  0x6e,  0x79,  0x6b,  0x69,  0x61,  
+   0x64,  0x6f
+};
+static const CSSM_DATA anchor_53_derIssuer = { 178, (uint8 *)anchor_53_derIssuer_bytes };
+
+/*
+   Country         : HU
+   Locality        : Budapest
+   Org             : NetLock Halozatbiztonsagi Kft.
+   OrgUnit         : Tanusitvanykiadok
+   Common Name     : NetLock Uzleti (Class B) Tanusitvanykiado
+*/
+static const uint8 anchor_60_derIssuer_bytes[] = {
+   0x30,  0x81,  0x99,  0x31,  0x0b,  0x30,  0x09,  0x06,  
+   0x03,  0x55,  0x04,  0x06,  0x13,  0x02,  0x48,  0x55,  
+   0x31,  0x11,  0x30,  0x0f,  0x06,  0x03,  0x55,  0x04,  
+   0x07,  0x13,  0x08,  0x42,  0x75,  0x64,  0x61,  0x70,  
+   0x65,  0x73,  0x74,  0x31,  0x27,  0x30,  0x25,  0x06,  
+   0x03,  0x55,  0x04,  0x0a,  0x13,  0x1e,  0x4e,  0x65,  
+   0x74,  0x4c,  0x6f,  0x63,  0x6b,  0x20,  0x48,  0x61,  
+   0x6c,  0x6f,  0x7a,  0x61,  0x74,  0x62,  0x69,  0x7a,  
+   0x74,  0x6f,  0x6e,  0x73,  0x61,  0x67,  0x69,  0x20,  
+   0x4b,  0x66,  0x74,  0x2e,  0x31,  0x1a,  0x30,  0x18,  
+   0x06,  0x03,  0x55,  0x04,  0x0b,  0x13,  0x11,  0x54,  
+   0x61,  0x6e,  0x75,  0x73,  0x69,  0x74,  0x76,  0x61,  
+   0x6e,  0x79,  0x6b,  0x69,  0x61,  0x64,  0x6f,  0x6b,  
+   0x31,  0x32,  0x30,  0x30,  0x06,  0x03,  0x55,  0x04,  
+   0x03,  0x13,  0x29,  0x4e,  0x65,  0x74,  0x4c,  0x6f,  
+   0x63,  0x6b,  0x20,  0x55,  0x7a,  0x6c,  0x65,  0x74,  
+   0x69,  0x20,  0x28,  0x43,  0x6c,  0x61,  0x73,  0x73,  
+   0x20,  0x42,  0x29,  0x20,  0x54,  0x61,  0x6e,  0x75,  
+   0x73,  0x69,  0x74,  0x76,  0x61,  0x6e,  0x79,  0x6b,  
+   0x69,  0x61,  0x64,  0x6f
+};
+static const CSSM_DATA anchor_60_derIssuer = { 156, (uint8 *)anchor_60_derIssuer_bytes };
+
+/*
+ Country         : TR
+ Locality        : Ankara
+ Org             : (c) 2005 TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş.
+ Common Name     : TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı
+ Serial Number   : 01
+ Not Before      : 10:27:17 May 13, 2005
+ Not After       : 10:27:17 Mar 22, 2015
+*/
+static const uint8 turk1_derIssuer_bytes[] = {
+       0x30,  0x81,  0xb7,  0x31,  0x3f,  0x30,  0x3d,  0x06,  
+       0x03,  0x55,  0x04,  0x03,  0x0c,  0x36,  0x54,  0xc3,  
+       0x9c,  0x52,  0x4b,  0x54,  0x52,  0x55,  0x53,  0x54,  
+       0x20,  0x45,  0x6c,  0x65,  0x6b,  0x74,  0x72,  0x6f,  
+       0x6e,  0x69,  0x6b,  0x20,  0x53,  0x65,  0x72,  0x74,  
+       0x69,  0x66,  0x69,  0x6b,  0x61,  0x20,  0x48,  0x69,  
+       0x7a,  0x6d,  0x65,  0x74,  0x20,  0x53,  0x61,  0xc4,  
+       0x9f,  0x6c,  0x61,  0x79,  0xc4,  0xb1,  0x63,  0xc4,  
+       0xb1,  0x73,  0xc4,  0xb1,  0x31,  0x0b,  0x30,  0x09,  
+       0x06,  0x03,  0x55,  0x04,  0x06,  0x0c,  0x02,  0x54,  
+       0x52,  0x31,  0x0f,  0x30,  0x0d,  0x06,  0x03,  0x55,  
+       0x04,  0x07,  0x0c,  0x06,  0x41,  0x4e,  0x4b,  0x41,  
+       0x52,  0x41,  0x31,  0x56,  0x30,  0x54,  0x06,  0x03,  
+       0x55,  0x04,  0x0a,  0x0c,  0x4d,  0x28,  0x63,  0x29,  
+       0x20,  0x32,  0x30,  0x30,  0x35,  0x20,  0x54,  0xc3,  
+       0x9c,  0x52,  0x4b,  0x54,  0x52,  0x55,  0x53,  0x54,  
+       0x20,  0x42,  0x69,  0x6c,  0x67,  0x69,  0x20,  0xc4,  
+       0xb0,  0x6c,  0x65,  0x74,  0x69,  0xc5,  0x9f,  0x69,  
+       0x6d,  0x20,  0x76,  0x65,  0x20,  0x42,  0x69,  0x6c,  
+       0x69,  0xc5,  0x9f,  0x69,  0x6d,  0x20,  0x47,  0xc3,  
+       0xbc,  0x76,  0x65,  0x6e,  0x6c,  0x69,  0xc4,  0x9f,  
+       0x69,  0x20,  0x48,  0x69,  0x7a,  0x6d,  0x65,  0x74,  
+       0x6c,  0x65,  0x72,  0x69,  0x20,  0x41,  0x2e,  0xc5,  
+       0x9e,  0x2e
+};
+static const CSSM_DATA turk1_derIssuer = { 186, (uint8 *)turk1_derIssuer_bytes };
+
+/*
+ Country         : TR
+ Locality        : Ankara
+ Org             : TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005
+ Common Name     : TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı 
+ Serial Number   : 01
+ Not Before      : 10:07:57 Nov 7, 2005
+ Not After       : 10:07:57 Sep 16, 2015
+*/
+static const uint8 turk2_derIssuer_bytes[] = {
+       0x30,  0x81,  0xbe,  0x31,  0x3f,  0x30,  0x3d,  0x06,  
+       0x03,  0x55,  0x04,  0x03,  0x0c,  0x36,  0x54,  0xc3,  
+       0x9c,  0x52,  0x4b,  0x54,  0x52,  0x55,  0x53,  0x54,  
+       0x20,  0x45,  0x6c,  0x65,  0x6b,  0x74,  0x72,  0x6f,  
+       0x6e,  0x69,  0x6b,  0x20,  0x53,  0x65,  0x72,  0x74,  
+       0x69,  0x66,  0x69,  0x6b,  0x61,  0x20,  0x48,  0x69,  
+       0x7a,  0x6d,  0x65,  0x74,  0x20,  0x53,  0x61,  0xc4,  
+       0x9f,  0x6c,  0x61,  0x79,  0xc4,  0xb1,  0x63,  0xc4,  
+       0xb1,  0x73,  0xc4,  0xb1,  0x31,  0x0b,  0x30,  0x09,  
+       0x06,  0x03,  0x55,  0x04,  0x06,  0x13,  0x02,  0x54,  
+       0x52,  0x31,  0x0f,  0x30,  0x0d,  0x06,  0x03,  0x55,  
+       0x04,  0x07,  0x0c,  0x06,  0x41,  0x6e,  0x6b,  0x61,  
+       0x72,  0x61,  0x31,  0x5d,  0x30,  0x5b,  0x06,  0x03,  
+       0x55,  0x04,  0x0a,  0x0c,  0x54,  0x54,  0xc3,  0x9c,  
+       0x52,  0x4b,  0x54,  0x52,  0x55,  0x53,  0x54,  0x20,  
+       0x42,  0x69,  0x6c,  0x67,  0x69,  0x20,  0xc4,  0xb0,  
+       0x6c,  0x65,  0x74,  0x69,  0xc5,  0x9f,  0x69,  0x6d,  
+       0x20,  0x76,  0x65,  0x20,  0x42,  0x69,  0x6c,  0x69,  
+       0xc5,  0x9f,  0x69,  0x6d,  0x20,  0x47,  0xc3,  0xbc,  
+       0x76,  0x65,  0x6e,  0x6c,  0x69,  0xc4,  0x9f,  0x69,  
+       0x20,  0x48,  0x69,  0x7a,  0x6d,  0x65,  0x74,  0x6c,  
+       0x65,  0x72,  0x69,  0x20,  0x41,  0x2e,  0xc5,  0x9e,  
+       0x2e,  0x20,  0x28,  0x63,  0x29,  0x20,  0x4b,  0x61,  
+       0x73,  0xc4,  0xb1,  0x6d,  0x20,  0x32,  0x30,  0x30,  
+       0x35
+};
+static const CSSM_DATA turk2_derIssuer = { 193, (uint8 *)turk2_derIssuer_bytes };
+
+/*
+ Country         : TR
+ Locality        : Ankara
+ Org             : TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Aralık 2007
+ Common Name     : TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı 
+ Serial Number   : 01
+ Not Before      : 18:37:19 Dec 25, 2007
+ Not After       : 18:37:19 Dec 22, 2017
+*/
+static const uint8 turk3_derIssuer_bytes[] = {
+       0x30,  0x81,  0xbf,  0x31,  0x3f,  0x30,  0x3d,  0x06,  
+       0x03,  0x55,  0x04,  0x03,  0x0c,  0x36,  0x54,  0xc3,  
+       0x9c,  0x52,  0x4b,  0x54,  0x52,  0x55,  0x53,  0x54,  
+       0x20,  0x45,  0x6c,  0x65,  0x6b,  0x74,  0x72,  0x6f,  
+       0x6e,  0x69,  0x6b,  0x20,  0x53,  0x65,  0x72,  0x74,  
+       0x69,  0x66,  0x69,  0x6b,  0x61,  0x20,  0x48,  0x69,  
+       0x7a,  0x6d,  0x65,  0x74,  0x20,  0x53,  0x61,  0xc4,  
+       0x9f,  0x6c,  0x61,  0x79,  0xc4,  0xb1,  0x63,  0xc4,  
+       0xb1,  0x73,  0xc4,  0xb1,  0x31,  0x0b,  0x30,  0x09,  
+       0x06,  0x03,  0x55,  0x04,  0x06,  0x13,  0x02,  0x54,  
+       0x52,  0x31,  0x0f,  0x30,  0x0d,  0x06,  0x03,  0x55,  
+       0x04,  0x07,  0x0c,  0x06,  0x41,  0x6e,  0x6b,  0x61,  
+       0x72,  0x61,  0x31,  0x5e,  0x30,  0x5c,  0x06,  0x03,  
+       0x55,  0x04,  0x0a,  0x0c,  0x55,  0x54,  0xc3,  0x9c,  
+       0x52,  0x4b,  0x54,  0x52,  0x55,  0x53,  0x54,  0x20,  
+       0x42,  0x69,  0x6c,  0x67,  0x69,  0x20,  0xc4,  0xb0,  
+       0x6c,  0x65,  0x74,  0x69,  0xc5,  0x9f,  0x69,  0x6d,  
+       0x20,  0x76,  0x65,  0x20,  0x42,  0x69,  0x6c,  0x69,  
+       0xc5,  0x9f,  0x69,  0x6d,  0x20,  0x47,  0xc3,  0xbc,  
+       0x76,  0x65,  0x6e,  0x6c,  0x69,  0xc4,  0x9f,  0x69,  
+       0x20,  0x48,  0x69,  0x7a,  0x6d,  0x65,  0x74,  0x6c,  
+       0x65,  0x72,  0x69,  0x20,  0x41,  0x2e,  0xc5,  0x9e,  
+       0x2e,  0x20,  0x28,  0x63,  0x29,  0x20,  0x41,  0x72,  
+       0x61,  0x6c,  0xc4,  0xb1,  0x6b,  0x20,  0x32,  0x30,  
+       0x30,  0x37
+};
+static const CSSM_DATA turk3_derIssuer = { 194, (uint8 *)turk3_derIssuer_bytes };
+
+/*
+Cert File Name: globalSignRoot.cer
+   Country         : BE
+   Org             : GlobalSign nv-sa
+   OrgUnit         : Root CA
+   Common Name     : GlobalSign Root CA
+*/
+static const uint8 globalSignRoot_derIssuer_bytes[] = {
+   0x30,  0x57,  0x31,  0x0b,  0x30,  0x09,  0x06,  0x03,  
+   0x55,  0x04,  0x06,  0x13,  0x02,  0x42,  0x45,  0x31,  
+   0x19,  0x30,  0x17,  0x06,  0x03,  0x55,  0x04,  0x0a,  
+   0x13,  0x10,  0x47,  0x6c,  0x6f,  0x62,  0x61,  0x6c,  
+   0x53,  0x69,  0x67,  0x6e,  0x20,  0x6e,  0x76,  0x2d,  
+   0x73,  0x61,  0x31,  0x10,  0x30,  0x0e,  0x06,  0x03,  
+   0x55,  0x04,  0x0b,  0x13,  0x07,  0x52,  0x6f,  0x6f,  
+   0x74,  0x20,  0x43,  0x41,  0x31,  0x1b,  0x30,  0x19,  
+   0x06,  0x03,  0x55,  0x04,  0x03,  0x13,  0x12,  0x47,  
+   0x6c,  0x6f,  0x62,  0x61,  0x6c,  0x53,  0x69,  0x67,  
+   0x6e,  0x20,  0x52,  0x6f,  0x6f,  0x74,  0x20,  0x43,  
+   0x41
+};
+static const CSSM_DATA globalSignRoot_derIssuer = { 89, (uint8 *)globalSignRoot_derIssuer_bytes };
+
+/***********************
+Cert File Name: swisssign.der
+Subject Name       :
+   Country         : CH
+   Org             : SwissSign
+   Common Name     : SwissSign CA (RSA IK May 6 1999 18:00:58)
+   Email addrs     : ca@SwissSign.com
+   
+ This one has a bogus AuthorityKeyId, with a value of {0x30, 0} inside the octet string. 
+ ***********************/
+static const uint8 swisssign_derIssuer_bytes[] = {
+   0x30,  0x76,  0x31,  0x0b,  0x30,  0x09,  0x06,  0x03,  
+   0x55,  0x04,  0x06,  0x13,  0x02,  0x43,  0x48,  0x31,  
+   0x12,  0x30,  0x10,  0x06,  0x03,  0x55,  0x04,  0x0a,  
+   0x13,  0x09,  0x53,  0x77,  0x69,  0x73,  0x73,  0x53,  
+   0x69,  0x67,  0x6e,  0x31,  0x32,  0x30,  0x30,  0x06,  
+   0x03,  0x55,  0x04,  0x03,  0x13,  0x29,  0x53,  0x77,  
+   0x69,  0x73,  0x73,  0x53,  0x69,  0x67,  0x6e,  0x20,  
+   0x43,  0x41,  0x20,  0x28,  0x52,  0x53,  0x41,  0x20,  
+   0x49,  0x4b,  0x20,  0x4d,  0x61,  0x79,  0x20,  0x36,  
+   0x20,  0x31,  0x39,  0x39,  0x39,  0x20,  0x31,  0x38,  
+   0x3a,  0x30,  0x30,  0x3a,  0x35,  0x38,  0x29,  0x31,  
+   0x1f,  0x30,  0x1d,  0x06,  0x09,  0x2a,  0x86,  0x48,  
+   0x86,  0xf7,  0x0d,  0x01,  0x09,  0x01,  0x16,  0x10,  
+   0x63,  0x61,  0x40,  0x53,  0x77,  0x69,  0x73,  0x73,  
+   0x53,  0x69,  0x67,  0x6e,  0x2e,  0x63,  0x6f,  0x6d
+   
+};
+static const CSSM_DATA swisssign_derIssuer = { 120, (uint8 *)swisssign_derIssuer_bytes };
+
+/*
+ * Simple class to hold arrays of fields.
+ */
+class FieldArray {
+public:
+       /*
+        * Create from existing field array obtained from
+        * CSSM_CL_CertGetAllFields(). We'll do the CSSM_CL_FreeFields()
+        * in our destructor.
+        */
+       FieldArray(
+               CSSM_FIELD              *fields, 
+               uint32                  numFields,
+               CSSM_CL_HANDLE  clHand);
+       
+       /*
+        * Create empty array of specified size. We don't own the fields 
+        * themselves.
+        */
+       FieldArray(
+               uint32                  size);
+       
+       ~FieldArray();
+       
+       /*
+        * Append a field - no realloc!
+        */
+       void appendField(CSSM_FIELD &field);
+       
+       /* get specified field */
+       CSSM_FIELD &fieldAt(uint32 index);
+       
+       /* get nth occurence of field matching specified OID */
+       int  fieldForOid(
+               const CSSM_OID  &oid,
+               unsigned                n,                      // n == 0 --> first one
+               CSSM_FIELD              *&found);       // RETURNED
+               
+       CSSM_FIELD      *mFields;
+       uint32          mNumFields;             // sizeof of *fields
+       uint32          mMallocdSize;   // if NULL, read-only
+       CSSM_CL_HANDLE  mClHand;
+};
+
+FieldArray::FieldArray(
+       CSSM_FIELD *fields, 
+       uint32 numFields,
+       CSSM_CL_HANDLE clHand)
+{
+       mFields = fields;
+       mNumFields = numFields;
+       mMallocdSize = 0;
+       mClHand = clHand;
+}
+
+FieldArray::FieldArray(
+       uint32 size)
+{
+       unsigned len = sizeof(CSSM_FIELD) * size;
+       mFields = (CSSM_FIELD_PTR)malloc(len);
+       memset(mFields, 0, len);
+       mNumFields = 0;
+       mMallocdSize = size;
+       mClHand = 0;
+}
+
+FieldArray::~FieldArray()
+{
+       if(mMallocdSize != 0) {
+               /* 
+                * Just free the array of fields we mallocd, not the fields 
+                * themselves
+                */
+               free(mFields);
+       }
+       else {
+               /* The CL mallocd these fields, tell it to free the whole thing */
+               CSSM_RETURN crtn = CSSM_CL_FreeFields(mClHand, 
+                       mNumFields, &mFields);
+               if(crtn) {
+                       printError("CSSM_CL_FreeFields", crtn);
+               }
+       }
+       mFields = NULL;
+       mNumFields = 0;
+       mMallocdSize = 0;
+}
+
+void FieldArray::appendField(
+       CSSM_FIELD &field)
+{
+       if(mMallocdSize == 0) {
+               printf("***Attempt to append to a read-only FieldArray\n");
+               exit(1);
+       }
+       if(mNumFields >= mMallocdSize) {
+               printf("***Attempt to append past present size of FieldArray\n");
+               exit(1);
+       }
+       mFields[mNumFields] = field;
+       mNumFields++;
+}
+
+CSSM_FIELD &FieldArray::fieldAt(
+       uint32 index)
+{
+       if(index >= mNumFields) {
+               printf("***Attempt to access past present size of FieldArray\n");
+               exit(1);
+       }
+       return mFields[index];
+}
+
+/* get nth occurence of field matching specified OID */
+/* returns nonzero on error */
+int FieldArray::fieldForOid(
+       const CSSM_OID  &oid,
+       unsigned                n,                      // n == 0 --> first one
+       CSSM_FIELD              *&found)        // RETURNED
+{
+       unsigned foundDex = 0;
+       for(unsigned dex=0; dex<mNumFields; dex++) {
+               CSSM_FIELD &field = mFields[dex];
+               if(appCompareCssmData(&field.FieldOid, &oid)) {
+                       if(foundDex == n) {
+                               found = &field;
+                               return 0;
+                       }
+                       foundDex++;
+               }
+       }
+       printf("FieldArray::fieldForOid field not found\n");
+       return 1;
+}
+
+/*
+ * How many items in a NULL-terminated array of pointers?
+ */
+static unsigned nssArraySize(
+       const void **array)
+{
+    unsigned count = 0;
+    if (array) {
+               while (*array++) {
+                       count++;
+               }
+    }
+    return count;
+}
+
+static void doPrintCert(
+       const CSSM_DATA &cert)
+{
+       printCert(cert.Data, cert.Length, CSSM_TRUE);
+}
+
+/*
+ * The extensions whose presence causes us to skip the "compare
+ * encoded and original TBS" test.
+ */
+#define USE_SKIPPED_EXTENS             1
+#if            USE_SKIPPED_EXTENS
+static const CSSM_OID *skippedExtens[] = {     // %%% FIXME: this is a workaround for <rdar://8265523>; shouldn't need to skip!
+       &CSSMOID_PolicyMappings,
+       &CSSMOID_PolicyConstraints
+};
+#define NUM_SKIPPED_EXTENS     \
+       (sizeof(skippedExtens) / sizeof(skippedExtens[0]))
+#endif /* USE_SKIPPED_EXTENS */
+
+static const CSSM_DATA *skippedCerts[] = {
+       &anchor_46_derIssuer,
+       &anchor_53_derIssuer,
+       &anchor_60_derIssuer,
+       &turk1_derIssuer,
+       &turk2_derIssuer,
+       &turk3_derIssuer,
+       &globalSignRoot_derIssuer,
+       &swisssign_derIssuer
+};
+#define NUM_SKIPPED_CERTS      (sizeof(skippedCerts) / sizeof(skippedCerts[0]))
+
+static bool skipThisCert(
+       const NSS_TBSCertificate &tbs)
+{
+       /* search by extension - currently unused */
+       unsigned dex;
+       #if USE_SKIPPED_EXTENS
+       unsigned numExtens = nssArraySize((const void **)tbs.extensions);
+       /* skip this section if that's empty - compiler warning causes failure */
+       for(dex=0; dex<numExtens; dex++) {
+               NSS_CertExtension *exten = tbs.extensions[dex];
+               CSSM_OID *oid = &exten->extnId;
+               for(unsigned skipDex=0; skipDex<NUM_SKIPPED_EXTENS; skipDex++) {
+                       if(appCompareCssmData(skippedExtens[skipDex], oid)) {
+                               return true;
+                       }
+               }
+       }
+       #endif  /* USE_SKIPPED_EXTENS */
+       
+       /* search by specific issuer */
+       for(dex=0; dex<NUM_SKIPPED_CERTS; dex++) {
+               if(appCompareCssmData(skippedCerts[dex], &tbs.derIssuer)) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+/*
+ * Given a field OID, figure out what WE think this field is.
+ */
+/* the field types we grok */
+typedef enum {
+       FT_Unknown,             // probably means we're out of sync with the CL
+       FT_Normal,              // standard component of TBS
+       FT_ReadOnly,    // Read only, don't use to create template
+       FT_NotTBS,              // part of top-level cert, don't use to create TBS
+       FT_ExtenParsed, // extension the CL SHOULD HAVE parsed
+       FT_ExtenUnknown // extension the CL should NOT have parsed              
+} FieldType;
+
+/* map OID --> FieldType */
+typedef struct {
+       const CSSM_OID  *oid;
+       FieldType               type;
+} FieldOidType;
+
+/* 
+ * The CL-specific mapping table. 
+ * This has to change whenever the CL is modified to add or delete
+ * an extension or field!
+ * For newbies, a tip: this basically has to stay in sync with the 
+ * fieldFuncTable array in Security/AppleX509CL/CertFields.cpp. 
+ */
+FieldOidType knownFields[] = {
+       {       &CSSMOID_X509V1Version, FT_Normal },
+       {       &CSSMOID_X509V1SerialNumber, FT_Normal },
+       {       &CSSMOID_X509V1IssuerNameCStruct, FT_Normal },
+       {       &CSSMOID_X509V1SubjectNameCStruct, FT_Normal },
+       {       &CSSMOID_X509V1SignatureAlgorithmTBS, FT_Normal },
+       {       &CSSMOID_X509V1SignatureAlgorithm, FT_NotTBS },
+       {       &CSSMOID_X509V1ValidityNotBefore, FT_Normal },
+       {       &CSSMOID_X509V1ValidityNotAfter, FT_Normal },
+       {       &CSSMOID_X509V1CertificateIssuerUniqueId, FT_Normal },
+       {       &CSSMOID_X509V1CertificateSubjectUniqueId, FT_Normal },
+       /* only one of these two can be set - use the SubjectPublicKeyInfo
+        * version */
+       {       &CSSMOID_X509V1SubjectPublicKeyCStruct, FT_Normal },
+       {       &CSSMOID_CSSMKeyStruct, FT_ReadOnly },
+       {       &CSSMOID_X509V1Signature, FT_NotTBS },
+       {   &CSSMOID_X509V1IssuerName, FT_ReadOnly },           // DER encoded
+       {   &CSSMOID_X509V1SubjectName, FT_ReadOnly },          // DER encoded
+       {   &CSSMOID_X509V1IssuerNameStd, FT_ReadOnly },        // DER encoded
+       {   &CSSMOID_X509V1SubjectNameStd,FT_ReadOnly },        // DER encoded
+               
+       /* Extensions */
+       {       &CSSMOID_KeyUsage, FT_ExtenParsed },
+       {   &CSSMOID_BasicConstraints, FT_ExtenParsed },
+       {       &CSSMOID_ExtendedKeyUsage, FT_ExtenParsed } ,
+       {       &CSSMOID_SubjectKeyIdentifier, FT_ExtenParsed } ,
+       {       &CSSMOID_AuthorityKeyIdentifier, FT_ExtenParsed } ,
+       {       &CSSMOID_SubjectAltName, FT_ExtenParsed } ,
+       {       &CSSMOID_IssuerAltName, FT_ExtenParsed } ,
+       {       &CSSMOID_CertificatePolicies, FT_ExtenParsed } ,
+       {       &CSSMOID_NetscapeCertType, FT_ExtenParsed } ,
+       {       &CSSMOID_CrlDistributionPoints, FT_ExtenParsed },
+       {   &CSSMOID_AuthorityInfoAccess, FT_ExtenParsed },
+       {   &CSSMOID_SubjectInfoAccess, FT_ExtenParsed },
+       {   &CSSMOID_X509V3CertificateExtensionCStruct, FT_ExtenUnknown },
+       {   &CSSMOID_QC_Statements, FT_ExtenParsed },
+       {       &CSSMOID_NameConstraints, FT_ExtenParsed },
+       {       &CSSMOID_PolicyMappings, FT_ExtenParsed },
+       {       &CSSMOID_PolicyConstraints, FT_ExtenParsed },
+//     {       &CSSMOID_InhibitAnyPolicy, FT_ExtenParsed } //%%% FIXME: CSSMOID_InhibitAnyPolicy not exported!?
+};
+#define NUM_KNOWN_FIELDS (sizeof(knownFields) / sizeof(knownFields[0]))
+
+static FieldType typeForOid(
+       const CSSM_OID &oid)
+{
+       for(unsigned dex=0; dex<NUM_KNOWN_FIELDS; dex++) {
+               FieldOidType &ft = knownFields[dex];
+               if(appCompareCssmData(&oid, ft.oid)) {
+                       return ft.type;
+               }
+       }
+       /* not found */
+       return FT_Unknown;
+}
+
+static const char *fieldTypeStr(
+       FieldType type)
+{
+       switch(type) {
+               case FT_Unknown:                return "FT_Unknown";
+               case FT_Normal:                 return "FT_Normal";
+               case FT_ReadOnly:               return "FT_ReadOnly";
+               case FT_NotTBS:                 return "FT_NotTBS";
+               case FT_ExtenParsed:    return "FT_ExtenParsed";
+               case FT_ExtenUnknown:   return "FT_ExtenUnknown";
+               default:
+                       printf("***BRRZAP!\n");
+                       exit(1);
+       }
+}
+
+static const uint8 emptyAuthKeyId[2] = {0x30, 0};
+
+/*
+ * Extensions come in two flavors - parsed and not. However the 
+ * CL will give us an unparsed version for extensions it normally 
+ * understands but failed to decode. Detect that, and basic
+ * extension formatting screwups, here.
+ */
+static int vfyExtens(
+       FieldArray &extenFields,
+       const CSSM_DATA &cert,          // for error display only 
+       CSSM_BOOL quiet)        
+{
+       for(unsigned dex=0; dex<extenFields.mNumFields; dex++) {
+               CSSM_FIELD &extenField = extenFields.fieldAt(dex);
+               FieldType type = typeForOid(extenField.FieldOid);
+               FieldType expectType;
+               
+               /* first verify well-formed extension field */
+               CSSM_DATA &fieldValue = extenField.FieldValue;
+               CSSM_X509_EXTENSION *exten = (CSSM_X509_EXTENSION *)fieldValue.Data;
+               if((exten == NULL) || 
+                               (fieldValue.Length != sizeof(CSSM_X509_EXTENSION))) {
+                       doPrintCert(cert);
+                       printf("***Malformed CSSM_X509_EXTENSION\n");
+                       if(testError(quiet)) {
+                               return 1;
+                       }
+                       /* well let's limp along */
+                       continue;
+               }
+
+               /* currently (since Radar 3593624), these are both always valid */
+               if((exten->BERvalue.Data == NULL) || 
+                  (exten->value.parsedValue == NULL)) {  /* actually, one of three variants */
+                       printf("***Malformed CSSM_X509_EXTENSION (1)\n");
+                       return 1;
+               }
+               
+               switch(exten->format) {
+                       case CSSM_X509_DATAFORMAT_ENCODED:
+                               if(type != FT_ExtenUnknown) {
+                                       doPrintCert(cert);
+                                       printf("***Entension format ENCODED, expected PARSED\n");
+                                       if(testError(quiet)) {
+                                               return 1;
+                                       }
+                               }
+                               
+                               /* 
+                                * Now make sure that the underlying extension ID isn't
+                                * one that the CL was SUPPOSED to parse 
+                                */
+                               // %%% FIXME: need to investigate why these are not fully parsed: <rdar://8265523>
+                               if(appCompareCssmData(&exten->extnId, &CSSMOID_PolicyConstraints)) {
+                                       printf("...skipping policyConstraints extension per <rdar://8265523> (fix me!)\n");
+                                       break;
+                               }
+                               if(appCompareCssmData(&exten->extnId, &CSSMOID_PolicyMappings)) {
+                                       printf("...skipping policyMappings extension per <rdar://8265523> (fix me!)\n");
+                                       break;
+                               }
+                               
+                               expectType = typeForOid(exten->extnId);
+                               if(expectType != FT_Unknown) {
+                                       /* 
+                                        * Swisscom root has an authorityKeyId extension with an illegal value, 
+                                        * data inside the octet string is <30 00>, no context-specific wrapper
+                                        * or tag. 
+                                        * Instead of a hopeless complaint about that cert, let's just tolerate it 
+                                        * like this...
+                                        */
+                                       if(appCompareCssmData(&exten->extnId, &CSSMOID_AuthorityKeyIdentifier) &&
+                                          (exten->BERvalue.Length == 2) &&
+                                          !memcmp(emptyAuthKeyId, exten->BERvalue.Data, 2)) {
+                                               printf("...skipping bogus swisssign AuthorityKeyId\n");
+                                               break;
+                                       }
+                                       doPrintCert(cert);
+                                       printf("***underlying exten type %s, expect Unknown\n",
+                                               fieldTypeStr(expectType));
+                                       if(testError(quiet)) {
+                                               return 1;
+                                       }
+                               }
+                               break;
+                               
+                       case CSSM_X509_DATAFORMAT_PARSED:
+                               if(type != FT_ExtenParsed) {
+                                       doPrintCert(cert);
+                                       printf("***Entension format PARSED, expected ENCODED\n");
+                                       if(testError(quiet)) {
+                                               return 1;
+                                       }
+                               }
+                               if(exten->value.parsedValue == NULL) {
+                                       doPrintCert(cert);
+                                       printf("***Parsed extension with NULL parsedValue\n");
+                                       if(testError(quiet)) {
+                                               return 1;
+                                       }
+                               }
+                               break;
+                               
+                       default:
+                               doPrintCert(cert);
+                               printf("***Unknown Entension format %u\n",
+                                       exten->format);
+                               if(testError(quiet)) {
+                                       return 1;
+                               }
+                               break;
+                               
+               }       /* switch(exten.format) */
+       }
+       return 0;
+}
+
+/*
+ * Here's the hard part.
+ * Given a raw cert and its components in two FieldArrays, crate a TBS
+ * cert from scratch from those fields and ensure that the result 
+ * is the same as the raw TBS field in the original cert. 
+ */
+static int buildTbs(
+       CSSM_CL_HANDLE  clHand,
+       const CSSM_DATA &rawCert,
+       FieldArray              &allFields,             // on entry, standard fields
+       FieldArray              &extenFields,   // extensions only 
+       CSSM_BOOL               quiet,
+       CSSM_BOOL               verbose,
+       CSSM_BOOL               writeBlobs)
+{
+       /*
+        * First do raw BER-decode in two ways - one to get the 
+        * extensions as they actuallly appear in the cert, and one
+        * to get the raw undecoded TBS.
+        */
+       SecAsn1CoderRef coder;
+       OSStatus ortn = SecAsn1CoderCreate(&coder);
+       if(ortn) {
+               cssmPerror("SecAsn1CoderCreate", ortn);
+               return testError(quiet);
+       }
+       
+       NSS_SignedCertOrCRL signedCert; // with TBS as ASN_ANY
+       memset(&signedCert, 0, sizeof(signedCert));
+       if(SecAsn1DecodeData(coder, &rawCert, kSecAsn1SignedCertOrCRLTemplate,
+                       &signedCert)) {
+               doPrintCert(rawCert);
+               printf("***Error decoding cert to kSecAsn1SignedCertOrCRL\n");
+               return testError(quiet);
+       }
+       
+       NSS_Certificate fullCert;               // fully decoded
+       memset(&fullCert, 0, sizeof(fullCert));
+       if(SecAsn1DecodeData(coder, &rawCert, kSecAsn1SignedCertTemplate,
+                       &fullCert)) {
+               doPrintCert(rawCert);
+               printf("***Error decoding cert to kSecAsn1Certificate\n");
+               return testError(quiet);
+       }
+        
+       NSS_TBSCertificate &tbs = fullCert.tbs;
+       unsigned numExtens = nssArraySize((const void **)tbs.extensions);
+       if(numExtens != extenFields.mNumFields) {
+               /* The CL told us the wrong number of extensions */
+               doPrintCert(rawCert);
+               printf("***NSS says %u extens, CL says %u\n", numExtens,
+                       (unsigned)extenFields.mNumFields);
+               return testError(quiet);
+       }
+       
+       if(skipThisCert(tbs)) {
+               if(verbose) {
+                       printf("   ...skipping TBS blob check\n");
+               }
+               SecAsn1CoderRelease(coder);
+               return 0;
+       }       
+       
+       /*
+        * The CL returns extension fields in an order which differs from
+        * the order of the extensions in the actual cert (because it
+        * does a table-based lookup, field by field, when doing a 
+        * CSSM_CL_CertGetAllFields()). We have to add the extensions
+        * from extenFields to allFields in the order they appear in 
+        * OUR decoded fullCert.
+        */
+       unsigned numUnknowns = 0;
+       for(unsigned dex=0; dex<numExtens; dex++) {
+               NSS_CertExtension *exten = tbs.extensions[dex];
+               CSSM_OID &oid = exten->extnId;
+               FieldType type = typeForOid(oid);
+               CSSM_FIELD *found = NULL;
+               int rtn;
+               switch(type) {
+                       case FT_ExtenParsed:
+                               /* 
+                                * look for this exact extension
+                                * NOTE we're assuming that only one copy of 
+                                * each specific parsed extension exists. The 
+                                * 509 spec does't specifically require this but
+                                * I've never seen a case of multiple extensions
+                                * of the same type in one cert. 
+                                */
+                               rtn = extenFields.fieldForOid(oid, 0, found);
+                               break;
+                       case FT_Unknown:
+                               /* search for nth unparsed exten field */
+                               rtn = extenFields.fieldForOid(
+                                       CSSMOID_X509V3CertificateExtensionCStruct,
+                                       numUnknowns++,
+                                       found);
+                               break;
+                       default:
+                               /* caller was already supposed to check this */
+                               doPrintCert(rawCert);
+                               printf("***HEY! buildTBS was given a bogus extension!\n");
+                               return 1;
+               }
+               if(rtn) {
+                       doPrintCert(rawCert);
+                       printf("***buildTBS could not find extension in CL's fields\n");
+                       return testError(quiet);
+               }
+               
+               allFields.appendField(*found);
+       }       /* processing extensions */
+       
+       /* 
+        * OK, the field array in allFields is ready to go down to 
+        * the CL.
+        */
+       CSSM_RETURN crtn;
+       CSSM_DATA clTbs = {0, NULL};
+       crtn = CSSM_CL_CertCreateTemplate(clHand,
+               allFields.mNumFields,
+               allFields.mFields,
+               &clTbs);
+       if(crtn) {
+               doPrintCert(rawCert);
+               printError("CSSM_CL_CertCreateTemplate", crtn);
+               return testError(quiet);
+       }
+       
+       /*
+        * The moment of truth. Is that template identical to the 
+        * raw undecoded TBS blob we got by decoding a NSS_SignedCertOrCRL?
+        */
+       int ourRtn = 0;
+       if(!appCompareCssmData(&clTbs, &signedCert.tbsBlob)) {
+               doPrintCert(rawCert);
+               printf("***Encoded TBS does not match decoded TBS.\n");
+               if(writeBlobs) {
+                       writeFile(ENC_TBS_BLOB, clTbs.Data, clTbs.Length);
+                       writeFile(DEC_TBS_BLOB, signedCert.tbsBlob.Data, 
+                               signedCert.tbsBlob.Length);
+                       printf("...wrote TBS blobs to %s and %s\n",
+                               ENC_TBS_BLOB, DEC_TBS_BLOB);
+               }
+               ourRtn = testError(quiet);
+       }
+       CSSM_FREE(clTbs.Data);
+       SecAsn1CoderRelease(coder);
+       return ourRtn;
+}
+
+/* verify root with itself using TP */
+static int verifyRoot(
+       CSSM_TP_HANDLE  tpHand,
+       CSSM_CL_HANDLE  clHand,
+       CSSM_CSP_HANDLE cspHand,
+       const CSSM_DATA &cert,
+       CSSM_BOOL               allowExpired,
+       CSSM_BOOL               useTrustSettings,
+       CSSM_BOOL               quiet)
+{
+       BlobList blobs;
+       blobs.addBlob(cert, CSSM_TRUE);
+       int i;
+       
+       const char *certStatus;
+       if(useTrustSettings) {
+               /*
+                * CSSM_CERT_STATUS_IS_IN_INPUT_CERTS 
+                * CSSM_CERT_STATUS_IS_ROOT
+                * CSSM_CERT_STATUS_TRUST_SETTINGS_FOUND_SYSTEM
+                * CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST
+                */
+               certStatus = "0:0x314";  
+       }
+       else {
+               /*
+                * CSSM_CERT_STATUS_IS_IN_INPUT_CERTS (new since radar 3855635 was fixed)
+                * CSSM_CERT_STATUS_IS_IN_ANCHORS
+                * CSSM_CERT_STATUS_IS_ROOT
+                */
+               certStatus = "0:0x1C";  
+       }
+
+       /* try one with allowExpiredRoot false, then true on error and if so 
+        * enabled to make sure we know what's going wrong */
+       CSSM_BOOL expireEnable = CSSM_FALSE;
+       for(int dex=0; dex<2; dex++) {
+               i = certVerifySimple(tpHand, clHand, cspHand,
+                       blobs,          // certs
+                       blobs,          // and roots
+                       CSSM_FALSE, // useSystemAnchors
+                       CSSM_TRUE,  // leaf is CA
+                       expireEnable,
+                       CVP_Basic,
+                       NULL,           // SSL host
+                       CSSM_FALSE, // SSL client
+                       NULL,           // sender email
+                       0,                      // key use
+                       NULL,           // expected error str
+                       0, NULL,        // per-cert errors
+                       1, &certStatus, // per-cert status
+                       useTrustSettings,
+                       quiet, 
+                       CSSM_FALSE);    // verbose
+               if(i == 0) {
+                       /* success */
+                       if(dex == 1) {
+                               printf("...warning: expired root detected. Be aware.\n");
+                       }
+                       return 0;
+               }
+               if(!allowExpired) {
+                       /* no second chance */
+                       return i;
+               }
+               expireEnable = CSSM_TRUE;
+               if(useTrustSettings) {
+                       /* now expect EXPIRED, IS_ROOT, IS_IN_INPUT_CERTS, TRUST_SETTINGS_FOUND_SYSTEM,
+                        * CSSM_CERT_STATUS_TRUST_SETTINGS_TRUST */
+                       certStatus = "0:0x315"; 
+               }
+               else {
+                       /* now expect EXPIRED, IS_ROOT, IS_IN_ANCHORS, IS_IN_INPUT_CERTS */
+                       certStatus = "0:0x1d";  
+               }
+       }
+       return i;
+}
+
+
+static int doTest(
+       CSSM_CL_HANDLE  clHand,
+       CSSM_TP_HANDLE  tpHand,
+       CSSM_CSP_HANDLE cspHand,
+       const CSSM_DATA &cert,
+       CSSM_BOOL               allowExpired,
+       CSSM_BOOL               quiet,
+       CSSM_BOOL               verbose,
+       CSSM_BOOL               writeBlobs,
+       CSSM_BOOL               useTrustSettings)
+{
+       /* first see if this anchor self-verifies. */
+       if(verifyRoot(tpHand, clHand, cspHand, cert, allowExpired, 
+                       useTrustSettings, quiet)) {
+               doPrintCert(cert);
+               printf("***This anchor does not self-verify!\n");
+               return testError(quiet);
+       }
+       
+       /* have the CL parse it to the best of its ability */
+       CSSM_FIELD_PTR certFields;
+       uint32 numFields;
+       CSSM_RETURN crtn = CSSM_CL_CertGetAllFields(clHand, &cert, &numFields, 
+               &certFields);
+       if(crtn) {
+               printError("CSSM_CL_CertGetAllFields", crtn);
+               doPrintCert(cert);
+               printf("***The CL can not parse this anchor!\n");
+               return testError(quiet);
+       }
+
+       /* save, this object does the free fields when it goes out of scope */
+       FieldArray parsed(certFields, numFields, clHand);
+       
+       /* 
+        * We're going to build a TBSCert from these received fields.
+        * Extensions need to be processed specially because they 
+        * come back from the CL ordered differently than they appear
+        * in the cert. 
+        *
+        * First make two buckets for making copies of incoming fields.
+        */
+       FieldArray forCreate(numFields);        // for creating template
+       FieldArray extenFields(numFields);
+       
+       for(unsigned dex=0; dex<numFields; dex++) {
+               CSSM_FIELD &parsedField = parsed.fieldAt(dex);
+               FieldType type = typeForOid(parsedField.FieldOid);
+               switch(type) {
+                       case FT_Normal:
+                               forCreate.appendField(parsedField);
+                               break;
+                       case FT_ReadOnly:
+                       case FT_NotTBS:
+                               /* ignore */
+                               break;
+                       case FT_ExtenParsed:
+                       case FT_ExtenUnknown:
+                               /* extensions, save and process later */
+                               extenFields.appendField(parsedField);
+                               break;
+                       default:
+                               doPrintCert(cert);
+                               printf("***This anchor contains an unknown field!\n");
+                               if(testError(quiet)) {
+                                       return 1;
+                               }
+                               /* well let's limp along */
+                               forCreate.appendField(parsedField);
+                               break;
+               }
+       }
+       
+       /* basic extension verification */
+       if(vfyExtens(extenFields, cert, quiet)) {
+               return 1;
+       }
+       return buildTbs(clHand, cert, forCreate, extenFields, quiet, 
+               verbose, writeBlobs);
+}
+
+int main(int argc, char **argv)
+{
+       CSSM_BOOL quiet = CSSM_FALSE;
+       CSSM_BOOL verbose = CSSM_FALSE;
+       CSSM_BOOL writeBlobs = CSSM_FALSE;
+       CSSM_BOOL allowExpired = CSSM_FALSE;
+       CSSM_BOOL useTrustSettings = CSSM_FALSE;
+
+       for(int arg=1; arg<argc; arg++) {
+               switch(argv[arg][0]) {
+                       case 'q':
+                               quiet = CSSM_TRUE;
+                               break;
+                       case 'v':
+                               verbose = CSSM_TRUE;
+                               break;
+                       case 'w':
+                               writeBlobs = CSSM_TRUE;
+                               break;
+                       case 't':
+                               useTrustSettings = CSSM_TRUE;
+                               break;
+                       case 'e':
+                               allowExpired = CSSM_TRUE;
+                               break;
+                       default:
+                               usage(argv);
+               }
+       }
+       
+       printf("Starting anchorTest; args: ");
+       for(int i=1; i<argc; i++) {
+               printf("%s ", argv[i]);
+       }
+       printf("\n");
+
+       /* get system anchors only; convert to CSSM */
+       CFArrayRef cfAnchors;
+       OSStatus ortn;
+       CSSM_DATA *anchors;
+       unsigned numAnchors;
+       ortn = getSystemAnchors(&cfAnchors, &anchors, &numAnchors);
+       if(ortn) {
+               exit(1);
+       }
+       if(numAnchors < 50) {
+               printf("***Hey! I can only find %u anchors; there should be way more than that.\n",
+                       numAnchors);
+               exit(1);
+       }
+
+       CSSM_CL_HANDLE clHand = clStartup();
+       CSSM_TP_HANDLE tpHand = tpStartup();
+       CSSM_CSP_HANDLE cspHand = cspStartup();
+       if((clHand == 0) || (tpHand == 0) || (cspHand == 0)) {
+               return 0;
+       }
+
+       int rtn = 0;
+       for(unsigned dex=0; dex<numAnchors; dex++) {
+               if(!quiet) {
+                       printf("...anchor %u\n", dex);
+               }
+               rtn = doTest(clHand, tpHand, cspHand, anchors[dex], allowExpired,
+                       quiet, verbose, writeBlobs, useTrustSettings);
+               if(rtn) {
+                       break;
+               }
+       }
+       if(rtn == 0) {
+               if(!quiet) {
+                       printf("...%s success.\n", argv[0]);
+               }
+       }
+       return rtn;
+}