MYCertificateInfo.m
author Jens Alfke <jens@mooseyard.com>
Sat Jun 06 15:01:28 2009 -0700 (2009-06-06)
changeset 21 2c300b15b381
parent 20 MYParsedCertificate.m@df9da0f6b358
child 23 39fec79de6e8
permissions -rw-r--r--
* Created class MYCertificateRequest, factored out of MYCertificateInfo.
* Added method to create a MYIdentity directly from a MYCertificateRequest.
* Added raw modulus+exponent accessor and initializer for MYPublicKey.
* Removed obsolete MYCertGen code, and the MYPrivateKey identity-creation method that used it.
jens@17
     1
//
jens@21
     2
//  MYCertificateInfo.m
jens@17
     3
//  MYCrypto
jens@17
     4
//
jens@17
     5
//  Created by Jens Alfke on 6/2/09.
jens@17
     6
//  Copyright 2009 Jens Alfke. All rights reserved.
jens@17
     7
//
jens@17
     8
jens@17
     9
// References:
jens@20
    10
// <http://www.columbia.edu/~ariel/ssleay/layman.html> "Layman's Guide To ASN.1/BER/DER"
jens@20
    11
// <http://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt> "X.509 Style Guide"
jens@20
    12
// <http://en.wikipedia.org/wiki/X.509> Wikipedia article on X.509
jens@17
    13
jens@17
    14
jens@21
    15
#import "MYCertificateInfo.h"
jens@21
    16
#import "MYCrypto.h"
jens@17
    17
#import "MYASN1Object.h"
jens@17
    18
#import "MYOID.h"
jens@17
    19
#import "MYBERParser.h"
jens@17
    20
#import "MYDEREncoder.h"
jens@17
    21
#import "MYErrorUtils.h"
jens@17
    22
jens@17
    23
jens@19
    24
#define kDefaultExpirationTime (60.0 * 60.0 * 24.0 * 365.0)
jens@19
    25
jens@19
    26
jens@17
    27
static id $atIf(NSArray *array, NSUInteger index) {
jens@17
    28
    return index < array.count ?[array objectAtIndex: index] :nil;
jens@17
    29
}
jens@17
    30
jens@17
    31
jens@20
    32
@interface MYCertificateName ()
jens@20
    33
- (id) _initWithComponents: (NSArray*)components;
jens@20
    34
@end
jens@20
    35
jens@21
    36
@interface MYCertificateInfo ()
jens@21
    37
@property (retain) NSArray *_root;
jens@21
    38
@end
jens@21
    39
jens@20
    40
jens@20
    41
#pragma mark -
jens@21
    42
@implementation MYCertificateInfo
jens@17
    43
jens@17
    44
jens@19
    45
static MYOID *kRSAAlgorithmID, *kRSAWithSHA1AlgorithmID, *kCommonNameOID,
jens@19
    46
            *kGivenNameOID, *kSurnameOID, *kDescriptionOID, *kEmailOID;
jens@17
    47
jens@17
    48
jens@17
    49
+ (void) initialize {
jens@19
    50
    if (!kEmailOID) {
jens@19
    51
        kRSAAlgorithmID = [[MYOID alloc] initWithComponents: (UInt32[]){1, 2, 840, 113549, 1, 1, 1,}
jens@19
    52
                                                      count: 7];
jens@19
    53
        kRSAWithSHA1AlgorithmID = [[MYOID alloc] initWithComponents: (UInt32[]){1, 2, 840, 113549, 1, 1, 5}
jens@19
    54
                                                              count: 7];
jens@19
    55
        kCommonNameOID = [[MYOID alloc] initWithComponents: (UInt32[]){2, 5, 4, 3}
jens@19
    56
                                                     count: 4];
jens@19
    57
        kGivenNameOID = [[MYOID alloc] initWithComponents: (UInt32[]){2, 5, 4, 42}
jens@19
    58
                                                    count: 4];
jens@19
    59
        kSurnameOID = [[MYOID alloc] initWithComponents: (UInt32[]){2, 5, 4, 4}
jens@19
    60
                                                  count: 4];
jens@19
    61
        kDescriptionOID = [[MYOID alloc] initWithComponents: (UInt32[]){2, 5, 4, 13}
jens@19
    62
                                                count: 7];
jens@19
    63
        kEmailOID = [[MYOID alloc] initWithComponents: (UInt32[]){1, 2, 840, 113549, 1, 9, 1}
jens@19
    64
                                                count: 7];
jens@17
    65
    }
jens@21
    66
}
jens@21
    67
