diff --git a/enzevalos_iphone/SMIME.swift b/enzevalos_iphone/SMIME.swift index ece422e0a131c11a6665334f8835de618272cf55..4fa30dd4ed97b3d2002924f874e8dcec33cdc4b9 100644 --- a/enzevalos_iphone/SMIME.swift +++ b/enzevalos_iphone/SMIME.swift @@ -7,6 +7,8 @@ // import Foundation +import Security +import KeychainAccess /** The class is a swift wrapper around the C implementation for SMIME so that all C pointers are deallocated correctly @@ -213,15 +215,152 @@ AYIHvW6qRLTsSR6BZZS3pqGXYue7fE0vj4HJ2IEpj05qQ5RXrD57Wg== var test_string = "Hello world whatever the fuck"; + + private let privateKeyPattern = "[-]*BEGIN[a-zA-Z ]*PRIVATE KEY[-]*[a-zA-Z+/\n\r=0-9]*[-]*END[a-zA-Z ]*PRIVATE KEY[-]*" + private let certificatePattern = "[-]*BEGIN[a-zA-Z ]* CERTIFICATE[-]*[a-zA-Z+/\n\r=0-9]*[-]*END[a-zA-Z ]* CERTIFICATE[-]*" + + + private var privateKeyKeychain: Keychain { + get { + return Keychain(service: "Enzevalos/SMIME/privateKeys") + } + } + + private var certsKeychain: Keychain { + get { + return Keychain(service: "Enzevalos/SMIME/certs") + } + } + + private var CAKeychain: Keychain { + get { + return Keychain(service: "Enzevalos/SMIME/CAs") + } + } + + private func importInKeychain(certs: [String], keychain: Keychain) -> [String] { + var fingerprints: [String] = [] + + for cert in certs { + let (fp, _, _) = getFingerprintFromPem(pem: cert) + if fp != nil { + keychain[fp!] = cert + fingerprints.append(fp!) + } + } + + return fingerprints + } + + func importCerts(certs: [String]) -> [String] { + return importInKeychain(certs: certs.map({ (cert: String) -> String in + return cert.extractPattern(pattern: certificatePattern).joined(separator: "\r\n") + }), keychain: certsKeychain) + } + + func importCA(certs: [String]) -> [String] { + return importInKeychain(certs: certs.map({ (cert: String) -> String in + return cert.extractPattern(pattern: certificatePattern).joined(separator: "\r\n") + }), keychain: CAKeychain) + } + + func importKeys(certsAndKeys: [(String, String)]) -> [String] { + var fingerprints: [String] = [] + + for (cert, key) in certsAndKeys { + let (certFP, _, _) = getFingerprintFromPem(pem: cert) + if (certFP != nil) { + certsKeychain[certFP!] = cert + privateKeyKeychain[certFP!] = key + fingerprints.append(certFP!) + } + } + + return fingerprints + } + + func resetKeychain () { + do{ + try privateKeyKeychain.removeAll() + try certsKeychain.removeAll() + try CAKeychain.removeAll() + }catch { + print("Can not reset keychains.") + } + } + + func importKeys(certsAndKeys: [String]) -> [String] { + var certsAndKeysSplit: [(String, String)] = [] + + for str in certsAndKeys { + let keys = str.extractPattern(pattern: privateKeyPattern) + let certs = str.extractPattern(pattern: certificatePattern) + if keys.count != 1 { + print("Incorrect amount of keys: ", keys.count) + continue + //todo exception + } + if certs.count == 0 + { + print("No certs") + continue//do exception + } + certsAndKeysSplit.append((certs.joined(separator: "\r\n"), keys[0])) + } + + return importKeys(certsAndKeys: certsAndKeysSplit) + } + + + func addPrivateKey(keyPlusCertPEM: String) -> String + { + let fp = importKeys(certsAndKeys: [keyPlusCertPEM]) + privateKeyKeychain["ownkey"]=fp[0] + return fp[0] + } + + func addCertForContant(email: String, cert:String) -> String + { + let fp = importCerts(certs: [cert]) + certsKeychain[email]=fp[0] + return fp[0] + } + let cryptoScheme = CryptoScheme.SMIME + func testKeychain () { + resetKeychain() + + let cas = importCA(certs: [testCA]) + let keys = importKeys(certsAndKeys: [test_key]) + privateKeyKeychain["ownkey"]=keys[0] + let certs = importCerts(certs: [test_key_other]) + + for ca in cas + { + print("\nCA FP ",ca, " \nPEM \n", CAKeychain[ca]!) + } + for c in certs + { + print("\nCERT FP ",c, " \nPEM \n", certsKeychain[c]!) + } + for k in keys + { + print("\nPRIV FP ",k, " \nPEM \n", privateKeyKeychain[k]!) + } + } + func testSMIMEencrypt(){ // OpenSSL_print_ver() - let (enc, enc_errs) = encrypt_with_pem(message: test_string, keyAsPem: test_key) + let fp = addPrivateKey(keyPlusCertPEM: test_key) + let cert = certsKeychain[privateKeyKeychain["ownkey"]!]! + let key = privateKeyKeychain[privateKeyKeychain["ownkey"]!]! + + let (enc, enc_errs) = encryptWithPem(message: test_string, certAsPem: cert) if enc != nil { // the pointers point to memory allocatedi in c that needs to be manually dealocated print("SWIFT ENC DONE: ", enc ?? "") - let (dec, dec_errs) = decrypt_with_pem(message: enc ?? "", keyAsPem: test_key) + let (dec, dec_errs) = decryptWithPem(message: enc ?? "", certAsPem: cert, keyAsPem: key) if dec != nil { print("SWIFT DEC DONE: ", dec ?? "") } @@ -238,7 +377,7 @@ AYIHvW6qRLTsSR6BZZS3pqGXYue7fE0vj4HJ2IEpj05qQ5RXrD57Wg== } } - let (sig, sigErr) = sign_with_pem(message: test_string, keyAsPem: test_key, detached: false) + let (sig, sigErr) = signWithPem(message: test_string, certAsPem: cert, keyAsPem: key, detached: false) if sig != nil{ // the pointers point to memory allocatedi in c that needs to be manually dealocated print("\nSWIFT SIGN (attached): \n", sig ?? "") @@ -246,7 +385,7 @@ AYIHvW6qRLTsSR6BZZS3pqGXYue7fE0vj4HJ2IEpj05qQ5RXrD57Wg== else{ print("\n SWIFT SIGN1 failed") } - let (sig2, sig2Err) = sign_with_pem(message: test_string, keyAsPem: test_key, detached: true) + let (sig2, sig2Err) = signWithPem(message: test_string, certAsPem: cert, keyAsPem: key, detached: true) if sig2 != nil{ // the pointers point to memory allocatedi in c that needs to be manually dealocated print("SWIFT SIGN (dettached): \n", sig2 ?? "") @@ -254,8 +393,13 @@ AYIHvW6qRLTsSR6BZZS3pqGXYue7fE0vj4HJ2IEpj05qQ5RXrD57Wg== else{ print("\n SWIFT SIGN2 failed") } - - let (vertest, certs,verErrs) = verify_with_CApem(message: sig!, pemCAArr: [testCA, testEvilCA]) + + var CAs : [String] = [] + for k in CAKeychain.allKeys() + { + CAs.append(CAKeychain[k]!) + } + let (vertest, certs,verErrs) = verifyWithCApem(message: sig!, pemCAArr: CAs ) if vertest != nil { print("In Verification", vertest!) @@ -267,10 +411,22 @@ AYIHvW6qRLTsSR6BZZS3pqGXYue7fE0vj4HJ2IEpj05qQ5RXrD57Wg== else{ print("Vertest was nil") } - } - func encrypt_with_pem(message: String,keyAsPem: String) -> (String?, [UInt]?) { - let enc = OpenSSL_encrypt(message,keyAsPem) + let (fpTestKey, _, _) = getFingerprintFromPem(pem: test_key) + if (fpTestKey != nil) { + print("TEST KEY FINGERPRINT", fpTestKey ?? "") + } + + let (fpTestCA, _, _) = getFingerprintFromPem(pem: testCA) + if (fpTestCA != nil) { + print("TEST CA FINGERPRINT", fpTestCA ?? "") + } + } + + + + func encryptWithPem(message: String,certAsPem: String) -> (String?, [UInt]?) { + let enc = OpenSSL_encrypt(message, certAsPem) let result = enc?.pointee; let encStr = result?._extractOutput() @@ -280,8 +436,8 @@ AYIHvW6qRLTsSR6BZZS3pqGXYue7fE0vj4HJ2IEpj05qQ5RXrD57Wg== return (encStr, errArr) } - func decrypt_with_pem(message:String, keyAsPem:String) -> (String?, [UInt]?) { - let dec = OpenSSL_decrypt(message,keyAsPem) + func decryptWithPem(message:String, certAsPem: String, keyAsPem:String) -> (String?, [UInt]?) { + let dec = OpenSSL_decrypt(message, certAsPem, keyAsPem) let result = dec?.pointee; let decStr = result?._extractOutput() @@ -291,13 +447,13 @@ AYIHvW6qRLTsSR6BZZS3pqGXYue7fE0vj4HJ2IEpj05qQ5RXrD57Wg== return (decStr, errArr) } - func sign_with_pem(message:String, keyAsPem:String, detached:Bool) -> (String?, [UInt]?) { + func signWithPem(message:String, certAsPem: String, keyAsPem:String, detached:Bool) -> (String?, [UInt]?) { var detFlag : Int32 = 0 if detached { detFlag = 1 } - let sig = OpenSSL_sign(message,keyAsPem, detFlag) + let sig = OpenSSL_sign(message, certAsPem, keyAsPem, detFlag) let result = sig?.pointee let sigStr = result?._extractOutput() @@ -307,7 +463,7 @@ AYIHvW6qRLTsSR6BZZS3pqGXYue7fE0vj4HJ2IEpj05qQ5RXrD57Wg== return (sigStr, errArr) } - func verify_with_CApem (message:String, pemCAArr: [String]) -> (String?, [String]?, [UInt]?) { + func verifyWithCApem (message:String, pemCAArr: [String]) -> (String?, [String]?, [UInt]?) { let pemCAArrC = createCStrArr(sarr: pemCAArr) let ver = OpenSSL_verify(message, pemCAArrC, Int32(pemCAArr.count)) deallocateCStrArr(arr: pemCAArrC, len:pemCAArr.count) @@ -322,6 +478,18 @@ AYIHvW6qRLTsSR6BZZS3pqGXYue7fE0vj4HJ2IEpj05qQ5RXrD57Wg== return (verStr, certArr, errArr) } + func getFingerprintFromPem (pem: String) -> (String?, [String]?, [UInt]?) { + let res = get_fingerprint_from_pem(pem, 0); + + let result = res?.pointee; + + let fpStr = result?._extractOutput() + let errArr = result?._extractErrors() + + res?.deallocate() + return (fpStr, nil, errArr) + } + func getErrorString(errCode: UInt) -> String { let cStr = get_err_string(errCode); if (cStr != nil) { @@ -411,8 +579,18 @@ extension result { return swiftStr } - - - } +extension String { + func extractPattern(pattern: String) -> [String]{ + let range = NSRange(location: 0, length: self.utf16.count) + let regex = try! NSRegularExpression(pattern: pattern) + let check = regex.matches(in: self, options: [], range: range) + var foundLinks:[String] = [] + for match in check{ + guard let range = Range(match.range, in: self) else {continue} + foundLinks.append(String(self[range])) + } + return (foundLinks) + } +} diff --git a/enzevalos_iphone/SearchHelper.swift b/enzevalos_iphone/SearchHelper.swift index 5bb55e030eda11175751a492a3f6890332478ee1..e8a8e79772ad74d4858a07b7a2eb47d280e3cc1c 100644 --- a/enzevalos_iphone/SearchHelper.swift +++ b/enzevalos_iphone/SearchHelper.swift @@ -32,6 +32,7 @@ func containsSearchTerms ( content : String?, searchText: String) -> Bool //OpenSSL_print_ver(); var smime: SMIME = SMIME() smime.testSMIMEencrypt() + //smime.testKeychain() var longterms : [String] = [] var terms : [String] = [] diff --git a/enzevalos_iphone/SwiftPGP.swift b/enzevalos_iphone/SwiftPGP.swift index e4ab8e5ce5b4a77508473163fdc658d040ba10e4..9cdcd750ac49117f9bc3f1fd2258589944beaa1c 100644 --- a/enzevalos_iphone/SwiftPGP.swift +++ b/enzevalos_iphone/SwiftPGP.swift @@ -25,7 +25,6 @@ class SwiftPGP: Encryption { } - private func generatePW(size: Int, splitInBlocks: Bool) -> String{ let file = open("/dev/urandom", O_RDONLY) if file >= 0{ diff --git a/enzevalos_iphone/c/openssl-helpers.c b/enzevalos_iphone/c/openssl-helpers.c index cb073ca8b440b7ce714355971453ade0b70423da..4bd982d826d17cfeaee9c222213a084e0c7a91e1 100644 --- a/enzevalos_iphone/c/openssl-helpers.c +++ b/enzevalos_iphone/c/openssl-helpers.c @@ -8,6 +8,9 @@ #include "openssl-helpers.h" #include <glob.h> +// This is a wrapper around the OpenSSL functions, where everz function is primarily inspired by the SMIME demos in the OpenSSL github +// https://github.com/openssl/openssl/tree/master/demos/smime + STACK_OF(X509)* create_stack_x509(X509 *arr, int len) { STACK_OF(X509) *stack = sk_X509_new_null(); @@ -45,15 +48,6 @@ void OpenSSL_print_ver(void) { printf("%s", OPENSSL_VERSION_TEXT); } -void OpenSSL_initialize(void) { - - ERR_load_crypto_strings(); -} - -void OpenSSL_deinitialize(void) { - ERR_free_strings(); -} - int print_test(int a) { printf("%d\n", a); @@ -191,16 +185,15 @@ deinit: BIO_free(out); // also frees tmp BIO_free(rec_cert_bio); X509_free(rec_cert); - OpenSSL_deinitialize(); return res; } -result * OpenSSL_decrypt(const char *str, const char *pem) { +result * OpenSSL_decrypt(const char *str, const char *pem_cert, const char *pem_key) { // https://github.com/openssl/openssl/blob/master/demos/cms/cms_dec.c - OpenSSL_initialize(); + OpenSSL_add_all_algorithms(); char *decrypted = NULL, *tmp=NULL; - BIO *in = NULL, *out = NULL, *rec_cert_bio = NULL; + BIO *in = NULL, *out = NULL, *rec_cert_bio = NULL, *rec_key_bio = NULL; // recipient certificate X509 *rec_cert = NULL; CMS_ContentInfo *cms = NULL; @@ -209,11 +202,16 @@ result * OpenSSL_decrypt(const char *str, const char *pem) { array_with_length *temp = NULL; // this trick allows to hardcode a certificate as a string - rec_cert_bio = BIO_new_mem_buf(pem, (int) strlen(pem)); + rec_cert_bio = BIO_new_mem_buf(pem_cert, (int) strlen(pem_cert)); + rec_key_bio = BIO_new_mem_buf(pem_key, (int) strlen(pem_key)); in = BIO_new_mem_buf(str,(int) strlen(str)); // simpletest if (!rec_cert_bio) { + printf("Failed reading cert!\n"); + goto deinit; + } + if (!rec_key_bio) { printf("Failed reading mykey.pem!\n"); goto deinit; } @@ -224,15 +222,13 @@ result * OpenSSL_decrypt(const char *str, const char *pem) { goto deinit; } - BIO_reset(rec_cert_bio); - rkey = PEM_read_bio_PrivateKey(rec_cert_bio, NULL, 0, NULL); - + rkey = PEM_read_bio_PrivateKey(rec_key_bio, NULL, 0, NULL); + if (!rkey) { printf("Failed reading pem key\n"); goto deinit; } - cms = SMIME_read_CMS(in, NULL); if (!cms) { printf("DEC Failed at SMIME_READ"); @@ -261,17 +257,18 @@ deinit: BIO_free(in); BIO_free(out); // also frees tmp BIO_free(rec_cert_bio); + BIO_free(rec_key_bio); + EVP_PKEY_free(rkey); X509_free(rec_cert); - OpenSSL_deinitialize(); // OpenSSL ver 1.0.2.f has a bug (seemingly) that causes a crash when freeing cms content info pointers return res; } -result * OpenSSL_sign(const char *text, const char *pem, const int detached) +result * OpenSSL_sign(const char *text, const char *pem_cert, const char *pem_key, const int detached) { - OpenSSL_initialize(); + OpenSSL_add_all_algorithms(); char *mail = NULL, *tmp=NULL; - BIO *in = NULL, *out = NULL, *sig_cert_bio = NULL; + BIO *in = NULL, *out = NULL, *sig_cert_bio = NULL, *sig_key_bio = NULL; // recipient certificate X509 *sig_cert = NULL; STACK_OF(X509) *cert_stack = NULL; @@ -286,8 +283,14 @@ result * OpenSSL_sign(const char *text, const char *pem, const int detached) in = BIO_new_mem_buf(text,(int) strlen(text)); // simpletest - sig_cert_bio = BIO_new_mem_buf(pem, (int) strlen(pem)); + sig_cert_bio = BIO_new_mem_buf(pem_cert, (int) strlen(pem_cert)); if (!sig_cert_bio) { + printf("Failed reading cert!\n"); + goto deinit; + } + + sig_key_bio = BIO_new_mem_buf(pem_key, (int) strlen(pem_key)); + if (!sig_key_bio) { printf("Failed reading mykey.pem!\n"); goto deinit; } @@ -306,8 +309,7 @@ result * OpenSSL_sign(const char *text, const char *pem, const int detached) goto deinit; } - BIO_reset(sig_cert_bio); - skey = PEM_read_bio_PrivateKey(sig_cert_bio, NULL, 0, NULL); + skey = PEM_read_bio_PrivateKey(sig_key_bio, NULL, 0, NULL); if (!skey) { printf("Failed reading pem key\n"); @@ -348,9 +350,10 @@ deinit: BIO_free(in); BIO_free(out); // also frees tmp BIO_free(sig_cert_bio); + BIO_free(sig_key_bio); + EVP_PKEY_free(skey); X509_free(sig_cert); - OpenSSL_deinitialize(); return res; } @@ -362,7 +365,7 @@ result * OpenSSL_verify(const char *text, char **pem_cert, const int num_certs) ver->errors = NULL; ver->res = NULL; - OpenSSL_initialize(); + OpenSSL_add_all_algorithms(); char *realtext = NULL, *tmp=NULL; BIO *in = NULL, *out = NULL; @@ -441,19 +444,74 @@ deinit: BIO_free(out); // also frees tmp X509_STORE_free(cert_store); for (int i =0;i>num_certs;i++) X509_free(sig_certs[i]); //We need to free all certs - OpenSSL_deinitialize(); // OpenSSL ver 1.0.2.f has a bug (seemingly) that causes a crash when freeing cms content info pointers return ver; } +result * get_fingerprint_from_pem(const char *pem, int md_alg) { + // https://github.com/openssl/openssl/blob/master/demos/cms/cms_enc.c + OpenSSL_add_all_algorithms(); + BIO *rec_cert_bio = NULL; + // recipient certificate + X509 *rec_cert = NULL; + array_with_length *temp = NULL; + result *res = malloc(sizeof(result)); + unsigned int len; + unsigned char md[EVP_MAX_MD_SIZE]; + + // this trick allows to hardcode a certificate as a string + rec_cert_bio = BIO_new_mem_buf(pem, (int) strlen(pem)); + + if (!rec_cert_bio) { + printf("Failed reading mykey.pem!\n"); + goto deinit; + } + + rec_cert = PEM_read_bio_X509(rec_cert_bio, NULL, 0, NULL); + + if (!rec_cert) { + printf("Failed reading pem\n"); + goto deinit; + } + const EVP_MD *alg=NULL; + + switch(md_alg) + { + case 0: + alg = EVP_sha256(); + break; + case 1: + alg = EVP_sha1(); + break; + default: + alg= EVP_sha256(); + } + + if (!X509_digest(rec_cert, alg, md, &len)) { + printf("Failed at digest!\n"); + goto deinit; + } + + res->res = bin_to_hex(md, len); +deinit: + temp = create_list_of_errors(); + res->errors = temp->arr; + res->num_errors = temp->size; + free(temp); + BIO_free(rec_cert_bio); + X509_free(rec_cert); + return res; +} + char ** init_str_arr(int num) { char ** arr = malloc(num*sizeof(char*)); return arr; } + char ** add_str_to_arr(const char *str, char **arr, int i) { - int len = strlen(str); + unsigned long len = strlen(str); char *newstr = malloc(len+1); memcpy(newstr, str, len); newstr[len]=0; @@ -469,7 +527,7 @@ void deallocate_str_arr(char **arr, int len) free(arr); } -char *bin_to_hex ( unsigned char *bin, int len) +char *bin_to_hex(unsigned char *bin, int len) { unsigned char buf[3]; char *res = malloc(2*len+1); diff --git a/enzevalos_iphone/c/openssl-helpers.h b/enzevalos_iphone/c/openssl-helpers.h index 92bb5f65ef434f1104465471d7afcf066e55b1bd..57ed283a3583477ae6e9547869435a4b69d55a26 100644 --- a/enzevalos_iphone/c/openssl-helpers.h +++ b/enzevalos_iphone/c/openssl-helpers.h @@ -10,6 +10,7 @@ #include <stdio.h> #include <string.h> +#include <stdlib.h> #include <openssl/opensslv.h> #include <openssl/evp.h> // this header may not be needed #include <openssl/err.h> // this header may not be needed @@ -46,13 +47,17 @@ char * pop_error (result *res); void OpenSSL_print_ver(void); // (de)init function makes initialization less cryptic char *get_err_string(unsigned long err); + result * OpenSSL_encrypt(const char *text, const char *pem); -result * OpenSSL_decrypt(const char *str, const char *pem); -result * OpenSSL_sign(const char *text, const char *pem, const int detached); +result * OpenSSL_decrypt(const char *str, const char *pem_cert, const char *pem_key); +result * OpenSSL_sign(const char *text, const char *pem_cert, const char *pem_key, const int detached); result * OpenSSL_verify(const char *text, char **pem_cert, const int num_certs); +result * get_fingerprint_from_pem(const char *pem, int md_alg); // 0 for SHA-256, 1 for SHA-1 + char ** init_str_arr(int num); char ** add_str_to_arr(const char *str, char **arr, int i); void deallocate_str_arr(char **arr, int len); +char *bin_to_hex ( unsigned char *bin, int len); int print_test(int);