| 1 | // |
|---|
| 2 | // SUDSAVerifier.m |
|---|
| 3 | // Sparkle |
|---|
| 4 | // |
|---|
| 5 | // Created by Andy Matuschak on 3/16/06. |
|---|
| 6 | // Copyright 2006 Andy Matuschak. All rights reserved. |
|---|
| 7 | // |
|---|
| 8 | |
|---|
| 9 | // DSA stuff adapted from code provided by Allan Odgaard. Thanks, Allan! |
|---|
| 10 | |
|---|
| 11 | #import "SUDSAVerifier.h" |
|---|
| 12 | |
|---|
| 13 | #import <stdio.h> |
|---|
| 14 | #import <openssl/evp.h> |
|---|
| 15 | #import <openssl/bio.h> |
|---|
| 16 | #import <openssl/pem.h> |
|---|
| 17 | #import <openssl/rsa.h> |
|---|
| 18 | #import <openssl/sha.h> |
|---|
| 19 | |
|---|
| 20 | long b64decode(unsigned char* str) |
|---|
| 21 | { |
|---|
| 22 | unsigned char *cur, *start; |
|---|
| 23 | int d, dlast, phase; |
|---|
| 24 | unsigned char c; |
|---|
| 25 | static int table[256] = { |
|---|
| 26 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */ |
|---|
| 27 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */ |
|---|
| 28 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */ |
|---|
| 29 | 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */ |
|---|
| 30 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */ |
|---|
| 31 | 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */ |
|---|
| 32 | -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */ |
|---|
| 33 | 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */ |
|---|
| 34 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */ |
|---|
| 35 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */ |
|---|
| 36 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */ |
|---|
| 37 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */ |
|---|
| 38 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */ |
|---|
| 39 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */ |
|---|
| 40 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */ |
|---|
| 41 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */ |
|---|
| 42 | }; |
|---|
| 43 | |
|---|
| 44 | d = dlast = phase = 0; |
|---|
| 45 | start = str; |
|---|
| 46 | for (cur = str; *cur != '\0'; ++cur ) |
|---|
| 47 | { |
|---|
| 48 | if(*cur == '\n' || *cur == '\r'){phase = dlast = 0; continue;} |
|---|
| 49 | d = table[(int)*cur]; |
|---|
| 50 | if(d != -1) |
|---|
| 51 | { |
|---|
| 52 | switch(phase) |
|---|
| 53 | { |
|---|
| 54 | case 0: |
|---|
| 55 | ++phase; |
|---|
| 56 | break; |
|---|
| 57 | case 1: |
|---|
| 58 | c = ((dlast << 2) | ((d & 0x30) >> 4)); |
|---|
| 59 | *str++ = c; |
|---|
| 60 | ++phase; |
|---|
| 61 | break; |
|---|
| 62 | case 2: |
|---|
| 63 | c = (((dlast & 0xf) << 4) | ((d & 0x3c) >> 2)); |
|---|
| 64 | *str++ = c; |
|---|
| 65 | ++phase; |
|---|
| 66 | break; |
|---|
| 67 | case 3: |
|---|
| 68 | c = (((dlast & 0x03 ) << 6) | d); |
|---|
| 69 | *str++ = c; |
|---|
| 70 | phase = 0; |
|---|
| 71 | break; |
|---|
| 72 | } |
|---|
| 73 | dlast = d; |
|---|
| 74 | } |
|---|
| 75 | } |
|---|
| 76 | *str = '\0'; |
|---|
| 77 | return str - start; |
|---|
| 78 | } |
|---|
| 79 | |
|---|
| 80 | EVP_PKEY* load_dsa_key(char *key) |
|---|
| 81 | { |
|---|
| 82 | EVP_PKEY* pkey = NULL; |
|---|
| 83 | BIO *bio; |
|---|
| 84 | if((bio = BIO_new_mem_buf(key, (int)strlen(key)))) |
|---|
| 85 | { |
|---|
| 86 | DSA* dsa_key = 0; |
|---|
| 87 | if(PEM_read_bio_DSA_PUBKEY(bio, &dsa_key, NULL, NULL)) |
|---|
| 88 | { |
|---|
| 89 | if((pkey = EVP_PKEY_new())) |
|---|
| 90 | { |
|---|
| 91 | if(EVP_PKEY_assign_DSA(pkey, dsa_key) != 1) |
|---|
| 92 | { |
|---|
| 93 | DSA_free(dsa_key); |
|---|
| 94 | EVP_PKEY_free(pkey); |
|---|
| 95 | pkey = NULL; |
|---|
| 96 | } |
|---|
| 97 | } |
|---|
| 98 | } |
|---|
| 99 | BIO_free(bio); |
|---|
| 100 | } |
|---|
| 101 | return pkey; |
|---|
| 102 | } |
|---|
| 103 | |
|---|
| 104 | @implementation SUDSAVerifier |
|---|
| 105 | |
|---|
| 106 | + (BOOL)validatePath:(NSString *)path withEncodedDSASignature:(NSString *)encodedSignature withPublicDSAKey:(NSString *)pkeyString |
|---|
| 107 | { |
|---|
| 108 | BOOL result = NO; |
|---|
| 109 | if (!encodedSignature) { return NO; } |
|---|
| 110 | if (!pkeyString) { return NO; } |
|---|
| 111 | |
|---|
| 112 | // Remove whitespace around each line of the key. |
|---|
| 113 | NSMutableArray *pkeyTrimmedLines = [NSMutableArray array]; |
|---|
| 114 | NSEnumerator *pkeyLinesEnumerator = [[pkeyString componentsSeparatedByString:@"\n"] objectEnumerator]; |
|---|
| 115 | NSString *pkeyLine; |
|---|
| 116 | while ((pkeyLine = [pkeyLinesEnumerator nextObject]) != nil) |
|---|
| 117 | { |
|---|
| 118 | [pkeyTrimmedLines addObject:[pkeyLine stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]]; |
|---|
| 119 | } |
|---|
| 120 | pkeyString = [pkeyTrimmedLines componentsJoinedByString:@"\n"]; // Put them back together. |
|---|
| 121 | |
|---|
| 122 | EVP_PKEY* pkey = NULL; |
|---|
| 123 | pkey = load_dsa_key((char *)[pkeyString UTF8String]); |
|---|
| 124 | if (!pkey) { return NO; } |
|---|
| 125 | |
|---|
| 126 | // Now, the signature is in base64; we have to decode it into a binary stream. |
|---|
| 127 | unsigned char *signature = (unsigned char *)[encodedSignature UTF8String]; |
|---|
| 128 | long length = b64decode(signature); // Decode the signature in-place and get the new length of the signature string. |
|---|
| 129 | |
|---|
| 130 | // We've got the signature, now get the file data. |
|---|
| 131 | NSData *pathData = [NSData dataWithContentsOfFile:path]; |
|---|
| 132 | if (!pathData) { return NO; } |
|---|
| 133 | |
|---|
| 134 | // Hash the file with SHA-1. |
|---|
| 135 | unsigned char md[SHA_DIGEST_LENGTH]; |
|---|
| 136 | SHA1([pathData bytes], [pathData length], md); |
|---|
| 137 | |
|---|
| 138 | // Actually verify the signature on the file. |
|---|
| 139 | EVP_MD_CTX ctx; |
|---|
| 140 | if(EVP_VerifyInit(&ctx, EVP_dss1()) == 1) // We're using DSA keys. |
|---|
| 141 | { |
|---|
| 142 | EVP_VerifyUpdate(&ctx, md, SHA_DIGEST_LENGTH); |
|---|
| 143 | result = (EVP_VerifyFinal(&ctx, signature, (unsigned int)length, pkey) == 1); |
|---|
| 144 | } |
|---|
| 145 | |
|---|
| 146 | EVP_PKEY_free(pkey); |
|---|
| 147 | return result; |
|---|
| 148 | } |
|---|
| 149 | |
|---|
| 150 | @end |
|---|