jens@21
    68
jens@21
    69
- (id) initWithRoot: (NSArray*)root
jens@21
    70
{
jens@21
    71
    self = [super init];
jens@21
    72
    if (self != nil) {
jens@21
    73
        _root = [root retain];
jens@21
    74
    }
jens@21
    75
    return self;
jens@17
    76
}
jens@17
    77
jens@17
    78
+ (NSString*) validate: (id)root {
jens@17
    79
    NSArray *top = $castIf(NSArray,root);
jens@17
    80
    if (top.count < 3)
jens@17
    81
        return @"Too few top-level components";
jens@17
    82
    NSArray *info = $castIf(NSArray, [top objectAtIndex: 0]);
jens@17
    83
    if (info.count < 7)
jens@17
    84
        return @"Too few identity components";
jens@17
    85
    MYASN1Object *version = $castIf(MYASN1Object, [info objectAtIndex: 0]);
jens@17
    86
    if (!version || version.tag != 0)
jens@17
    87
        return @"Missing or invalid version";
jens@17
    88
    NSArray *versionComps = $castIf(NSArray, version.components);
jens@17
    89
    if (!versionComps || versionComps.count != 1)
jens@17
    90
        return @"Invalid version";
jens@17
    91
    NSNumber *versionNum = $castIf(NSNumber, [versionComps objectAtIndex: 0]);
jens@17
    92
    if (!versionNum || versionNum.intValue < 0 || versionNum.intValue > 2)
jens@17
    93
        return @"Unrecognized version number";
jens@17
    94
    return nil;
jens@17
    95
}
jens@17
    96
jens@17
    97
jens@17
    98
- (id) initWithCertificateData: (NSData*)data error: (NSError**)outError;
jens@17
    99
{
jens@21
   100
    if (outError) *outError = nil;
jens@21
   101
    id root = MYBERParse(data,outError);
jens@21
   102
    NSString *errorMsg = [[self class] validate: root];
jens@21
   103
    if (errorMsg) {
jens@21
   104
        if (outError && !*outError)
jens@21
   105
            *outError = MYError(2, MYASN1ErrorDomain, @"Invalid certificate: %@", errorMsg);
jens@21
   106
        [self release];
jens@21
   107
        return nil;
jens@17
   108
    }
jens@21
   109
jens@21
   110
    return [self initWithRoot: root];
jens@17
   111
}
jens@17
   112
jens@17
   113
- (void) dealloc
jens@17
   114
{
jens@17
   115
    [_root release];
jens@17
   116
    [super dealloc];
jens@17
   117
}
jens@17
   118
jens@21
   119
- (BOOL) isEqual: (id)object {
jens@21
   120
    return [object isKindOfClass: [MYCertificateInfo class]]
jens@21
   121
        && [_root isEqual: ((MYCertificateInfo*)object)->_root];
jens@21
   122
}
jens@17
   123
jens@19
   124
- (NSArray*) _info       {return $castIf(NSArray,$atIf(_root,0));}
jens@17
   125
jens@19
   126
- (NSArray*) _validDates {return $castIf(NSArray, [self._info objectAtIndex: 4]);}
jens@17
   127
jens@21
   128
@synthesize _root;
jens@19
   129
jens@19
   130
jens@19
   131
- (NSDate*) validFrom       {return $castIf(NSDate, $atIf(self._validDates, 0));}
jens@19
   132
- (NSDate*) validTo         {return $castIf(NSDate, $atIf(self._validDates, 1));}
jens@20
   133
jens@20
   134
- (MYCertificateName*) subject {
jens@20
   135
    return [[[MYCertificateName alloc] _initWithComponents: [self._info objectAtIndex: 5]] autorelease];
jens@20
   136
}
jens@20
   137
jens@20
   138
- (MYCertificateName*) issuer {
jens@20
   139
    return [[[MYCertificateName alloc] _initWithComponents: [self._info objectAtIndex: 3]] autorelease];
jens@20
   140
}
jens@19
   141
jens@19
   142
- (BOOL) isSigned           {return [_root count] >= 3;}
jens@19
   143
jens@19
   144
