MYCertGen.m
author Jens Alfke <jens@mooseyard.com>
Wed Jul 01 14:19:13 2009 -0700 (2009-07-01)
changeset 26 d9c2a06d4e4e
parent 21 2c300b15b381
permissions -rw-r--r--
Whew, lots and lots of changes accumulated over the past few weeks. Mostly fixes for bugs I discovered while retrofitting Cloudy to use MYCrypto.
     1 //
     2 //  MYCertGen.m
     3 //  MYCrypto
     4 //
     5 //  Created by Jens Alfke on 4/3/09.
     6 //  Copyright 2009 Jens Alfke. All rights reserved.
     7 //
     8 
     9 //  NOTE: This module has been replaced by MYCertificateInfo, which isn't dependent on
    10 //  CSSM APIs that aren't available on iPhone.
    11 
    12 //  Derived from ...
    13 
    14 //
    15 //  CertificateGeneration.m
    16 //  Keychain
    17 //
    18 //  Created by Wade Tregaskis on Tue May 27 2003.
    19 //
    20 //  Copyright (c) 2003 - 2007, Wade Tregaskis.  All rights reserved.
    21 //  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
    22 //    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    23 //    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    24 //    * Neither the name of Wade Tregaskis nor the names of any other contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    25 //  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    26 
    27 #import "MYCertGen.h"
    28 #import "MYCrypto_Private.h"
    29 #import "MYIdentity.h"
    30 #import <Security/Security.h>
    31 
    32 
    33 
    34 static CSSM_X509_NAME* createNameList( NSDictionary *name );
    35 static CSSM_X509_TIME* timeForNSDate(NSDate *date);
    36 
    37 static NSData* NSDataFromDataNoCopy(CSSM_DATA data, BOOL freeWhenDone);
    38 
    39 static BOOL intToDER(uint32_t theInt, CSSM_DATA *data);
    40 
    41 
    42 
    43 NSData* MYCertificateCreateTemplate(const CSSM_X509_NAME *subject, const CSSM_X509_NAME *issuer,
    44                                     NSDate *validFrom, NSDate *validTo,
    45                                     uint32_t serialNumber,
    46                                     const CSSM_X509_EXTENSION **extensions, unsigned nExtensions,
    47                                     MYPublicKey *publicKey,
    48                                     const CSSM_X509_ALGORITHM_IDENTIFIER *signingAlgorithm,
    49                                     CSSM_CL_HANDLE clHandle) 
    50 {
    51     CAssert(subject);
    52     CAssert(issuer);
    53     CAssert(publicKey);
    54     CAssert(signingAlgorithm);
    55     CAssert(clHandle);
    56 
    57     const CSSM_KEY *cssmPubKey = publicKey.cssmKey;
    58     if (cssmPubKey->KeyHeader.BlobType != CSSM_KEYBLOB_RAW) {
    59         cssmPubKey = publicKey._unwrappedCSSMKey;
    60         if (!cssmPubKey) {
    61             Warn(@"MYCertificateCreateTemplate: unable to unwrap public key %@", publicKey);
    62             return nil;
    63         }
    64     }
    65 
    66     uint32_t numberOfFields = 5; // always requires at least 5 user-supplied fields
    67     if (serialNumber)
    68         ++numberOfFields;
    69     if (validFrom)
    70         ++numberOfFields;
    71     if (validTo)
    72         ++numberOfFields;
    73     numberOfFields += nExtensions;
    74     
    75     CSSM_FIELD fields[numberOfFields];
    76 
    77     // now we fill in the fields appropriately
    78     
    79     uint32_t index = 0;
    80     fields[index].FieldOid = CSSMOID_X509V1Version;
    81     intToDER(2, &(fields[index++].FieldValue));
    82 
    83     if (serialNumber) {
    84         fields[index].FieldOid = CSSMOID_X509V1SerialNumber;
    85         intToDER(serialNumber, &fields[index].FieldValue);
    86         index++;
    87     }
    88 
    89     fields[index].FieldOid = CSSMOID_X509V1IssuerNameCStruct;
    90     fields[index].FieldValue.Data = (uint8_t*)issuer;
    91     fields[index++].FieldValue.Length = sizeof(CSSM_X509_NAME);
    92 
    93     fields[index].FieldOid = CSSMOID_X509V1SubjectNameCStruct;
    94     fields[index].FieldValue.Data = (uint8_t*)subject;
    95     fields[index++].FieldValue.Length = sizeof(CSSM_X509_NAME);
    96 
    97     if (validFrom) {
    98         fields[index].FieldOid = CSSMOID_X509V1ValidityNotBefore;
    99         fields[index].FieldValue.Data = (uint8_t*)timeForNSDate(validFrom);
   100         fields[index++].FieldValue.Length = sizeof(CSSM_X509_TIME);
   101     }
   102     
   103     if (validTo) {
   104         fields[index].FieldOid = CSSMOID_X509V1ValidityNotAfter;
   105         fields[index].FieldValue.Data = (uint8_t*)timeForNSDate(validTo);
   106         fields[index++].FieldValue.Length = sizeof(CSSM_X509_TIME);
   107     }
   108     
   109     fields[index].FieldOid = CSSMOID_CSSMKeyStruct;
   110     fields[index].FieldValue.Data = (uint8_t*)cssmPubKey;
   111     fields[index++].FieldValue.Length = sizeof(CSSM_KEY);
   112 
   113     fields[index].FieldOid = CSSMOID_X509V1SignatureAlgorithmTBS;
   114     fields[index].FieldValue.Data = (uint8_t*)signingAlgorithm;
   115     fields[index++].FieldValue.Length = sizeof(CSSM_X509_ALGORITHM_IDENTIFIER);
   116     
   117     for (unsigned i=0; i<nExtensions; i++) {
   118         fields[index].FieldOid = extensions[i]->extnId;
   119         fields[index].FieldValue.Data = (uint8_t*)extensions[i];
   120         fields[index++].FieldValue.Length = sizeof(CSSM_X509_EXTENSION);
   121     }
   122     CAssert(index == numberOfFields);
   123     
   124     CSSM_DATA result = {};
   125     checkcssm(CSSM_CL_CertCreateTemplate(clHandle, numberOfFields, fields, &result),
   126               @"CSSM_CL_CertCreateTemplate");
   127     return NSDataFromDataNoCopy(result, YES);
   128 }
   129 
   130 
   131 NSData* MYCertificateSign(NSData *certificateTemplate, 
   132                           MYPrivateKey *privateKey, 
   133                           CSSM_ALGORITHMS signingAlgorithmID,
   134                           CSSM_CL_HANDLE cssmCLHandle) 
   135 {
   136 
   137     CAssert(certificateTemplate.length);
   138     CAssert(privateKey);
   139         
   140     NSData *signedCertificate = nil;
   141     CSSM_CC_HANDLE ccHandle = [privateKey _createSignatureContext: signingAlgorithmID];
   142     if (ccHandle) {
   143         CSSM_DATA rawCert = {certificateTemplate.length, (void*)certificateTemplate.bytes};
   144         CSSM_DATA signedResult = {};
   145         if (checkcssm(CSSM_CL_CertSign(cssmCLHandle, ccHandle, &rawCert, NULL, 0, &signedResult),
   146                       @"CSSM_CL_CertSign")) {
   147             signedCertificate = NSDataFromDataNoCopy(signedResult, YES);
   148             checkcssm(CSSM_DeleteContext(ccHandle), @"CSSM_DeleteContext");
   149         }
   150     }
   151     return signedCertificate;
   152 }
   153 
   154 
   155 MYCertificate* MYCertificateCreateSelfSigned(MYPrivateKey *privateKey,
   156                                              NSDictionary *attributes )
   157 {
   158     // Extract attributes:
   159     NSMutableDictionary *subject = [[attributes mutableCopy] autorelease];
   160 
   161     unsigned serialNumber = [[attributes objectForKey: @"Serial Number"] unsignedIntValue];
   162     [subject removeObjectForKey: @"Serial Number"];
   163 
   164     NSDate *validFrom = [attributes objectForKey: @"Valid From"];
   165     [subject removeObjectForKey: @"Valid From"];
   166     NSDate *validTo = [attributes objectForKey: @"Valid To"];
   167     [subject removeObjectForKey: @"Valid To"];
   168     
   169     if (!serialNumber)
   170         serialNumber = 1;
   171     if (!validFrom)
   172         validFrom = [NSCalendarDate date];
   173     if (!validTo)
   174         validTo = [validFrom addTimeInterval: 60*60*24*366];
   175     
   176     const CSSM_X509_NAME *subjectStruct = createNameList(subject);
   177 
   178     // Create the key-usage extensions for the cert:    
   179     UInt8 keyUsageBits[2] = {0x00,0xFC};
   180     // that's binary 111111000; see http://tools.ietf.org/html/rfc3280#section-4.2.1.3
   181     CSSM_X509_EXTENSION keyUsage = {
   182         CSSMOID_KeyUsage, 
   183         false,      // non-critical
   184         CSSM_X509_DATAFORMAT_PARSED,
   185         {.parsedValue = &keyUsageBits}
   186     };
   187     
   188     // See http://tools.ietf.org/html/rfc3280#section-4.2.1.13
   189     struct ExtendedUsageList {
   190         UInt32 count;
   191         const CSSM_OID *oids;
   192     };
   193     CSSM_OID usageOids[3] = {CSSMOID_ServerAuth, CSSMOID_ClientAuth, CSSMOID_ExtendedKeyUsageAny};
   194     struct ExtendedUsageList extUsageBits = {3, usageOids};
   195     CSSM_X509_EXTENSION extendedKeyUsage = {
   196         CSSMOID_ExtendedKeyUsage,
   197         false,      // non-critical
   198         CSSM_X509_DATAFORMAT_PARSED,
   199         {.parsedValue = &extUsageBits}
   200     };
   201     
   202     const CSSM_X509_EXTENSION* extensions[2] = {&keyUsage, &extendedKeyUsage};
   203     
   204     CSSM_X509_ALGORITHM_IDENTIFIER algorithmID = {.algorithm=CSSMOID_RSA};
   205     CSSM_CL_HANDLE cssmCLHandle = getCLHandle();
   206 
   207     // Now create the certificate request and sign it:
   208     NSData *template, *signedCertificate = nil;
   209     
   210     template = MYCertificateCreateTemplate(subjectStruct, subjectStruct, // issuer==subject (self-signed) 
   211                                            validFrom, validTo, 
   212                                            serialNumber, 
   213                                            extensions, 2,
   214                                            privateKey.publicKey, 
   215                                            &algorithmID, 
   216                                            cssmCLHandle);
   217     if (!template)
   218         return nil;
   219     
   220     signedCertificate = MYCertificateSign(template, 
   221                                           privateKey, 
   222                                           CSSM_ALGID_SHA1WithRSA, 
   223                                           cssmCLHandle);
   224     if (!signedCertificate)
   225         return nil;
   226     
   227     return [[[MYCertificate alloc] initWithCertificateData: signedCertificate 
   228                                                       type: CSSM_CERT_UNKNOWN 
   229                                                   encoding: CSSM_CERT_ENCODING_UNKNOWN] autorelease];
   230 }
   231 
   232 
   233 
   234 MYIdentity* MYIdentityCreateSelfSigned(MYPrivateKey *privateKey,
   235                                        NSDictionary *attributes )
   236 {
   237     MYCertificate *cert = MYCertificateCreateSelfSigned(privateKey, attributes);
   238     if (!cert)
   239         return nil;
   240     if (![privateKey.keychain addCertificate: cert])
   241         return nil;
   242     MYIdentity *identity = [[[MYIdentity alloc] initWithCertificateRef: cert.certificateRef] autorelease];
   243     CAssert(identity);
   244     return identity;
   245 }
   246 
   247 
   248 
   249 #pragma mark -
   250 #pragma mark HELPER FUNCTIONS:
   251 
   252 
   253 static void* mallocAutoreleased( size_t size ) {
   254     NSMutableData *data = [NSMutableData dataWithLength: size];
   255     return data.mutableBytes;
   256 }
   257 
   258 #define callocAutoreleasedArray(TYPE,N)  (TYPE*)mallocAutoreleased( sizeof(TYPE) * (N) )
   259 
   260 
   261 /*
   262     CSSM_X509_NAME:
   263         uint32 numberOfRDNs;
   264         CSSM_X509_RDN_PTR RelativeDistinguishedName:
   265             uint32 numberOfPairs;
   266             CSSM_X509_TYPE_VALUE_PAIR_PTR AttributeTypeAndValue:
   267                 CSSM_OID type;
   268                 CSSM_BER_TAG valueType; // The Tag to be used when this value is BER encoded 
   269                 CSSM_DATA value;
   270 */
   271 
   272 
   273 static CSSM_X509_NAME* createNameList( NSDictionary *name ) {
   274     static NSArray *sNameKeys;
   275     static CSSM_OID sNameOIDs[7];
   276     if (!sNameKeys) {
   277         sNameKeys = [$array(@"Common Name", @"Surname",  @"Description", @"Name", @"Given Name", 
   278                             @"Email Address", @"Unstructured Name") retain];
   279         sNameOIDs[0] = CSSMOID_CommonName;
   280         sNameOIDs[1] = CSSMOID_Surname;
   281         sNameOIDs[2] = CSSMOID_Description;
   282         sNameOIDs[3] = CSSMOID_Name;
   283         sNameOIDs[4] = CSSMOID_GivenName;
   284         sNameOIDs[5] = CSSMOID_EmailAddress;
   285         sNameOIDs[6] = CSSMOID_UnstructuredName;
   286     }
   287     
   288     unsigned n = name.count;
   289     CAssert(n>0);
   290     CSSM_X509_RDN *rdns = callocAutoreleasedArray(CSSM_X509_RDN, name.count);
   291     CSSM_X509_RDN *rdn = &rdns[0];
   292     CSSM_X509_TYPE_VALUE_PAIR *pairs = callocAutoreleasedArray(CSSM_X509_TYPE_VALUE_PAIR, n);
   293     CSSM_X509_TYPE_VALUE_PAIR *pair = &pairs[0];
   294     for (NSString *key in name) {
   295         NSString *value = [name objectForKey: key];
   296         unsigned index = [sNameKeys indexOfObject: key];
   297         CAssert(index!=NSNotFound, @"X509 name key '%@' not supported'", key);
   298         rdn->numberOfPairs = 1;
   299         rdn->AttributeTypeAndValue = pair;
   300         pair->type = sNameOIDs[index];
   301         pair->valueType = BER_TAG_PRINTABLE_STRING;
   302         pair->value.Data = (void*) value.UTF8String;
   303         pair->value.Length = strlen((char*)pair->value.Data);
   304         rdn++;
   305         pair++;
   306     }
   307     CSSM_X509_NAME *outName = callocAutoreleasedArray(CSSM_X509_NAME,1);
   308     outName->numberOfRDNs = n;
   309     outName->RelativeDistinguishedName = rdns;
   310     return outName;
   311 }
   312 
   313 
   314 #pragma mark -
   315 #pragma mark HELPER FUNCTIONS (from Keychain.framework)
   316 
   317 
   318 static CSSM_X509_TIME* timeForNSDate(NSDate *date) {
   319     CAssert(date);
   320     
   321     NSCalendarDate *dateGMT = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate: 
   322                                                             date.timeIntervalSinceReferenceDate];
   323     [dateGMT setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
   324     
   325     /* RFC 2549:
   326      
   327      4.1.2.5.2  GeneralizedTime
   328      
   329      The generalized time type, GeneralizedTime, is a standard ASN.1 type
   330      for variable precision representation of time.  Optionally, the
   331      GeneralizedTime field can include a representation of the time
   332      differential between local and Greenwich Mean Time.
   333      
   334      For the purposes of this profile, GeneralizedTime values MUST be
   335      expressed Greenwich Mean Time (Zulu) and MUST include seconds (i.e.,
   336      times are YYYYMMDDHHMMSSZ), even where the number of seconds is zero.
   337      GeneralizedTime values MUST NOT include fractional seconds. */
   338     
   339     CSSM_X509_TIME *result = mallocAutoreleased(sizeof(CSSM_X509_TIME));
   340     result->timeType = BER_TAG_GENERALIZED_TIME;
   341     result->time.Length = 15;
   342     result->time.Data = mallocAutoreleased(16);
   343     [[dateGMT descriptionWithCalendarFormat:@"%Y%m%d%H%M%SZ"] getCString: (char*)(result->time.Data)
   344                                                                maxLength: 16 
   345                                                                 encoding: NSASCIIStringEncoding];
   346     return result;
   347 }
   348 
   349 
   350 static NSData* NSDataFromDataNoCopy(CSSM_DATA data, BOOL freeWhenDone) {
   351     if (data.Data)
   352         return [NSData dataWithBytesNoCopy:data.Data length:data.Length freeWhenDone: freeWhenDone];
   353     else
   354         return nil;
   355 }
   356 
   357 
   358 static BOOL intToDER(uint32_t theInt, CSSM_DATA *data) {
   359     CAssert(data);
   360     data->Length = 0;
   361     
   362     if (theInt < 0x100) {
   363         data->Data = (uint8_t*)malloc(1);
   364         
   365         if (NULL != data->Data) {
   366             data->Length = 1;
   367 
   368             data->Data[0] = (unsigned char)(theInt);
   369         }
   370     } else if (theInt < 0x10000) {
   371         data->Data = (uint8_t*)malloc(2);
   372         
   373         if (NULL != data->Data) {
   374             data->Length = 2;
   375 
   376             data->Data[0] = (unsigned char)(theInt >> 8);
   377             data->Data[1] = (unsigned char)(theInt);
   378         }
   379     } else if (theInt < 0x1000000) {
   380         data->Data = (uint8_t*)malloc(3);
   381         
   382         if (NULL != data->Data) {
   383             data->Length = 3;
   384 
   385             data->Data[0] = (unsigned char)(theInt >> 16);
   386             data->Data[1] = (unsigned char)(theInt >> 8);
   387             data->Data[2] = (unsigned char)(theInt);
   388         }
   389     } else {
   390         data->Data = (uint8_t*)malloc(4);
   391         
   392         if (NULL != data->Data) {
   393             data->Length = 4;
   394 
   395             data->Data[0] = (unsigned char)(theInt >> 24);
   396             data->Data[1] = (unsigned char)(theInt >> 16);
   397             data->Data[2] = (unsigned char)(theInt >> 8);
   398             data->Data[3] = (unsigned char)(theInt);
   399         }
   400     }
   401     
   402     return (NULL != data->Data);
   403 }
   404 
   405 
   406 
   407 #pragma mark -
   408 #pragma mark HELPER FUNCTIONS (from Apple's source code):
   409 
   410 
   411 // From Apple's cuCdsaUtils.cpp, in libsecurity_cdsa_utils:
   412 
   413 
   414 /*
   415  * Standard app-level memory functions required by CDSA.
   416  */
   417 static void * cuAppMalloc (CSSM_SIZE size, void *allocRef)             {return( malloc(size) );}
   418 static void   cuAppFree (void *mem_ptr, void *allocRef)                {free(mem_ptr);}
   419 static void * cuAppRealloc (void *ptr, CSSM_SIZE size, void *allocRef) {return( realloc( ptr, size ) );}
   420 static void * cuAppCalloc (uint32 num, CSSM_SIZE size, void *allocRef) {return( calloc( num, size ) );}
   421 
   422 static CSSM_VERSION vers = {2, 0};
   423 static CSSM_API_MEMORY_FUNCS memFuncs = {
   424     cuAppMalloc,
   425     cuAppFree,
   426     cuAppRealloc,
   427     cuAppCalloc,
   428     NULL
   429 };
   430 
   431 static CSSM_CL_HANDLE cuClStartup()
   432 {
   433 	CSSM_CL_HANDLE clHand;
   434 	
   435 	if (!checkcssm(CSSM_ModuleLoad(&gGuidAppleX509CL,
   436                                    CSSM_KEY_HIERARCHY_NONE,
   437                                    NULL,			// eventHandler
   438                                    NULL), @"CSSM_ModuleLoad"))
   439         return 0;
   440     if (!checkcssm(CSSM_ModuleAttach(&gGuidAppleX509CL,
   441                                      &vers,
   442                                      &memFuncs,				// memFuncs
   443                                      0,						// SubserviceID
   444                                      CSSM_SERVICE_CL,		// SubserviceFlags - Where is this used?
   445                                      0,						// AttachFlags
   446                                      CSSM_KEY_HIERARCHY_NONE,
   447                                      NULL,					// FunctionTable
   448                                      0,						// NumFuncTable
   449                                      NULL,					// reserved
   450                                      &clHand), @"CSSM_ModuleAttach"))
   451         return 0;
   452     return clHand;
   453 }
   454         
   455 CSSM_CL_HANDLE getCLHandle() {
   456     static CSSM_CL_HANDLE sCLHandle = 0;
   457     if (!sCLHandle)
   458         sCLHandle = cuClStartup();
   459     return sCLHandle;
   460 }
   461 
   462 
   463 #pragma mark -
   464 #pragma mark TEST CASE:
   465 
   466 
   467 TestCase(MYCertGen) {
   468     CSSM_CL_HANDLE cl = getCLHandle();
   469     Log(@"CSSM_CL_HANDLE = %p", cl);
   470     CAssert(cl);
   471     
   472     Log(@"Generating a key pair...");
   473     MYPrivateKey *privateKey = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 2048];
   474     Log(@"Key-pair = { %@, %@ }", privateKey, privateKey.publicKey);
   475     
   476     Log(@"...creating cert...");
   477     
   478     MYCertificate *cert = MYCertificateCreateSelfSigned(privateKey,
   479                                                       $dict(
   480                                                           {@"Common Name", @"waldo"},
   481                                                           {@"Given Name", @"Waldo"},
   482                                                           {@"Surname", @"Widdershins"},
   483                                                           {@"Email Address", @"waldo@example.com"},
   484                                                           {@"Description", @"Just a fictitious person"},
   485                                                       ));
   486     Log(@"Cert = %@", cert);
   487     CAssert(cert);
   488     [cert.certificateData writeToFile: @"/tmp/MYCryptoTest.cer" atomically: NO];
   489     
   490     Log(@"Cert name = %@", cert.commonName);
   491     Log(@"Cert email = %@", cert.emailAddresses);
   492     Log(@"Cert pub key = %@", cert.publicKey);
   493     CAssertEqual(cert.commonName, @"waldo");
   494     CAssertEqual(cert.emailAddresses, $array(@"waldo@example.com"));
   495     CAssertEqual(cert.publicKey.publicKeyDigest, privateKey.publicKeyDigest);
   496     
   497     CAssert([[MYKeychain defaultKeychain] addCertificate: cert]);
   498     
   499     CAssert([cert setUserTrust: kSecTrustResultProceed]);
   500 }
   501 
   502 
   503 
   504 /*
   505  Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   506  
   507  Redistribution and use in source and binary forms, with or without modification, are permitted
   508  provided that the following conditions are met:
   509  
   510  * Redistributions of source code must retain the above copyright notice, this list of conditions
   511  and the following disclaimer.
   512  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   513  and the following disclaimer in the documentation and/or other materials provided with the
   514  distribution.
   515  
   516  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   517  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   518  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   519  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   520  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   521   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   522  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   523  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   524  */