- (BOOL) isRoot {
jens@19
   145
    id issuer = $atIf(self._info,3);
jens@19
   146
    return $equal(issuer, $atIf(self._info,5)) || $equal(issuer, $array());
jens@19
   147
}
jens@19
   148
jens@19
   149
jens@17
   150
- (MYPublicKey*) subjectPublicKey {
jens@19
   151
    NSArray *keyInfo = $cast(NSArray, $atIf(self._info, 6));
jens@17
   152
    MYOID *keyAlgorithmID = $castIf(MYOID, $atIf($castIf(NSArray,$atIf(keyInfo,0)), 0));
jens@17
   153
    if (!$equal(keyAlgorithmID, kRSAAlgorithmID))
jens@17
   154
        return nil;
jens@17
   155
    MYBitString *keyData = $cast(MYBitString, $atIf(keyInfo, 1));
jens@17
   156
    if (!keyData) return nil;
jens@17
   157
    return [[[MYPublicKey alloc] initWithKeyData: keyData.bits] autorelease];
jens@17
   158
}
jens@17
   159
jens@21
   160
@end
jens@17
   161
jens@17
   162
jens@17
   163
jens@17
   164
jens@19
   165
#pragma mark -
jens@21
   166
@implementation MYCertificateRequest
jens@19
   167
jens@21
   168
- (id) initWithPublicKey: (MYPublicKey*)publicKey {
jens@21
   169
    Assert(publicKey);
jens@21
   170
    id empty = [NSNull null];
jens@21
   171
    id version = [[MYASN1Object alloc] initWithTag: 0 ofClass: 2 components: $array($object(0))];
jens@21
   172
    NSArray *root = $array( $marray(version,
jens@21
   173
                                    empty,       // serial #
jens@21
   174
                                    $array(kRSAAlgorithmID),
jens@21
   175
                                    $marray(),
jens@21
   176
                                    $marray(empty, empty),
jens@21
   177
                                    $marray(),
jens@21
   178
                                    $array( $array(kRSAAlgorithmID, empty),
jens@21
   179
                                           [MYBitString bitStringWithData: publicKey.keyData] ) ) );
jens@21
   180
    self = [super initWithRoot: root];
jens@21
   181
    [version release];
jens@19
   182
    return self;
jens@19
   183
}
jens@19
   184
jens@21
   185
- (NSDate*) validFrom       {return [super validFrom];}
jens@21
   186
- (NSDate*) validTo         {return [super validTo];}
jens@19
   187
jens@19
   188
- (void) setValidFrom: (NSDate*)validFrom {
jens@19
   189
    [(NSMutableArray*)self._validDates replaceObjectAtIndex: 0 withObject: validFrom];
jens@19
   190
}
jens@19
   191
jens@19
   192
- (void) setValidTo: (NSDate*)validTo {
jens@19
   193
    [(NSMutableArray*)self._validDates replaceObjectAtIndex: 1 withObject: validTo];
jens@19
   194
}
jens@19
   195
jens@19
   196
jens@21
   197
- (void) fillInValues {
jens@19
   198
    NSMutableArray *info = (NSMutableArray*)self._info;
jens@19
   199
    // Set serial number if there isn't one yet:
jens@19
   200
    if (!$castIf(NSNumber, [info objectAtIndex: 1])) {
jens@19
   201
        UInt64 serial = floor(CFAbsoluteTimeGetCurrent() * 1000);
jens@19
   202
        [info replaceObjectAtIndex: 1 withObject: $object(serial)];
jens@19
   203
    }
jens@19
   204
    
jens@19
   205
    // Set up valid date range if there isn't one yet:
jens@19
   206
    NSDate *validFrom = self.validFrom;
jens@19
   207
    if (!validFrom)
jens@19
   208
        validFrom = self.validFrom = [NSDate date];
jens@19
   209
    NSDate *validTo = self.validTo;
jens@19
   210
    if (!validTo)
jens@19
   211
        self.validTo = [validFrom addTimeInterval: kDefaultExpirationTime]; 
jens@21
   212
}
jens@21
   213
jens@21
   214
jens@21
   215
- (NSData*) requestData: (NSError**)outError {
jens@21
   216
    [self fillInValues];
jens@21
   217
    return [MYDEREncoder encodeRootObject: self._info error: outError];
jens@21
   218
}
jens@21
   219
jens@21
   220
jens@21
   221
- (NSData*) selfSignWithPrivateKey: (MYPrivateKey*)privateKey 
jens@21
   222
                             error: (NSError**)outError 
jens@21
   223
{
jens@21
   224
    AssertEqual(privateKey.publicKey, _publicKey);  // Keys must form a pair
jens@19
   225
    
jens@21
   226
    // Copy subject to issuer:
jens@21
   227
    NSMutableArray *info = (NSMutableArray*)self._info;
jens@21
   228
    [info replaceObjectAtIndex: 3 withObject: [info objectAtIndex: 5]];
jens@21
   229
    
jens@21
   230
    // Sign the request:
jens@21
   231
    NSData *dataToSign = [self requestData: outError];
jens@19
   232
    if (!dataToSign)
jens@21
   233
        return nil;
jens@21
   234
    MYBitString *signature = [MYBitString bitStringWithData: [privateKey signData: dataToSign]];
jens@19
   235
    
jens@21
   236
    // Generate and encode the certificate:
jens@21
   237
    NSArray *root = $array(info, 
jens@21
   238
                           $array(kRSAWithSHA1AlgorithmID, [NSNull null]),
jens@21
   239
                           signature);
jens@21
   240
    return [MYDEREncoder encodeRootObject: root error: outError];
jens@21
   241
}
jens@21
   242
jens@21
   243
jens@21
   244
- (MYIdentity*) createSelfSignedIdentityWithPrivateKey: (MYPrivateKey*)privateKey
jens@21
   245
                                                 error: (NSError**)outError
jens@21
   246
{
jens@21
   247
    NSData *certData = [self selfSignWithPrivateKey: privateKey error: outError];
jens@21
   248
    if (!certData)
jens@21
   249
        return nil;
jens@21
   250
    MYCertificate *cert = [privateKey.keychain importCertificate: certData];
jens@21
   251
    Assert(cert!=nil);
jens@21
   252
    MYIdentity *identity = cert.identity;
jens@21
   253
    Assert(identity!=nil);
jens@21
   254
    return identity;
jens@19
   255
}
jens@19
   256
jens@19
   257
jens@17
   258
@end
jens@17
   259
jens@17
   260
jens@17
   261
jens@20
   262
#pragma mark -
jens@20
   263
@implementation MYCertificateName
jens@20
   264
jens@20
   265
- (id) _initWithComponents: (NSArray*)components
jens@20
   266
{
jens@20
   267
    self = [super init];
jens@20
   268
    if (self != nil) {
jens@20
   269
        _components = [components retain];
jens@20
   270
    }
jens@20
   271
    return self;
jens@20
   272
}
jens@20
   273
jens@20
   274
- (void) dealloc
jens@20
   275
{
jens@20
   276
    [_components release];
jens@20
   277
    [super dealloc];
jens@20
   278
}
jens@20
   279
jens@20
   280
- (BOOL) isEqual: (id)object {
jens@20
   281
    return [object isKindOfClass: [MYCertificateName class]]
jens@20
   282
        && [_components isEqual: ((MYCertificateName*)object)->_components];
jens@20
   283
}
jens@20
   284
jens@20
   285
- (NSArray*) _pairForOID: (MYOID*)oid {
jens@20
   286
    for (id nameEntry in _components) {
jens@20
   287
        for (id pair in $castIf(NSSet,nameEntry)) {
jens@20
   288
            if ([pair isKindOfClass: [NSArray class]] && [pair count] == 2) {
jens@20
   289
                if ($equal(oid, [pair objectAtIndex: 0]))
jens@20
   290
                    return pair;
jens@20
   291
            }
jens@20
   292
        }
jens@20
   293
    }
jens@20
   294
    return nil;
jens@20
   295
}
jens@20
   296
jens@20
   297
- (NSString*) stringForOID: (MYOID*)oid {
jens@20
   298
    return [[self _pairForOID: oid] objectAtIndex: 1];
jens@20
   299
}
jens@20
   300
jens@20
   301
- (void) setString: (NSString*)value forOID: (MYOID*)oid {
jens@20
   302
    NSMutableArray *pair = (NSMutableArray*) [self _pairForOID: oid];
jens@20
   303
    if (pair)
jens@20
   304
        [pair replaceObjectAtIndex: 1 withObject: value];
jens@20
   305
    else
jens@20
   306
        [(NSMutableArray*)_components addObject: [NSSet setWithObject: $marray(oid,value)]];
jens@20
   307
}
jens@20
   308
jens@20
   309
- (NSString*) commonName    {return [self stringForOID: kCommonNameOID];}
jens@20
   310
- (NSString*) givenName     {return [self stringForOID: kGivenNameOID];}
jens@20
   311
- (NSString*) surname       {return [self stringForOID: kSurnameOID];}
jens@20
   312
- (NSString*) nameDescription {return [self stringForOID: kDescriptionOID];}
jens@20
   313
- (NSString*) emailAddress  {return [self stringForOID: kEmailOID];}
jens@20
   314
jens@20
   315
- (void) setCommonName: (NSString*)commonName   {[self setString: commonName forOID: kCommonNameOID];}
jens@20
   316
- (void) setGivenName: (NSString*)givenName     {[self setString: givenName forOID: kGivenNameOID];}
jens@20
   317
- (void) setSurname: (NSString*)surname         {[self setString: surname forOID: kSurnameOID];}
jens@20
   318
- (void) setNameDescription: (NSString*)desc    {[self setString: desc forOID: kDescriptionOID];}
jens@20
   319
- (void) setEmailAddress: (NSString*)email      {[self setString: email forOID: kEmailOID];}
jens@20
   320
jens@20
   321
jens@20
   322
@end
jens@20
   323
jens@20
   324
jens@20
   325
jens@20
   326
#pragma mark -
jens@20
   327
#pragma mark TEST CASES:
jens@17
   328
jens@19
   329
#if DEBUG
jens@19
   330
jens@19
   331
jens@21
   332
static MYCertificateInfo* testCert(NSString *filename, BOOL selfSigned) {
jens@21
   333
    Log(@"--- Creating MYCertificateInfo from %@", filename);
jens@19
   334
    NSData *certData = [NSData dataWithContentsOfFile: filename];
jens@19
   335
    //Log(@"Cert Data =\n%@", certData);
jens@19
   336
    NSError *error = nil;
jens@21
   337
    MYCertificateInfo *pcert = [[MYCertificateInfo alloc] initWithCertificateData: certData 
jens@21
   338
                                                                            error: &error];
jens@19
   339
    CAssertNil(error);
jens@19
   340
    CAssert(pcert != nil);
jens@19
   341
    
jens@19
   342
    CAssertEq(pcert.isRoot, selfSigned);
jens@21
   343
        
jens@19
   344
    Log(@"Subject Public Key = %@", pcert.subjectPublicKey);
jens@19
   345
    CAssert(pcert.subjectPublicKey);
jens@20
   346
    MYCertificateName *subject = pcert.subject;
jens@20
   347
    Log(@"Common Name = %@", subject.commonName);
jens@20
   348
    Log(@"Given Name  = %@", subject.givenName);
jens@20
   349
    Log(@"Surname     = %@", subject.surname);
jens@20
   350
    Log(@"Desc        = %@", subject.nameDescription);
jens@20
   351
    Log(@"Email       = %@", subject.emailAddress);
jens@21
   352
    CAssert(subject.commonName);
jens@21
   353
    
jens@21
   354
    // Now go through MYCertificate:
jens@21
   355
    MYCertificate *cert = [[MYCertificate alloc] initWithCertificateData: certData];
jens@21
   356
    CAssert(cert);
jens@21
   357
    CAssertEqual(cert.info, pcert);
jens@21
   358
    
jens@19
   359
    return pcert;
jens@19
   360
}
jens@19
   361
jens@19
   362
jens@17
   363
TestCase(ParsedCert) {
jens@17
   364
    testCert(@"../../Tests/selfsigned.cer", YES);
jens@17
   365
    testCert(@"../../Tests/iphonedev.cer", NO);
jens@19
   366
}
jens@19
   367
jens@19
   368
jens@21
   369
#import "MYCrypto_Private.h"
jens@19
   370
jens@19
   371
TestCase(CreateCert) {
jens@19
   372
    MYPrivateKey *privateKey = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 512];
jens@21
   373
    CAssert(privateKey);
jens@21
   374
    MYIdentity *identity = nil;
jens@21
   375
    @try{
jens@21
   376
        MYCertificateRequest *pcert = [[MYCertificateRequest alloc] initWithPublicKey: privateKey.publicKey];
jens@21
   377
        MYCertificateName *subject = pcert.subject;
jens@21
   378
        subject.commonName = @"testcase";
jens@21
   379
        subject.givenName = @"Test";
jens@21
   380
        subject.surname = @"Case";
jens@21
   381
        subject.nameDescription = @"Just a test certificate created by MYCrypto";
jens@21
   382
        subject.emailAddress = @"testcase@example.com";
jens@19
   383
jens@21
   384
        subject = pcert.subject;
jens@21
   385
        CAssertEqual(subject.commonName, @"testcase");
jens@21
   386
        CAssertEqual(subject.givenName, @"Test");
jens@21
   387
        CAssertEqual(subject.surname, @"Case");
jens@21
   388
        CAssertEqual(subject.nameDescription, @"Just a test certificate created by MYCrypto");
jens@21
   389
        CAssertEqual(subject.emailAddress, @"testcase@example.com");
jens@21
   390
        
jens@21
   391
        Log(@"Signing...");
jens@21
   392
        NSError *error;
jens@21
   393
        NSData *certData = [pcert selfSignWithPrivateKey: privateKey error: &error];
jens@21
   394
        Log(@"Generated cert = \n%@", certData);
jens@21
   395
        CAssert(certData);
jens@21
   396
        CAssertNil(error);
jens@21
   397
        CAssert(certData);
jens@21
   398
        [certData writeToFile: @"../../Tests/generated.cer" atomically: YES];
jens@21
   399
        MYCertificateInfo *pcert2 = testCert(@"../../Tests/generated.cer", YES);
jens@21
   400
        
jens@21
   401
        Log(@"Verifying Info...");
jens@21
   402
        MYCertificateName *subject2 = pcert2.subject;
jens@21
   403
        CAssertEqual(subject2,subject);
jens@21
   404
        CAssertEqual(subject2.commonName, @"testcase");
jens@21
   405
        CAssertEqual(subject2.givenName, @"Test");
jens@21
   406
        CAssertEqual(subject2.surname, @"Case");
jens@21
   407
        CAssertEqual(subject2.nameDescription, @"Just a test certificate created by MYCrypto");
jens@21
   408
        CAssertEqual(subject2.emailAddress, @"testcase@example.com");
jens@21
   409
        
jens@21
   410
        Log(@"Verifying Signature...");
jens@21
   411
        MYCertificate *cert = [[MYCertificate alloc] initWithCertificateData: certData];
jens@21
   412
        Log(@"Loaded %@", cert);
jens@21
   413
        CAssert(cert);
jens@21
   414
        MYPublicKey *certKey = cert.publicKey;
jens@21
   415
        Log(@"Its public key = %@", certKey);
jens@21
   416
        CAssertEqual(certKey.keyData, privateKey.publicKey.keyData);
jens@21
   417
        
jens@21
   418
        Log(@"Creating Identity...");
jens@21
   419
        identity = [pcert createSelfSignedIdentityWithPrivateKey: privateKey error: &error];
jens@21
   420
        Log(@"Identity = %@", identity);
jens@21
   421
        CAssert(identity);
jens@21
   422
        CAssertNil(error);
jens@21
   423
        CAssertEqual(identity.keychain, [MYKeychain defaultKeychain]);
jens@21
   424
        CAssertEqual(identity.privateKey, privateKey);
jens@21
   425
        CAssert([identity isEqualToCertificate: cert]);
jens@21
   426
        
jens@21
   427
        [pcert release];
jens@21
   428
        
jens@21
   429
    } @finally {
jens@21
   430
        [privateKey removeFromKeychain];
jens@21
   431
        [identity removeFromKeychain];
jens@21
   432
    }
jens@19
   433
}
jens@19
   434
jens@19
   435
#endif
jens@19
   436
jens@17
   437
jens@21
   438
/*
jens@21
   439
 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
jens@17
   440
 
jens@21
   441
 Redistribution and use in source and binary forms, with or without modification, are permitted
jens@21
   442
 provided that the following conditions are met:
jens@21
   443
 
jens@21
   444
 * Redistributions of source code must retain the above copyright notice, this list of conditions
jens@21
   445
 and the following disclaimer.
jens@21
   446
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
jens@21
   447
 and the following disclaimer in the documentation and/or other materials provided with the
jens@21
   448
 distribution.
jens@21
   449
 
jens@21
   450
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
jens@21
   451
 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
jens@21
   452
 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
jens@21
   453
 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
jens@21
   454
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
jens@21
   455
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
jens@21
   456
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
jens@21
   457
 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
jens@21
   458
 */