diff --git a/.gitmodules b/.gitmodules index 761522b..c688681 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "Carthage/Checkouts/CryptoSwift"] path = Carthage/Checkouts/CryptoSwift url = https://github.com/krzyzanowskim/CryptoSwift.git +[submodule "Carthage/Checkouts/SwCrypt"] + path = Carthage/Checkouts/SwCrypt + url = https://github.com/jossgb/SwCrypt.git diff --git a/Cartfile b/Cartfile index 4f0cac5..84fa85e 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1,2 @@ github "krzyzanowskim/CryptoSwift" ~> 0.6.1 +github "jossgb/SwCrypt" diff --git a/Cartfile.resolved b/Cartfile.resolved index 5b562e4..f04055a 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1,2 @@ -github "krzyzanowskim/CryptoSwift" "0.6.1" +github "jossgb/SwCrypt" "4.0.0" +github "krzyzanowskim/CryptoSwift" "0.6.9" diff --git a/Carthage/Checkouts/CryptoSwift b/Carthage/Checkouts/CryptoSwift index 5f9bb95..85c659a 160000 --- a/Carthage/Checkouts/CryptoSwift +++ b/Carthage/Checkouts/CryptoSwift @@ -1 +1 @@ -Subproject commit 5f9bb95c6f246c7e19bf4346a6ad1a0c406415f0 +Subproject commit 85c659ac534c5f8a72eb3b72488fb941e82763bd diff --git a/Carthage/Checkouts/SwCrypt/.gitignore b/Carthage/Checkouts/SwCrypt/.gitignore new file mode 100644 index 0000000..101296b --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/.gitignore @@ -0,0 +1,25 @@ +## Build generated +build/ +DerivedData +test_output + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata + +## Other +*.xccheckout +*.moved-aside +*.xcuserstate +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa diff --git a/Carthage/Checkouts/SwCrypt/Carthage/Build b/Carthage/Checkouts/SwCrypt/Carthage/Build new file mode 120000 index 0000000..76f9ba2 --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/Carthage/Build @@ -0,0 +1 @@ +../../../../Carthage/Build \ No newline at end of file diff --git a/Carthage/Checkouts/SwCrypt/README.md b/Carthage/Checkouts/SwCrypt/README.md new file mode 100644 index 0000000..005072e --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/README.md @@ -0,0 +1,137 @@ +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) + +SwCrypt +========= + +### Create public and private RSA keys in DER format +``` +let (privateKey, publicKey) = try! CC.RSA.generateKeyPair(2048) +``` +### Convert them to PEM format +``` +let privateKeyPEM = try SwKeyConvert.PrivateKey.derToPKCS1PEM(privateKey) +let publicKeyPEM = SwKeyConvert.PublicKey.derToPKCS8PEM(publicKey) +``` +### Or read them from strings with PEM data +``` +let privateKeyDER = SwKeyConvert.PrivateKey.pemToPKCS1DER(privateKeyPEM) +let publicKeyDER = SwKeyConvert.PublicKey.pemToPKCS1DER(publicKeyPEM) +``` +### Or encrypt, decrypt the private key (OpenSSL compatible) +``` +try SwKeyConvert.PrivateKey.encryptPEM(privateKeyPEM, passphrase: "longpassword", mode: .aes256CBC) +try SwKeyConvert.PrivateKey.decryptPEM(privEncrypted, passphrase: "longpassword") +``` +### Encrypt, decrypt data with RSA +``` +try CC.RSA.encrypt(data, derKey: publicKey, tag: tag, padding: .oaep, digest: .sha1) +try CC.RSA.decrypt(data, derKey: privateKey, tag: tag, padding: .oaep, digest: .sha1) +``` +### Sign, verify data with RSA +``` +let sign = try? CC.RSA.sign(testMessage, derKey: privKey, padding: .pss, + digest: .sha256, saltLen: 16) +let verified = try? CC.RSA.verify(testMessage, derKey: pubKey, padding: .pss, + digest: .sha256, saltLen: 16, signedData: sign!) +``` +### Elliptic curve functions +``` +let keys = try? CC.EC.generateKeyPair(384) +let signed = try? CC.EC.signHash(keys!.0, hash: hash) +let verified = try? CC.EC.verifyHash(keys!.1, hash: hash, signedData: signed!) + +let shared = try? CC.EC.computeSharedSecret(keys!.0, publicKey: partnerPubKey) +``` +### Diffie-Hellman functions +``` +let dh = try CC.DH.DH(dhParam: .rfc3526Group5) +let myPubKey = try dh.generateKey() +let commonKey = try dh.computeKey(partnerPubKey!) +``` +### Encrypt, decrypt data with symmetric ciphers +``` +try CC.crypt(.encrypt, blockMode: .cbc, algorithm: .aes, padding: .pkcs7Padding, data: data, key: aesKey, iv: iv) +try CC.crypt(.decrypt, blockMode: .cfb, algorithm: .aes, padding: .pkcs7Padding, data: data, key: aesKey, iv: iv) +``` +### Encrypt, decrypt data with symmetric authenticating ciphers +``` +try CC.cryptAuth(.encrypt, blockMode: .gcm, algorithm: .aes, data: data, aData: aData, key: aesKey, iv: iv, tagLength: tagLength) +try CC.cryptAuth(.decrypt, blockMode: .ccm, algorithm: .aes, data: data, aData: aData, key: aesKey, iv: iv, tagLength: tagLength) +``` +### Digest functions +``` +CC.digest(data, alg: .md5) +CC.digest(data, alg: .sha256) +CC.digest(data, alg: .sha512) +``` +### HMAC function +``` +CC.HMAC(data, alg: .sha512, key: key) +``` +### CMAC function +``` +CC.CMAC.AESCMAC(input, key: key) +``` +### CRC function +``` +let output = try? CC.CRC.crc(input, mode: .crc32) +``` +### KeyDerivation +``` +CC.KeyDerivation.PBKDF2(password, salt: salt, prf: .sha256, rounds: 4096) +``` +### Symmetric Key Wrapping +``` +try CC.KeyWrap.SymmetricKeyWrap(CC.KeyWrap.rfc3394IV, kek: kek, rawKey: rawKey) +try CC.KeyWrap.SymmetricKeyUnwrap(CC.KeyWrap.rfc3394IV, kek: kek, wrappedKey: wrappedKey) +``` +### Upsert, get, delete keys from KeyStore +``` +try SwKeyStore.upsertKey(privateKeyPEM, keyTag: "priv", options: [kSecAttrAccessible:kSecAttrAccessibleWhenUnlockedThisDeviceOnly]) +try SwKeyStore.getKey("priv") +try SwKeyStore.delKey("priv") +``` +----- + + +Check availability +--------------------- + +SwCrypt uses dlopen and dlsym to load the CommonCrypto's functions, because not all of them are available in public header files. You have to check the availability before using them. + +``` +let digestAvailable : Bool = CC.digestAvailable() +let ramdomAvailable : Bool = CC.randomAvailable(() +let hmacAvailable : Bool = CC.hmacAvailable() +let cryptorAvailable : Bool = CC.cryptorAvailable +let keyDerivationAvailable : Bool = CC.KeyDerivation.available() +let keyWrapAvailable : Bool = CC.KeyWrap.available() +let rsaAvailable : Bool = CC.RSA.available() +let dhAvailable : Bool = CC.DH.available() +let ecAvailable : Bool = CC.EC.available() +let crcAvailable : Bool = CC.CRC.available() +let cmacAvailable : Bool = CC.CMAC.available() +let gcmAvailable : Bool = CC.GCM.available() +let ccmAvailable : Bool = CC.CCM.available() + +or all in one turn: +let ccAvailable : Bool = CC.available() +``` + +Install +------- +Just copy [SwCrypt.swift](https://github.com/soyersoyer/SwCrypt/blob/master/SwCrypt/SwCrypt.swift) to your project or use the [Carthage](https://github.com/Carthage/Carthage) dependency manager. + +Inspired from +------------- + + - + - + - + - + - + +License +------- + +This project is copyrighted under the MIT license. diff --git a/Carthage/Checkouts/SwCrypt/SwCrypt.xcodeproj/project.pbxproj b/Carthage/Checkouts/SwCrypt/SwCrypt.xcodeproj/project.pbxproj new file mode 100644 index 0000000..d4ce306 --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/SwCrypt.xcodeproj/project.pbxproj @@ -0,0 +1,574 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + A051E37E1DBA5DA4004293E9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A051E37D1DBA5DA4004293E9 /* AppDelegate.swift */; }; + A051E3801DBA5DA4004293E9 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A051E37F1DBA5DA4004293E9 /* ViewController.swift */; }; + A051E3831DBA5DA4004293E9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A051E3811DBA5DA4004293E9 /* Main.storyboard */; }; + A051E3851DBA5DA4004293E9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A051E3841DBA5DA4004293E9 /* Assets.xcassets */; }; + A051E3881DBA5DA4004293E9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A051E3861DBA5DA4004293E9 /* LaunchScreen.storyboard */; }; + A0A71E7C1CBC7D74002C5C88 /* SwCrypt.h in Headers */ = {isa = PBXBuildFile; fileRef = A0A71E7B1CBC7D74002C5C88 /* SwCrypt.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A0A71E831CBC7D74002C5C88 /* SwCrypt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A0A71E781CBC7D74002C5C88 /* SwCrypt.framework */; }; + A0A71E881CBC7D74002C5C88 /* SwCryptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0A71E871CBC7D74002C5C88 /* SwCryptTests.swift */; }; + A0A71E931CBC7DB4002C5C88 /* SwCrypt.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0A71E921CBC7DB4002C5C88 /* SwCrypt.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + A051E38D1DBA5DAA004293E9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A0A71E6F1CBC7D74002C5C88 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A051E37A1DBA5DA3004293E9; + remoteInfo = dummyTestApp; + }; + A0A71E841CBC7D74002C5C88 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A0A71E6F1CBC7D74002C5C88 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A0A71E771CBC7D74002C5C88; + remoteInfo = SwCrypt; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + A042DCB91CC0800F00271902 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + A051E37B1DBA5DA3004293E9 /* dummyTestApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = dummyTestApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A051E37D1DBA5DA4004293E9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + A051E37F1DBA5DA4004293E9 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + A051E3821DBA5DA4004293E9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + A051E3841DBA5DA4004293E9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + A051E3871DBA5DA4004293E9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + A051E3891DBA5DA4004293E9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A051E38F1DBA5DDB004293E9 /* dummyTestApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = dummyTestApp.entitlements; sourceTree = ""; }; + A0A71E781CBC7D74002C5C88 /* SwCrypt.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwCrypt.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A0A71E7B1CBC7D74002C5C88 /* SwCrypt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwCrypt.h; sourceTree = ""; }; + A0A71E7D1CBC7D74002C5C88 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A0A71E821CBC7D74002C5C88 /* SwCryptTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwCryptTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + A0A71E871CBC7D74002C5C88 /* SwCryptTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwCryptTests.swift; sourceTree = ""; }; + A0A71E891CBC7D74002C5C88 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A0A71E921CBC7DB4002C5C88 /* SwCrypt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwCrypt.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + A051E3781DBA5DA3004293E9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A0A71E741CBC7D74002C5C88 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A0A71E7F1CBC7D74002C5C88 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A0A71E831CBC7D74002C5C88 /* SwCrypt.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + A051E37C1DBA5DA4004293E9 /* dummyTestApp */ = { + isa = PBXGroup; + children = ( + A051E38F1DBA5DDB004293E9 /* dummyTestApp.entitlements */, + A051E37D1DBA5DA4004293E9 /* AppDelegate.swift */, + A051E37F1DBA5DA4004293E9 /* ViewController.swift */, + A051E3811DBA5DA4004293E9 /* Main.storyboard */, + A051E3841DBA5DA4004293E9 /* Assets.xcassets */, + A051E3861DBA5DA4004293E9 /* LaunchScreen.storyboard */, + A051E3891DBA5DA4004293E9 /* Info.plist */, + ); + path = dummyTestApp; + sourceTree = ""; + }; + A0A71E6E1CBC7D74002C5C88 = { + isa = PBXGroup; + children = ( + A042DCB91CC0800F00271902 /* README.md */, + A0A71E7A1CBC7D74002C5C88 /* SwCrypt */, + A0A71E861CBC7D74002C5C88 /* SwCryptTests */, + A051E37C1DBA5DA4004293E9 /* dummyTestApp */, + A0A71E791CBC7D74002C5C88 /* Products */, + ); + sourceTree = ""; + }; + A0A71E791CBC7D74002C5C88 /* Products */ = { + isa = PBXGroup; + children = ( + A0A71E781CBC7D74002C5C88 /* SwCrypt.framework */, + A0A71E821CBC7D74002C5C88 /* SwCryptTests.xctest */, + A051E37B1DBA5DA3004293E9 /* dummyTestApp.app */, + ); + name = Products; + sourceTree = ""; + }; + A0A71E7A1CBC7D74002C5C88 /* SwCrypt */ = { + isa = PBXGroup; + children = ( + A0A71E921CBC7DB4002C5C88 /* SwCrypt.swift */, + A0A71E7B1CBC7D74002C5C88 /* SwCrypt.h */, + A0A71E7D1CBC7D74002C5C88 /* Info.plist */, + ); + path = SwCrypt; + sourceTree = ""; + }; + A0A71E861CBC7D74002C5C88 /* SwCryptTests */ = { + isa = PBXGroup; + children = ( + A0A71E871CBC7D74002C5C88 /* SwCryptTests.swift */, + A0A71E891CBC7D74002C5C88 /* Info.plist */, + ); + path = SwCryptTests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + A0A71E751CBC7D74002C5C88 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A0A71E7C1CBC7D74002C5C88 /* SwCrypt.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + A051E37A1DBA5DA3004293E9 /* dummyTestApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = A051E38C1DBA5DA4004293E9 /* Build configuration list for PBXNativeTarget "dummyTestApp" */; + buildPhases = ( + A051E3771DBA5DA3004293E9 /* Sources */, + A051E3781DBA5DA3004293E9 /* Frameworks */, + A051E3791DBA5DA3004293E9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dummyTestApp; + productName = dummyTestApp; + productReference = A051E37B1DBA5DA3004293E9 /* dummyTestApp.app */; + productType = "com.apple.product-type.application"; + }; + A0A71E771CBC7D74002C5C88 /* SwCrypt */ = { + isa = PBXNativeTarget; + buildConfigurationList = A0A71E8C1CBC7D74002C5C88 /* Build configuration list for PBXNativeTarget "SwCrypt" */; + buildPhases = ( + A0A71E731CBC7D74002C5C88 /* Sources */, + A0A71E741CBC7D74002C5C88 /* Frameworks */, + A0A71E751CBC7D74002C5C88 /* Headers */, + A0A71E761CBC7D74002C5C88 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SwCrypt; + productName = SwCrypt; + productReference = A0A71E781CBC7D74002C5C88 /* SwCrypt.framework */; + productType = "com.apple.product-type.framework"; + }; + A0A71E811CBC7D74002C5C88 /* SwCryptTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = A0A71E8F1CBC7D74002C5C88 /* Build configuration list for PBXNativeTarget "SwCryptTests" */; + buildPhases = ( + A0A71E7E1CBC7D74002C5C88 /* Sources */, + A0A71E7F1CBC7D74002C5C88 /* Frameworks */, + A0A71E801CBC7D74002C5C88 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + A0A71E851CBC7D74002C5C88 /* PBXTargetDependency */, + A051E38E1DBA5DAA004293E9 /* PBXTargetDependency */, + ); + name = SwCryptTests; + productName = SwCryptTests; + productReference = A0A71E821CBC7D74002C5C88 /* SwCryptTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + A0A71E6F1CBC7D74002C5C88 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0800; + LastUpgradeCheck = 0730; + ORGANIZATIONNAME = irl; + TargetAttributes = { + A051E37A1DBA5DA3004293E9 = { + CreatedOnToolsVersion = 8.0; + ProvisioningStyle = Manual; + SystemCapabilities = { + com.apple.Keychain = { + enabled = 1; + }; + }; + }; + A0A71E771CBC7D74002C5C88 = { + CreatedOnToolsVersion = 7.3; + LastSwiftMigration = 0800; + }; + A0A71E811CBC7D74002C5C88 = { + CreatedOnToolsVersion = 7.3; + LastSwiftMigration = 0800; + TestTargetID = A051E37A1DBA5DA3004293E9; + }; + }; + }; + buildConfigurationList = A0A71E721CBC7D74002C5C88 /* Build configuration list for PBXProject "SwCrypt" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = A0A71E6E1CBC7D74002C5C88; + productRefGroup = A0A71E791CBC7D74002C5C88 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A0A71E771CBC7D74002C5C88 /* SwCrypt */, + A0A71E811CBC7D74002C5C88 /* SwCryptTests */, + A051E37A1DBA5DA3004293E9 /* dummyTestApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + A051E3791DBA5DA3004293E9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A051E3881DBA5DA4004293E9 /* LaunchScreen.storyboard in Resources */, + A051E3851DBA5DA4004293E9 /* Assets.xcassets in Resources */, + A051E3831DBA5DA4004293E9 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A0A71E761CBC7D74002C5C88 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A0A71E801CBC7D74002C5C88 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + A051E3771DBA5DA3004293E9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A051E3801DBA5DA4004293E9 /* ViewController.swift in Sources */, + A051E37E1DBA5DA4004293E9 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A0A71E731CBC7D74002C5C88 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A0A71E931CBC7DB4002C5C88 /* SwCrypt.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A0A71E7E1CBC7D74002C5C88 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A0A71E881CBC7D74002C5C88 /* SwCryptTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + A051E38E1DBA5DAA004293E9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = A051E37A1DBA5DA3004293E9 /* dummyTestApp */; + targetProxy = A051E38D1DBA5DAA004293E9 /* PBXContainerItemProxy */; + }; + A0A71E851CBC7D74002C5C88 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = A0A71E771CBC7D74002C5C88 /* SwCrypt */; + targetProxy = A0A71E841CBC7D74002C5C88 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + A051E3811DBA5DA4004293E9 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + A051E3821DBA5DA4004293E9 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + A051E3861DBA5DA4004293E9 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + A051E3871DBA5DA4004293E9 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + A051E38A1DBA5DA4004293E9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_ENTITLEMENTS = dummyTestApp/dummyTestApp.entitlements; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = dummyTestApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = hu.irl.dummyTestApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + A051E38B1DBA5DA4004293E9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_ENTITLEMENTS = dummyTestApp/dummyTestApp.entitlements; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = dummyTestApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = hu.irl.dummyTestApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; + A0A71E8A1CBC7D74002C5C88 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + A0A71E8B1CBC7D74002C5C88 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + A0A71E8D1CBC7D74002C5C88 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = SwCrypt/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = hu.irl.SwCrypt; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + A0A71E8E1CBC7D74002C5C88 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = SwCrypt/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = hu.irl.SwCrypt; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; + A0A71E901CBC7D74002C5C88 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = SwCryptTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = hu.irl.SwCryptTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/dummyTestApp.app/dummyTestApp"; + }; + name = Debug; + }; + A0A71E911CBC7D74002C5C88 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = SwCryptTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = hu.irl.SwCryptTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/dummyTestApp.app/dummyTestApp"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + A051E38C1DBA5DA4004293E9 /* Build configuration list for PBXNativeTarget "dummyTestApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A051E38A1DBA5DA4004293E9 /* Debug */, + A051E38B1DBA5DA4004293E9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; + A0A71E721CBC7D74002C5C88 /* Build configuration list for PBXProject "SwCrypt" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A0A71E8A1CBC7D74002C5C88 /* Debug */, + A0A71E8B1CBC7D74002C5C88 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A0A71E8C1CBC7D74002C5C88 /* Build configuration list for PBXNativeTarget "SwCrypt" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A0A71E8D1CBC7D74002C5C88 /* Debug */, + A0A71E8E1CBC7D74002C5C88 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A0A71E8F1CBC7D74002C5C88 /* Build configuration list for PBXNativeTarget "SwCryptTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A0A71E901CBC7D74002C5C88 /* Debug */, + A0A71E911CBC7D74002C5C88 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = A0A71E6F1CBC7D74002C5C88 /* Project object */; +} diff --git a/Carthage/Checkouts/SwCrypt/SwCrypt.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Carthage/Checkouts/SwCrypt/SwCrypt.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..07304f9 --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/SwCrypt.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Carthage/Checkouts/SwCrypt/SwCrypt.xcodeproj/xcshareddata/xcschemes/SwCrypt.xcscheme b/Carthage/Checkouts/SwCrypt/SwCrypt.xcodeproj/xcshareddata/xcschemes/SwCrypt.xcscheme new file mode 100644 index 0000000..d2cb95f --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/SwCrypt.xcodeproj/xcshareddata/xcschemes/SwCrypt.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Carthage/Checkouts/SwCrypt/SwCrypt/Info.plist b/Carthage/Checkouts/SwCrypt/SwCrypt/Info.plist new file mode 100644 index 0000000..d3de8ee --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/SwCrypt/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/Carthage/Checkouts/SwCrypt/SwCrypt/SwCrypt.h b/Carthage/Checkouts/SwCrypt/SwCrypt/SwCrypt.h new file mode 100644 index 0000000..039f09e --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/SwCrypt/SwCrypt.h @@ -0,0 +1,11 @@ +#import + +//! Project version number for SwCrypt. +FOUNDATION_EXPORT double SwCryptVersionNumber; + +//! Project version string for SwCrypt. +FOUNDATION_EXPORT const unsigned char SwCryptVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Carthage/Checkouts/SwCrypt/SwCrypt/SwCrypt.swift b/Carthage/Checkouts/SwCrypt/SwCrypt/SwCrypt.swift new file mode 100644 index 0000000..b8d570a --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/SwCrypt/SwCrypt.swift @@ -0,0 +1,2117 @@ +import Foundation + +open class SwKeyStore { + + public enum SecError: OSStatus, Error { + case unimplemented = -4 + case param = -50 + case allocate = -108 + case notAvailable = -25291 + case authFailed = -25293 + case duplicateItem = -25299 + case itemNotFound = -25300 + case interactionNotAllowed = -25308 + case decode = -26275 + case missingEntitlement = -34018 + + public static var debugLevel = 1 + + init(_ status: OSStatus, function: String = #function, file: String = #file, line: Int = #line) { + self = SecError(rawValue: status)! + if SecError.debugLevel > 0 { + print("\(file):\(line): [\(function)] \(self._domain): \(self) (\(self.rawValue))") + } + } + init(_ type: SecError, function: String = #function, file: String = #file, line: Int = #line) { + self = type + if SecError.debugLevel > 0 { + print("\(file):\(line): [\(function)] \(self._domain): \(self) (\(self.rawValue))") + } + } + } + + open static func upsertKey(_ pemKey: String, keyTag: String, + options: [NSString : AnyObject] = [:]) throws { + let pemKeyAsData = pemKey.data(using: String.Encoding.utf8)! + + var parameters: [NSString : AnyObject] = [ + kSecClass: kSecClassKey, + kSecAttrKeyType: kSecAttrKeyTypeRSA, + kSecAttrIsPermanent: true as AnyObject, + kSecAttrApplicationTag: keyTag as AnyObject, + kSecValueData: pemKeyAsData as AnyObject + ] + options.forEach { k, v in + parameters[k] = v + } + + var status = SecItemAdd(parameters as CFDictionary, nil) + if status == errSecDuplicateItem { + try delKey(keyTag) + status = SecItemAdd(parameters as CFDictionary, nil) + } + guard status == errSecSuccess else { throw SecError(status) } + } + + open static func getKey(_ keyTag: String) throws -> String { + let parameters: [NSString : AnyObject] = [ + kSecClass : kSecClassKey, + kSecAttrKeyType : kSecAttrKeyTypeRSA, + kSecAttrApplicationTag : keyTag as AnyObject, + kSecReturnData : true as AnyObject + ] + var data: AnyObject? + let status = SecItemCopyMatching(parameters as CFDictionary, &data) + guard status == errSecSuccess else { throw SecError(status) } + + guard let pemKeyAsData = data as? Data else { + throw SecError(.decode) + } + guard let result = String(data: pemKeyAsData, encoding: String.Encoding.utf8) else { + throw SecError(.decode) + } + return result + } + + open static func delKey(_ keyTag: String) throws { + let parameters: [NSString : AnyObject] = [ + kSecClass : kSecClassKey, + kSecAttrApplicationTag: keyTag as AnyObject + ] + let status = SecItemDelete(parameters as CFDictionary) + guard status == errSecSuccess else { throw SecError(status) } + } +} + +open class SwKeyConvert { + + public enum SwError: Error { + case invalidKey + case badPassphrase + case keyNotEncrypted + + public static var debugLevel = 1 + + init(_ type: SwError, function: String = #function, file: String = #file, line: Int = #line) { + self = type + if SwError.debugLevel > 0 { + print("\(file):\(line): [\(function)] \(self._domain): \(self)") + } + } + } + + open class PrivateKey { + + open static func pemToPKCS1DER(_ pemKey: String) throws -> Data { + guard let derKey = try? PEM.PrivateKey.toDER(pemKey) else { + throw SwError(.invalidKey) + } + guard let pkcs1DERKey = PKCS8.PrivateKey.stripHeaderIfAny(derKey) else { + throw SwError(.invalidKey) + } + return pkcs1DERKey + } + + open static func derToPKCS1PEM(_ derKey: Data) -> String { + return PEM.PrivateKey.toPEM(derKey) + } + + public typealias EncMode = PEM.EncryptedPrivateKey.EncMode + + open static func encryptPEM(_ pemKey: String, passphrase: String, + mode: EncMode) throws -> String { + do { + let derKey = try PEM.PrivateKey.toDER(pemKey) + return PEM.EncryptedPrivateKey.toPEM(derKey, passphrase: passphrase, mode: mode) + } catch { + throw SwError(.invalidKey) + } + } + + open static func decryptPEM(_ pemKey: String, passphrase: String) throws -> String { + do { + let derKey = try PEM.EncryptedPrivateKey.toDER(pemKey, passphrase: passphrase) + return PEM.PrivateKey.toPEM(derKey) + } catch PEM.SwError.badPassphrase { + throw SwError(.badPassphrase) + } catch PEM.SwError.keyNotEncrypted { + throw SwError(.keyNotEncrypted) + } catch { + throw SwError(.invalidKey) + } + } + } + + open class PublicKey { + + open static func pemToPKCS1DER(_ pemKey: String) throws -> Data { + guard let derKey = try? PEM.PublicKey.toDER(pemKey) else { + throw SwError(.invalidKey) + } + guard let pkcs1DERKey = PKCS8.PublicKey.stripHeaderIfAny(derKey) else { + throw SwError(.invalidKey) + } + return pkcs1DERKey + } + + open static func derToPKCS1PEM(_ derKey: Data) -> String { + return PEM.PublicKey.toPEM(derKey) + } + + open static func derToPKCS8PEM(_ derKey: Data) -> String { + let pkcs8Key = PKCS8.PublicKey.addHeader(derKey) + return PEM.PublicKey.toPEM(pkcs8Key) + } + + } + +} + +open class PKCS8 { + + open class PrivateKey { + + //https://lapo.it/asn1js/ + open static func getPKCS1DEROffset(_ derKey: Data) -> Int? { + let bytes = derKey.bytesView + + var offset = 0 + guard bytes.length > offset else { return nil } + guard bytes[offset] == 0x30 else { return nil } + + offset += 1 + + guard bytes.length > offset else { return nil } + if bytes[offset] > 0x80 { + offset += Int(bytes[offset]) - 0x80 + } + offset += 1 + + guard bytes.length > offset else { return nil } + guard bytes[offset] == 0x02 else { return nil } + + offset += 3 + + //without PKCS8 header + guard bytes.length > offset else { return nil } + if bytes[offset] == 0x02 { + return 0 + } + + let OID: [UInt8] = [0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00] + + guard bytes.length > offset + OID.count else { return nil } + let slice = derKey.bytesViewRange(NSRange(location: offset, length: OID.count)) + + guard OID.elementsEqual(slice) else { return nil } + + offset += OID.count + + guard bytes.length > offset else { return nil } + guard bytes[offset] == 0x04 else { return nil } + + offset += 1 + + guard bytes.length > offset else { return nil } + if bytes[offset] > 0x80 { + offset += Int(bytes[offset]) - 0x80 + } + offset += 1 + + guard bytes.length > offset else { return nil } + guard bytes[offset] == 0x30 else { return nil } + + return offset + } + + open static func stripHeaderIfAny(_ derKey: Data) -> Data? { + guard let offset = getPKCS1DEROffset(derKey) else { + return nil + } + return derKey.subdata(in: offset.. Bool { + return getPKCS1DEROffset(derKey) != nil + } + + } + + open class PublicKey { + + open static func addHeader(_ derKey: Data) -> Data { + var result = Data() + + let encodingLength: Int = encodedOctets(derKey.count + 1).count + let OID: [UInt8] = [0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00] + + var builder: [UInt8] = [] + + // ASN.1 SEQUENCE + builder.append(0x30) + + // Overall size, made of OID + bitstring encoding + actual key + let size = OID.count + 2 + encodingLength + derKey.count + let encodedSize = encodedOctets(size) + builder.append(contentsOf: encodedSize) + result.append(builder, count: builder.count) + result.append(OID, count: OID.count) + builder.removeAll(keepingCapacity: false) + + builder.append(0x03) + builder.append(contentsOf: encodedOctets(derKey.count + 1)) + builder.append(0x00) + result.append(builder, count: builder.count) + + // Actual key bytes + result.append(derKey) + + return result + } + + //https://lapo.it/asn1js/ + open static func getPKCS1DEROffset(_ derKey: Data) -> Int? { + let bytes = derKey.bytesView + + var offset = 0 + guard bytes.length > offset else { return nil } + guard bytes[offset] == 0x30 else { return nil } + + offset += 1 + + guard bytes.length > offset else { return nil } + if bytes[offset] > 0x80 { + offset += Int(bytes[offset]) - 0x80 + } + offset += 1 + + //without PKCS8 header + guard bytes.length > offset else { return nil } + if bytes[offset] == 0x02 { + return 0 + } + + let OID: [UInt8] = [0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00] + + guard bytes.length > offset + OID.count else { return nil } + let slice = derKey.bytesViewRange(NSRange(location: offset, length: OID.count)) + + guard OID.elementsEqual(slice) else { return nil } + offset += OID.count + + // Type + guard bytes.length > offset else { return nil } + guard bytes[offset] == 0x03 else { return nil } + + offset += 1 + + guard bytes.length > offset else { return nil } + if bytes[offset] > 0x80 { + offset += Int(bytes[offset]) - 0x80 + } + offset += 1 + + // Contents should be separated by a null from the header + guard bytes.length > offset else { return nil } + guard bytes[offset] == 0x00 else { return nil } + + offset += 1 + guard bytes.length > offset else { return nil } + + return offset + } + + open static func stripHeaderIfAny(_ derKey: Data) -> Data? { + guard let offset = getPKCS1DEROffset(derKey) else { + return nil + } + return derKey.subdata(in: offset.. Bool { + return getPKCS1DEROffset(derKey) != nil + } + + fileprivate static func encodedOctets(_ int: Int) -> [UInt8] { + // Short form + if int < 128 { + return [UInt8(int)] + } + + // Long form + let i = (int / 256) + 1 + var len = int + var result: [UInt8] = [UInt8(i + 0x80)] + + for _ in 0..> 8 + } + + return result + } + } +} + +open class PEM { + + public enum SwError: Error { + case parse(String) + case badPassphrase + case keyNotEncrypted + + public static var debugLevel = 1 + + init(_ type: SwError, function: String = #function, file: String = #file, line: Int = #line) { + self = type + if SwError.debugLevel > 0 { + print("\(file):\(line): [\(function)] \(self._domain): \(self)") + } + } + } + + open class PrivateKey { + + open static func toDER(_ pemKey: String) throws -> Data { + guard let strippedKey = stripHeader(pemKey) else { + throw SwError(.parse("header")) + } + guard let data = PEM.base64Decode(strippedKey) else { + throw SwError(.parse("base64decode")) + } + return data + } + + open static func toPEM(_ derKey: Data) -> String { + let base64 = PEM.base64Encode(derKey) + return addRSAHeader(base64) + } + + fileprivate static let prefix = "-----BEGIN PRIVATE KEY-----\n" + fileprivate static let suffix = "\n-----END PRIVATE KEY-----" + fileprivate static let rsaPrefix = "-----BEGIN RSA PRIVATE KEY-----\n" + fileprivate static let rsaSuffix = "\n-----END RSA PRIVATE KEY-----" + + fileprivate static func addHeader(_ base64: String) -> String { + return prefix + base64 + suffix + } + + fileprivate static func addRSAHeader(_ base64: String) -> String { + return rsaPrefix + base64 + rsaSuffix + } + + fileprivate static func stripHeader(_ pemKey: String) -> String? { + return PEM.stripHeaderFooter(pemKey, header: prefix, footer: suffix) ?? + PEM.stripHeaderFooter(pemKey, header: rsaPrefix, footer: rsaSuffix) + } + } + + open class PublicKey { + + open static func toDER(_ pemKey: String) throws -> Data { + guard let strippedKey = stripHeader(pemKey) else { + throw SwError(.parse("header")) + } + guard let data = PEM.base64Decode(strippedKey) else { + throw SwError(.parse("base64decode")) + } + return data + } + + open static func toPEM(_ derKey: Data) -> String { + let base64 = PEM.base64Encode(derKey) + return addHeader(base64) + } + + fileprivate static let pemPrefix = "-----BEGIN PUBLIC KEY-----\n" + fileprivate static let pemSuffix = "\n-----END PUBLIC KEY-----" + + fileprivate static func addHeader(_ base64: String) -> String { + return pemPrefix + base64 + pemSuffix + } + + fileprivate static func stripHeader(_ pemKey: String) -> String? { + return PEM.stripHeaderFooter(pemKey, header: pemPrefix, footer: pemSuffix) + } + } + + open class EncryptedPrivateKey { + + public enum EncMode { + case aes128CBC, aes256CBC + } + + open static func toDER(_ pemKey: String, passphrase: String) throws -> Data { + guard let strippedKey = PrivateKey.stripHeader(pemKey) else { + throw SwError(.parse("header")) + } + guard let mode = getEncMode(strippedKey) else { + throw SwError(.keyNotEncrypted) + } + guard let iv = getIV(strippedKey) else { + throw SwError(.parse("iv")) + } + let aesKey = getAESKey(mode, passphrase: passphrase, iv: iv) + let base64Data = strippedKey.substring( + from: strippedKey.index(strippedKey.startIndex, offsetBy:aesHeaderLength)) + guard let data = PEM.base64Decode(base64Data) else { + throw SwError(.parse("base64decode")) + } + guard let decrypted = try? decryptKey(data, key: aesKey, iv: iv) else { + throw SwError(.badPassphrase) + } + guard PKCS8.PrivateKey.hasCorrectHeader(decrypted) else { + throw SwError(.badPassphrase) + } + return decrypted + } + + open static func toPEM(_ derKey: Data, passphrase: String, mode: EncMode) -> String { + let iv = CC.generateRandom(16) + let aesKey = getAESKey(mode, passphrase: passphrase, iv: iv) + let encrypted = encryptKey(derKey, key: aesKey, iv: iv) + let encryptedDERKey = addEncryptHeader(encrypted, iv: iv, mode: mode) + return PrivateKey.addRSAHeader(encryptedDERKey) + } + + fileprivate static let aes128CBCInfo = "Proc-Type: 4,ENCRYPTED\nDEK-Info: AES-128-CBC," + fileprivate static let aes256CBCInfo = "Proc-Type: 4,ENCRYPTED\nDEK-Info: AES-256-CBC," + fileprivate static let aesInfoLength = aes128CBCInfo.characters.count + fileprivate static let aesIVInHexLength = 32 + fileprivate static let aesHeaderLength = aesInfoLength + aesIVInHexLength + + fileprivate static func addEncryptHeader(_ key: Data, iv: Data, mode: EncMode) -> String { + return getHeader(mode) + iv.hexadecimalString() + "\n\n" + PEM.base64Encode(key) + } + + fileprivate static func getHeader(_ mode: EncMode) -> String { + switch mode { + case .aes128CBC: return aes128CBCInfo + case .aes256CBC: return aes256CBCInfo + } + } + + fileprivate static func getEncMode(_ strippedKey: String) -> EncMode? { + if strippedKey.hasPrefix(aes128CBCInfo) { + return .aes128CBC + } + if strippedKey.hasPrefix(aes256CBCInfo) { + return .aes256CBC + } + return nil + } + + fileprivate static func getIV(_ strippedKey: String) -> Data? { + let ivInHex = strippedKey.substring( + with: strippedKey.index(strippedKey.startIndex, + offsetBy:aesInfoLength) ..< strippedKey.index(strippedKey.startIndex, + offsetBy:aesHeaderLength)) + return ivInHex.dataFromHexadecimalString() + } + + fileprivate static func getAESKey(_ mode: EncMode, passphrase: String, iv: Data) -> Data { + switch mode { + case .aes128CBC: return getAES128Key(passphrase, iv: iv) + case .aes256CBC: return getAES256Key(passphrase, iv: iv) + } + } + + fileprivate static func getAES128Key(_ passphrase: String, iv: Data) -> Data { + //128bit_Key = MD5(Passphrase + Salt) + let pass = passphrase.data(using: String.Encoding.utf8)! + let salt = iv.subdata(in: 0..<8) + + var key = pass + key.append(salt) + return CC.digest(key, alg: .md5) + } + + fileprivate static func getAES256Key(_ passphrase: String, iv: Data) -> Data { + //128bit_Key = MD5(Passphrase + Salt) + //256bit_Key = 128bit_Key + MD5(128bit_Key + Passphrase + Salt) + let pass = passphrase.data(using: String.Encoding.utf8)! + let salt = iv.subdata(in: 0 ..< 8) + + var first = pass + first.append(salt) + let aes128Key = CC.digest(first, alg: .md5) + + var sec = aes128Key + sec.append(pass) + sec.append(salt) + + var aes256Key = aes128Key + aes256Key.append(CC.digest(sec, alg: .md5)) + return aes256Key + } + + fileprivate static func encryptKey(_ data: Data, key: Data, iv: Data) -> Data { + return try! CC.crypt( + .encrypt, blockMode: .cbc, algorithm: .aes, padding: .pkcs7Padding, + data: data, key: key, iv: iv) + } + + fileprivate static func decryptKey(_ data: Data, key: Data, iv: Data) throws -> Data { + return try CC.crypt( + .decrypt, blockMode: .cbc, algorithm: .aes, padding: .pkcs7Padding, + data: data, key: key, iv: iv) + } + + } + + fileprivate static func stripHeaderFooter(_ data: String, header: String, footer: String) -> String? { + guard data.hasPrefix(header) else { + return nil + } + guard let r = data.range(of: footer) else { + return nil + } + return data.substring(with: header.endIndex.. Data? { + return Data(base64Encoded: base64Data, options: [.ignoreUnknownCharacters]) + } + + fileprivate static func base64Encode(_ key: Data) -> String { + return key.base64EncodedString( + options: [.lineLength64Characters, .endLineWithLineFeed]) + } + +} + +open class CC { + + public typealias CCCryptorStatus = Int32 + public enum CCError: CCCryptorStatus, Error { + case paramError = -4300 + case bufferTooSmall = -4301 + case memoryFailure = -4302 + case alignmentError = -4303 + case decodeError = -4304 + case unimplemented = -4305 + case overflow = -4306 + case rngFailure = -4307 + + public static var debugLevel = 1 + + init(_ status: CCCryptorStatus, function: String = #function, + file: String = #file, line: Int = #line) { + self = CCError(rawValue: status)! + if CCError.debugLevel > 0 { + print("\(file):\(line): [\(function)] \(self._domain): \(self) (\(self.rawValue))") + } + } + init(_ type: CCError, function: String = #function, file: String = #file, line: Int = #line) { + self = type + if CCError.debugLevel > 0 { + print("\(file):\(line): [\(function)] \(self._domain): \(self) (\(self.rawValue))") + } + } + } + + open static func generateRandom(_ size: Int) -> Data { + var data = Data(count: size) + data.withUnsafeMutableBytes { (dataBytes: UnsafeMutablePointer) -> Void in + _ = CCRandomGenerateBytes!(dataBytes, size) + } + return data + } + + public typealias CCDigestAlgorithm = UInt32 + public enum DigestAlgorithm: CCDigestAlgorithm { + case none = 0 + case md5 = 3 + case rmd128 = 4, rmd160 = 5, rmd256 = 6, rmd320 = 7 + case sha1 = 8 + case sha224 = 9, sha256 = 10, sha384 = 11, sha512 = 12 + + var length: Int { + return CCDigestGetOutputSize!(self.rawValue) + } + } + + open static func digest(_ data: Data, alg: DigestAlgorithm) -> Data { + var output = Data(count: alg.length) + output.withUnsafeMutableBytes { (outputBytes: UnsafeMutablePointer) -> Void in + _ = CCDigest!(alg.rawValue, + (data as NSData).bytes, + data.count, + outputBytes) + } + return output + } + + public typealias CCHmacAlgorithm = UInt32 + public enum HMACAlg: CCHmacAlgorithm { + case sha1, md5, sha256, sha384, sha512, sha224 + + var digestLength: Int { + switch self { + case .sha1: return 20 + case .md5: return 16 + case .sha256: return 32 + case .sha384: return 48 + case .sha512: return 64 + case .sha224: return 28 + } + } + } + + open static func HMAC(_ data: Data, alg: HMACAlg, key: Data) -> Data { + var buffer = Data(count: alg.digestLength) + buffer.withUnsafeMutableBytes { (bufferBytes: UnsafeMutablePointer) -> Void in + CCHmac!(alg.rawValue, + (key as NSData).bytes, key.count, + (data as NSData).bytes, data.count, + bufferBytes) + } + return buffer + } + + public typealias CCOperation = UInt32 + public enum OpMode: CCOperation { + case encrypt = 0, decrypt + } + + public typealias CCMode = UInt32 + public enum BlockMode: CCMode { + case ecb = 1, cbc, cfb, ctr, f8, lrw, ofb, xts, rc4, cfb8 + var needIV: Bool { + switch self { + case .cbc, .cfb, .ctr, .ofb, .cfb8: return true + default: return false + } + } + } + + public enum AuthBlockMode: CCMode { + case gcm = 11, ccm + } + + public typealias CCAlgorithm = UInt32 + public enum Algorithm: CCAlgorithm { + case aes = 0, des, threeDES, cast, rc4, rc2, blowfish + + var blockSize: Int? { + switch self { + case .aes: return 16 + case .des: return 8 + case .threeDES: return 8 + case .cast: return 8 + case .rc2: return 8 + case .blowfish: return 8 + default: return nil + } + } + } + + public typealias CCPadding = UInt32 + public enum Padding: CCPadding { + case noPadding = 0, pkcs7Padding + } + + open static func crypt(_ opMode: OpMode, blockMode: BlockMode, + algorithm: Algorithm, padding: Padding, + data: Data, key: Data, iv: Data) throws -> Data { + if blockMode.needIV { + guard iv.count == algorithm.blockSize else { throw CCError(.paramError) } + } + + var cryptor: CCCryptorRef? = nil + var status = CCCryptorCreateWithMode!( + opMode.rawValue, blockMode.rawValue, + algorithm.rawValue, padding.rawValue, + (iv as NSData).bytes, (key as NSData).bytes, key.count, + nil, 0, 0, + CCModeOptions(), &cryptor) + guard status == noErr else { throw CCError(status) } + + defer { _ = CCCryptorRelease!(cryptor!) } + + let needed = CCCryptorGetOutputLength!(cryptor!, data.count, true) + var result = Data(count: needed) + var updateLen: size_t = 0 + status = result.withUnsafeMutableBytes({ (resultBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCCryptorUpdate!( + cryptor!, + (data as NSData).bytes, data.count, + resultBytes, result.count, + &updateLen) + }) + guard status == noErr else { throw CCError(status) } + + + var finalLen: size_t = 0 + status = result.withUnsafeMutableBytes({ (resultBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCCryptorFinal!( + cryptor!, + resultBytes + updateLen, + result.count - updateLen, + &finalLen) + }) + guard status == noErr else { throw CCError(status) } + + + result.count = updateLen + finalLen + return result + } + + //The same behaviour as in the CCM pdf + //http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf + open static func cryptAuth(_ opMode: OpMode, blockMode: AuthBlockMode, algorithm: Algorithm, + data: Data, aData: Data, + key: Data, iv: Data, tagLength: Int) throws -> Data { + let cryptFun = blockMode == .gcm ? GCM.crypt : CCM.crypt + if opMode == .encrypt { + let (cipher, tag) = try cryptFun(opMode, algorithm, data, + key, iv, aData, tagLength) + var result = cipher + result.append(tag) + return result + } else { + let cipher = data.subdata(in: 0..<(data.count - tagLength)) + let tag = data.subdata( + in: (data.count - tagLength).. Bool { + return CCDigest != nil && + CCDigestGetOutputSize != nil + } + + open static func randomAvailable() -> Bool { + return CCRandomGenerateBytes != nil + } + + open static func hmacAvailable() -> Bool { + return CCHmac != nil + } + + open static func cryptorAvailable() -> Bool { + return CCCryptorCreateWithMode != nil && + CCCryptorGetOutputLength != nil && + CCCryptorUpdate != nil && + CCCryptorFinal != nil && + CCCryptorRelease != nil + } + + open static func available() -> Bool { + return digestAvailable() && + randomAvailable() && + hmacAvailable() && + cryptorAvailable() && + KeyDerivation.available() && + KeyWrap.available() && + RSA.available() && + DH.available() && + EC.available() && + CRC.available() && + CMAC.available() && + GCM.available() && + CCM.available() + } + + fileprivate typealias CCCryptorRef = UnsafeRawPointer + fileprivate typealias CCRNGStatus = CCCryptorStatus + fileprivate typealias CC_LONG = UInt32 + fileprivate typealias CCModeOptions = UInt32 + + fileprivate typealias CCRandomGenerateBytesT = @convention(c) ( + _ bytes: UnsafeMutableRawPointer, + _ count: size_t) -> CCRNGStatus + fileprivate typealias CCDigestGetOutputSizeT = @convention(c) ( + _ algorithm: CCDigestAlgorithm) -> size_t + fileprivate typealias CCDigestT = @convention(c) ( + _ algorithm: CCDigestAlgorithm, + _ data: UnsafeRawPointer, + _ dataLen: size_t, + _ output: UnsafeMutableRawPointer) -> CInt + + fileprivate typealias CCHmacT = @convention(c) ( + _ algorithm: CCHmacAlgorithm, + _ key: UnsafeRawPointer, + _ keyLength: Int, + _ data: UnsafeRawPointer, + _ dataLength: Int, + _ macOut: UnsafeMutableRawPointer) -> Void + fileprivate typealias CCCryptorCreateWithModeT = @convention(c)( + _ op: CCOperation, + _ mode: CCMode, + _ alg: CCAlgorithm, + _ padding: CCPadding, + _ iv: UnsafeRawPointer?, + _ key: UnsafeRawPointer, _ keyLength: Int, + _ tweak: UnsafeRawPointer?, _ tweakLength: Int, + _ numRounds: Int32, _ options: CCModeOptions, + _ cryptorRef: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate typealias CCCryptorGetOutputLengthT = @convention(c)( + _ cryptorRef: CCCryptorRef, + _ inputLength: size_t, + _ final: Bool) -> size_t + fileprivate typealias CCCryptorUpdateT = @convention(c)( + _ cryptorRef: CCCryptorRef, + _ dataIn: UnsafeRawPointer, + _ dataInLength: Int, + _ dataOut: UnsafeMutableRawPointer, + _ dataOutAvailable: Int, + _ dataOutMoved: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate typealias CCCryptorFinalT = @convention(c)( + _ cryptorRef: CCCryptorRef, + _ dataOut: UnsafeMutableRawPointer, + _ dataOutAvailable: Int, + _ dataOutMoved: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate typealias CCCryptorReleaseT = @convention(c) + (_ cryptorRef: CCCryptorRef) -> CCCryptorStatus + + + fileprivate static let dl = dlopen("/usr/lib/system/libcommonCrypto.dylib", RTLD_NOW) + fileprivate static let CCRandomGenerateBytes: CCRandomGenerateBytesT? = + getFunc(dl!, f: "CCRandomGenerateBytes") + fileprivate static let CCDigestGetOutputSize: CCDigestGetOutputSizeT? = + getFunc(dl!, f: "CCDigestGetOutputSize") + fileprivate static let CCDigest: CCDigestT? = getFunc(dl!, f: "CCDigest") + fileprivate static let CCHmac: CCHmacT? = getFunc(dl!, f: "CCHmac") + fileprivate static let CCCryptorCreateWithMode: CCCryptorCreateWithModeT? = + getFunc(dl!, f: "CCCryptorCreateWithMode") + fileprivate static let CCCryptorGetOutputLength: CCCryptorGetOutputLengthT? = + getFunc(dl!, f: "CCCryptorGetOutputLength") + fileprivate static let CCCryptorUpdate: CCCryptorUpdateT? = + getFunc(dl!, f: "CCCryptorUpdate") + fileprivate static let CCCryptorFinal: CCCryptorFinalT? = + getFunc(dl!, f: "CCCryptorFinal") + fileprivate static let CCCryptorRelease: CCCryptorReleaseT? = + getFunc(dl!, f: "CCCryptorRelease") + + open class GCM { + + open static func crypt(_ opMode: OpMode, algorithm: Algorithm, data: Data, + key: Data, iv: Data, + aData: Data, tagLength: Int) throws -> (Data, Data) { + var result = Data(count: data.count) + var tagLength_ = tagLength + var tag = Data(count: tagLength) + let status = result.withUnsafeMutableBytes { + (resultBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return tag.withUnsafeMutableBytes({ (tagBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCCryptorGCM!(opMode.rawValue, algorithm.rawValue, + (key as NSData).bytes, key.count, (iv as NSData).bytes, iv.count, + (aData as NSData).bytes, aData.count, + (data as NSData).bytes, data.count, + resultBytes, tagBytes, &tagLength_) + }) + } + guard status == noErr else { throw CCError(status) } + + tag.count = tagLength_ + return (result, tag) + } + + open static func available() -> Bool { + if CCCryptorGCM != nil { + return true + } + return false + } + + fileprivate typealias CCCryptorGCMT = @convention(c) (_ op: CCOperation, _ alg: CCAlgorithm, + _ key: UnsafeRawPointer, _ keyLength: Int, + _ iv: UnsafeRawPointer, _ ivLen: Int, + _ aData: UnsafeRawPointer, _ aDataLen: Int, + _ dataIn: UnsafeRawPointer, _ dataInLength: Int, + _ dataOut: UnsafeMutableRawPointer, + _ tag: UnsafeRawPointer, _ tagLength: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate static let CCCryptorGCM: CCCryptorGCMT? = getFunc(dl!, f: "CCCryptorGCM") + + } + + open class CCM { + + open static func crypt(_ opMode: OpMode, algorithm: Algorithm, data: Data, + key: Data, iv: Data, + aData: Data, tagLength: Int) throws -> (Data, Data) { + var cryptor: CCCryptorRef? = nil + var status = CCCryptorCreateWithMode!( + opMode.rawValue, AuthBlockMode.ccm.rawValue, + algorithm.rawValue, Padding.noPadding.rawValue, + nil, (key as NSData).bytes, key.count, nil, 0, + 0, CCModeOptions(), &cryptor) + guard status == noErr else { throw CCError(status) } + defer { _ = CCCryptorRelease!(cryptor!) } + + status = CCCryptorAddParameter!(cryptor!, + Parameter.dataSize.rawValue, nil, data.count) + guard status == noErr else { throw CCError(status) } + + status = CCCryptorAddParameter!(cryptor!, + Parameter.macSize.rawValue, nil, tagLength) + guard status == noErr else { throw CCError(status) } + + status = CCCryptorAddParameter!(cryptor!, + Parameter.iv.rawValue, (iv as NSData).bytes, iv.count) + guard status == noErr else { throw CCError(status) } + + status = CCCryptorAddParameter!(cryptor!, + Parameter.authData.rawValue, (aData as NSData).bytes, aData.count) + guard status == noErr else { throw CCError(status) } + + var result = Data(count: data.count) + + var updateLen: size_t = 0 + status = result.withUnsafeMutableBytes({ (resultBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCCryptorUpdate!( + cryptor!, (data as NSData).bytes, data.count, + resultBytes, result.count, + &updateLen) + }) + guard status == noErr else { throw CCError(status) } + + var finalLen: size_t = 0 + status = result.withUnsafeMutableBytes({ (resultBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCCryptorFinal!(cryptor!, resultBytes + updateLen, + result.count - updateLen, + &finalLen) + }) + guard status == noErr else { throw CCError(status) } + + result.count = updateLen + finalLen + + var tagLength_ = tagLength + var tag = Data(count: tagLength) + status = tag.withUnsafeMutableBytes({ (tagBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCCryptorGetParameter!(cryptor!, Parameter.authTag.rawValue, + tagBytes, &tagLength_) + }) + guard status == noErr else { throw CCError(status) } + + tag.count = tagLength_ + + return (result, tag) + } + + open static func available() -> Bool { + if CCCryptorAddParameter != nil && + CCCryptorGetParameter != nil { + return true + } + return false + } + + fileprivate typealias CCParameter = UInt32 + fileprivate enum Parameter: CCParameter { + case iv, authData, macSize, dataSize, authTag + } + fileprivate typealias CCCryptorAddParameterT = @convention(c) (_ cryptorRef: CCCryptorRef, + _ parameter: CCParameter, + _ data: UnsafeRawPointer?, _ dataLength: size_t) -> CCCryptorStatus + fileprivate static let CCCryptorAddParameter: CCCryptorAddParameterT? = + getFunc(dl!, f: "CCCryptorAddParameter") + + fileprivate typealias CCCryptorGetParameterT = @convention(c) (_ cryptorRef: CCCryptorRef, + _ parameter: CCParameter, + _ data: UnsafeRawPointer, _ dataLength: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate static let CCCryptorGetParameter: CCCryptorGetParameterT? = + getFunc(dl!, f: "CCCryptorGetParameter") + } + + open class RSA { + + public typealias CCAsymmetricPadding = UInt32 + + public enum AsymmetricPadding: CCAsymmetricPadding { + case pkcs1 = 1001 + case oaep = 1002 + } + + public enum AsymmetricSAPadding: UInt32 { + case pkcs15 = 1001 + case pss = 1002 + } + + open static func generateKeyPair(_ keySize: Int = 4096) throws -> (Data, Data) { + var privateKey: CCRSACryptorRef? = nil + var publicKey: CCRSACryptorRef? = nil + let status = CCRSACryptorGeneratePair!( + keySize, + 65537, + &publicKey, + &privateKey) + guard status == noErr else { throw CCError(status) } + + defer { + CCRSACryptorRelease!(privateKey!) + CCRSACryptorRelease!(publicKey!) + } + + let privDERKey = try exportToDERKey(privateKey!) + let pubDERKey = try exportToDERKey(publicKey!) + + return (privDERKey, pubDERKey) + } + + open static func encrypt(_ data: Data, derKey: Data, tag: Data, padding: AsymmetricPadding, + digest: DigestAlgorithm) throws -> Data { + let key = try importFromDERKey(derKey) + defer { CCRSACryptorRelease!(key) } + + var bufferSize = getKeySize(key) + var buffer = Data(count: bufferSize) + + let status = buffer.withUnsafeMutableBytes { + (bufferBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCRSACryptorEncrypt!( + key, + padding.rawValue, + (data as NSData).bytes, + data.count, + bufferBytes, + &bufferSize, + (tag as NSData).bytes, tag.count, + digest.rawValue) + } + guard status == noErr else { throw CCError(status) } + + buffer.count = bufferSize + + return buffer + } + + open static func decrypt(_ data: Data, derKey: Data, tag: Data, padding: AsymmetricPadding, + digest: DigestAlgorithm) throws -> (Data, Int) { + let key = try importFromDERKey(derKey) + defer { CCRSACryptorRelease!(key) } + + let blockSize = getKeySize(key) + + var bufferSize = blockSize + var buffer = Data(count: bufferSize) + + let status = buffer.withUnsafeMutableBytes { + (bufferBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCRSACryptorDecrypt!( + key, + padding.rawValue, + (data as NSData).bytes, + bufferSize, + bufferBytes, + &bufferSize, + (tag as NSData).bytes, tag.count, + digest.rawValue) + } + guard status == noErr else { throw CCError(status) } + buffer.count = bufferSize + + return (buffer, blockSize) + } + + fileprivate static func importFromDERKey(_ derKey: Data) throws -> CCRSACryptorRef { + var key: CCRSACryptorRef? = nil + let status = CCRSACryptorImport!( + (derKey as NSData).bytes, + derKey.count, + &key) + guard status == noErr else { throw CCError(status) } + + return key! + } + + fileprivate static func exportToDERKey(_ key: CCRSACryptorRef) throws -> Data { + var derKeyLength = 8192 + var derKey = Data(count: derKeyLength) + let status = derKey.withUnsafeMutableBytes { + (derKeyBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCRSACryptorExport!(key, derKeyBytes, &derKeyLength) + } + guard status == noErr else { throw CCError(status) } + + derKey.count = derKeyLength + return derKey + } + + fileprivate static func getKeyType(_ key: CCRSACryptorRef) -> KeyType { + return KeyType(rawValue: CCRSAGetKeyType!(key))! + } + + fileprivate static func getKeySize(_ key: CCRSACryptorRef) -> Int { + return Int(CCRSAGetKeySize!(key)/8) + } + + open static func sign(_ message: Data, derKey: Data, padding: AsymmetricSAPadding, + digest: DigestAlgorithm, saltLen: Int) throws -> Data { + let key = try importFromDERKey(derKey) + defer { CCRSACryptorRelease!(key) } + guard getKeyType(key) == .privateKey else { throw CCError(.paramError) } + + let keySize = getKeySize(key) + + switch padding { + case .pkcs15: + let hash = CC.digest(message, alg: digest) + var signedDataLength = keySize + var signedData = Data(count:signedDataLength) + let status = signedData.withUnsafeMutableBytes({ + (signedDataBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCRSACryptorSign!( + key, + AsymmetricPadding.pkcs1.rawValue, + (hash as NSData).bytes, hash.count, + digest.rawValue, 0 /*unused*/, + signedDataBytes, &signedDataLength) + }) + guard status == noErr else { throw CCError(status) } + + signedData.count = signedDataLength + return signedData + case .pss: + let encMessage = try add_pss_padding( + digest, + saltLength: saltLen, + keyLength: keySize, + message: message) + return try crypt(encMessage, key: key) + } + } + + open static func verify(_ message: Data, derKey: Data, padding: AsymmetricSAPadding, + digest: DigestAlgorithm, saltLen: Int, + signedData: Data) throws -> Bool { + let key = try importFromDERKey(derKey) + defer { CCRSACryptorRelease!(key) } + guard getKeyType(key) == .publicKey else { throw CCError(.paramError) } + + let keySize = getKeySize(key) + + switch padding { + case .pkcs15: + let hash = CC.digest(message, alg: digest) + let status = CCRSACryptorVerify!( + key, + padding.rawValue, + (hash as NSData).bytes, hash.count, + digest.rawValue, 0 /*unused*/, + (signedData as NSData).bytes, signedData.count) + let kCCNotVerified: CCCryptorStatus = -4306 + if status == kCCNotVerified { + return false + } + guard status == noErr else { throw CCError(status) } + return true + case .pss: + let encoded = try crypt(signedData, key:key) + return try verify_pss_padding( + digest, + saltLength: saltLen, + keyLength: keySize, + message: message, + encMessage: encoded) + } + } + + fileprivate static func crypt(_ data: Data, key: CCRSACryptorRef) throws -> Data { + var outLength = data.count + var out = Data(count: outLength) + let status = out.withUnsafeMutableBytes { (outBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCRSACryptorCrypt!( + key, + (data as NSData).bytes, data.count, + outBytes, &outLength) + } + guard status == noErr else { throw CCError(status) } + out.count = outLength + + return out + } + + fileprivate static func mgf1(_ digest: DigestAlgorithm, + seed: Data, maskLength: Int) -> Data { + var tseed = seed + tseed.append(contentsOf: [0,0,0,0] as [UInt8]) + + var interval = maskLength / digest.length + if maskLength % digest.length != 0 { + interval += 1 + } + + func pack(_ n: Int) -> [UInt8] { + return [ + UInt8(n>>24 & 0xff), + UInt8(n>>16 & 0xff), + UInt8(n>>8 & 0xff), + UInt8(n>>0 & 0xff) + ] + } + + var mask = Data() + for counter in 0 ..< interval { + tseed.replaceSubrange((tseed.count - 4) ..< tseed.count, with: pack(counter)) + mask.append(CC.digest(tseed, alg: digest)) + } + mask.count = maskLength + return mask + } + + fileprivate static func xorData(_ data1: Data, _ data2: Data) -> Data { + precondition(data1.count == data2.count) + + var ret = Data(count: data1.count) + ret.withUnsafeMutableBytes { (r: UnsafeMutablePointer) -> Void in + let bytes1 = (data1 as NSData).bytes.bindMemory(to: UInt8.self, capacity: data1.count) + let bytes2 = (data2 as NSData).bytes.bindMemory(to: UInt8.self, capacity: data2.count) + for i in 0 ..< ret.count { + r[i] = bytes1[i] ^ bytes2[i] + } + } + return ret + } + + fileprivate static func add_pss_padding(_ digest: DigestAlgorithm, + saltLength: Int, + keyLength: Int, + message: Data) throws -> Data { + + if keyLength < 16 || saltLength < 0 { + throw CCError(.paramError) + } + + // The maximal bit size of a non-negative integer is one less than the bit + // size of the key since the first bit is used to store sign + let emBits = keyLength * 8 - 1 + var emLength = emBits / 8 + if emBits % 8 != 0 { + emLength += 1 + } + + let hash = CC.digest(message, alg: digest) + + if emLength < hash.count + saltLength + 2 { + throw CCError(.paramError) + } + + let salt = CC.generateRandom(saltLength) + + var mPrime = Data(count: 8) + mPrime.append(hash) + mPrime.append(salt) + let mPrimeHash = CC.digest(mPrime, alg: digest) + + let padding = Data(count: emLength - saltLength - hash.count - 2) + var db = padding + db.append([0x01] as [UInt8], count: 1) + db.append(salt) + let dbMask = mgf1(digest, seed: mPrimeHash, maskLength: emLength - hash.count - 1) + var maskedDB = xorData(db, dbMask) + + let zeroBits = 8 * emLength - emBits + maskedDB.withUnsafeMutableBytes { (mMaskedDb: UnsafeMutablePointer) -> Void in + mMaskedDb[0] &= UInt8(0xff >> zeroBits) + } + + var ret = maskedDB + ret.append(mPrimeHash) + ret.append([0xBC] as [UInt8], count: 1) + return ret + } + + fileprivate static func verify_pss_padding(_ digest: DigestAlgorithm, + saltLength: Int, keyLength: Int, + message: Data, encMessage: Data) throws -> Bool { + if keyLength < 16 || saltLength < 0 { + throw CCError(.paramError) + } + + guard encMessage.count > 0 else { + return false + } + + let emBits = keyLength * 8 - 1 + var emLength = emBits / 8 + if emBits % 8 != 0 { + emLength += 1 + } + + let hash = CC.digest(message, alg: digest) + + if emLength < hash.count + saltLength + 2 { + return false + } + if encMessage.bytesView[encMessage.count-1] != 0xBC { + return false + } + let zeroBits = 8 * emLength - emBits + let zeroBitsM = 8 - zeroBits + let maskedDBLength = emLength - hash.count - 1 + let maskedDB = encMessage.subdata(in: 0..> zeroBitsM != 0 { + return false + } + let mPrimeHash = encMessage.subdata(in: maskedDBLength ..< maskedDBLength + hash.count) + let dbMask = mgf1(digest, seed: mPrimeHash, maskLength: emLength - hash.count - 1) + var db = xorData(maskedDB, dbMask) + db.withUnsafeMutableBytes { (mDb: UnsafeMutablePointer) -> Void in + mDb[0] &= UInt8(0xff >> zeroBits) + } + + let zeroLength = emLength - hash.count - saltLength - 2 + let zeroString = Data(count:zeroLength) + if db.subdata(in: 0 ..< zeroLength) != zeroString { + return false + } + if db.bytesView[zeroLength] != 0x01 { + return false + } + let salt = db.subdata(in: (db.count - saltLength) ..< db.count) + var mPrime = Data(count:8) + mPrime.append(hash) + mPrime.append(salt) + let mPrimeHash2 = CC.digest(mPrime, alg: digest) + if mPrimeHash != mPrimeHash2 { + return false + } + return true + } + + + open static func available() -> Bool { + return CCRSACryptorGeneratePair != nil && + CCRSACryptorRelease != nil && + CCRSAGetKeyType != nil && + CCRSAGetKeySize != nil && + CCRSACryptorEncrypt != nil && + CCRSACryptorDecrypt != nil && + CCRSACryptorExport != nil && + CCRSACryptorImport != nil && + CCRSACryptorSign != nil && + CCRSACryptorVerify != nil && + CCRSACryptorCrypt != nil + } + + fileprivate typealias CCRSACryptorRef = UnsafeRawPointer + fileprivate typealias CCRSAKeyType = UInt32 + fileprivate enum KeyType: CCRSAKeyType { + case publicKey = 0, privateKey + case blankPublicKey = 97, blankPrivateKey + case badKey = 99 + } + + fileprivate typealias CCRSACryptorGeneratePairT = @convention(c) ( + _ keySize: Int, + _ e: UInt32, + _ publicKey: UnsafeMutablePointer, + _ privateKey: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate static let CCRSACryptorGeneratePair: CCRSACryptorGeneratePairT? = + getFunc(CC.dl!, f: "CCRSACryptorGeneratePair") + + fileprivate typealias CCRSACryptorReleaseT = @convention(c) (CCRSACryptorRef) -> Void + fileprivate static let CCRSACryptorRelease: CCRSACryptorReleaseT? = + getFunc(dl!, f: "CCRSACryptorRelease") + + fileprivate typealias CCRSAGetKeyTypeT = @convention(c) (CCRSACryptorRef) -> CCRSAKeyType + fileprivate static let CCRSAGetKeyType: CCRSAGetKeyTypeT? = getFunc(dl!, f: "CCRSAGetKeyType") + + fileprivate typealias CCRSAGetKeySizeT = @convention(c) (CCRSACryptorRef) -> Int32 + fileprivate static let CCRSAGetKeySize: CCRSAGetKeySizeT? = getFunc(dl!, f: "CCRSAGetKeySize") + + fileprivate typealias CCRSACryptorEncryptT = @convention(c) ( + _ publicKey: CCRSACryptorRef, + _ padding: CCAsymmetricPadding, + _ plainText: UnsafeRawPointer, + _ plainTextLen: Int, + _ cipherText: UnsafeMutableRawPointer, + _ cipherTextLen: UnsafeMutablePointer, + _ tagData: UnsafeRawPointer, + _ tagDataLen: Int, + _ digestType: CCDigestAlgorithm) -> CCCryptorStatus + fileprivate static let CCRSACryptorEncrypt: CCRSACryptorEncryptT? = + getFunc(dl!, f: "CCRSACryptorEncrypt") + + fileprivate typealias CCRSACryptorDecryptT = @convention (c) ( + _ privateKey: CCRSACryptorRef, + _ padding: CCAsymmetricPadding, + _ cipherText: UnsafeRawPointer, + _ cipherTextLen: Int, + _ plainText: UnsafeMutableRawPointer, + _ plainTextLen: UnsafeMutablePointer, + _ tagData: UnsafeRawPointer, + _ tagDataLen: Int, + _ digestType: CCDigestAlgorithm) -> CCCryptorStatus + fileprivate static let CCRSACryptorDecrypt: CCRSACryptorDecryptT? = + getFunc(dl!, f: "CCRSACryptorDecrypt") + + fileprivate typealias CCRSACryptorExportT = @convention(c) ( + _ key: CCRSACryptorRef, + _ out: UnsafeMutableRawPointer, + _ outLen: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate static let CCRSACryptorExport: CCRSACryptorExportT? = + getFunc(dl!, f: "CCRSACryptorExport") + + fileprivate typealias CCRSACryptorImportT = @convention(c) ( + _ keyPackage: UnsafeRawPointer, + _ keyPackageLen: Int, + _ key: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate static let CCRSACryptorImport: CCRSACryptorImportT? = + getFunc(dl!, f: "CCRSACryptorImport") + + fileprivate typealias CCRSACryptorSignT = @convention(c) ( + _ privateKey: CCRSACryptorRef, + _ padding: CCAsymmetricPadding, + _ hashToSign: UnsafeRawPointer, + _ hashSignLen: size_t, + _ digestType: CCDigestAlgorithm, + _ saltLen: size_t, + _ signedData: UnsafeMutableRawPointer, + _ signedDataLen: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate static let CCRSACryptorSign: CCRSACryptorSignT? = + getFunc(dl!, f: "CCRSACryptorSign") + + fileprivate typealias CCRSACryptorVerifyT = @convention(c) ( + _ publicKey: CCRSACryptorRef, + _ padding: CCAsymmetricPadding, + _ hash: UnsafeRawPointer, + _ hashLen: size_t, + _ digestType: CCDigestAlgorithm, + _ saltLen: size_t, + _ signedData: UnsafeRawPointer, + _ signedDataLen: size_t) -> CCCryptorStatus + fileprivate static let CCRSACryptorVerify: CCRSACryptorVerifyT? = + getFunc(dl!, f: "CCRSACryptorVerify") + + fileprivate typealias CCRSACryptorCryptT = @convention(c) ( + _ rsaKey: CCRSACryptorRef, + _ data: UnsafeRawPointer, _ dataLength: size_t, + _ out: UnsafeMutableRawPointer, + _ outLength: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate static let CCRSACryptorCrypt: CCRSACryptorCryptT? = + getFunc(dl!, f: "CCRSACryptorCrypt") + } + + open class DH { + + public enum DHParam { + case rfc3526Group5 + } + + //this is stateful in CommonCrypto too, sry + open class DH { + fileprivate var ref: CCDHRef? = nil + + public init(dhParam: DHParam) throws { + ref = CCDHCreate!(kCCDHRFC3526Group5!) + guard ref != nil else { + throw CCError(.paramError) + } + } + + open func generateKey() throws -> Data { + var outputLength = 8192 + var output = Data(count: outputLength) + let status = output.withUnsafeMutableBytes { (outputBytes: UnsafeMutablePointer) -> CInt in + return CCDHGenerateKey!(ref!, outputBytes, &outputLength) + } + output.count = outputLength + guard status != -1 else { + throw CCError(.paramError) + } + return output + } + + open func computeKey(_ peerKey: Data) throws -> Data { + var sharedKeyLength = 8192 + var sharedKey = Data(count: sharedKeyLength) + let status = sharedKey.withUnsafeMutableBytes { (sharedKeyBytes: UnsafeMutablePointer) -> CInt in + return CCDHComputeKey!( + sharedKeyBytes, &sharedKeyLength, + (peerKey as NSData).bytes, peerKey.count, + ref!) + } + sharedKey.count = sharedKeyLength + guard status == 0 else { + throw CCError(.paramError) + } + return sharedKey + } + + deinit { + if ref != nil { + CCDHRelease!(ref!) + } + } + } + + + open static func available() -> Bool { + return CCDHCreate != nil && + CCDHRelease != nil && + CCDHGenerateKey != nil && + CCDHComputeKey != nil + } + + fileprivate typealias CCDHParameters = UnsafeRawPointer + fileprivate typealias CCDHRef = UnsafeRawPointer + + fileprivate typealias kCCDHRFC3526Group5TM = UnsafePointer + fileprivate static let kCCDHRFC3526Group5M: kCCDHRFC3526Group5TM? = + getFunc(dl!, f: "kCCDHRFC3526Group5") + fileprivate static let kCCDHRFC3526Group5 = kCCDHRFC3526Group5M?.pointee + + fileprivate typealias CCDHCreateT = @convention(c) ( + _ dhParameter: CCDHParameters) -> CCDHRef + fileprivate static let CCDHCreate: CCDHCreateT? = getFunc(dl!, f: "CCDHCreate") + + fileprivate typealias CCDHReleaseT = @convention(c) ( + _ ref: CCDHRef) -> Void + fileprivate static let CCDHRelease: CCDHReleaseT? = getFunc(dl!, f: "CCDHRelease") + + fileprivate typealias CCDHGenerateKeyT = @convention(c) ( + _ ref: CCDHRef, + _ output: UnsafeMutableRawPointer, _ outputLength: UnsafeMutablePointer) -> CInt + fileprivate static let CCDHGenerateKey: CCDHGenerateKeyT? = getFunc(dl!, f: "CCDHGenerateKey") + + fileprivate typealias CCDHComputeKeyT = @convention(c) ( + _ sharedKey: UnsafeMutableRawPointer, _ sharedKeyLen: UnsafeMutablePointer, + _ peerPubKey: UnsafeRawPointer, _ peerPubKeyLen: size_t, + _ ref: CCDHRef) -> CInt + fileprivate static let CCDHComputeKey: CCDHComputeKeyT? = getFunc(dl!, f: "CCDHComputeKey") + } + + open class EC { + + open static func generateKeyPair(_ keySize: Int) throws -> (Data, Data) { + var privKey: CCECCryptorRef? = nil + var pubKey: CCECCryptorRef? = nil + let status = CCECCryptorGeneratePair!( + keySize, + &pubKey, + &privKey) + guard status == noErr else { throw CCError(status) } + + defer { + CCECCryptorRelease!(privKey!) + CCECCryptorRelease!(pubKey!) + } + + let privKeyDER = try exportKey(privKey!, format: .importKeyBinary, type: .keyPrivate) + let pubKeyDER = try exportKey(pubKey!, format: .importKeyBinary, type: .keyPublic) + return (privKeyDER, pubKeyDER) + } + + open static func signHash(_ privateKey: Data, hash: Data) throws -> Data { + let privKey = try importKey(privateKey, format: .importKeyBinary, keyType: .keyPrivate) + defer { CCECCryptorRelease!(privKey) } + + var signedDataLength = 4096 + var signedData = Data(count:signedDataLength) + let status = signedData.withUnsafeMutableBytes { + (signedDataBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCECCryptorSignHash!( + privKey, + (hash as NSData).bytes, hash.count, + signedDataBytes, &signedDataLength) + } + guard status == noErr else { throw CCError(status) } + + signedData.count = signedDataLength + return signedData + } + + open static func verifyHash(_ publicKey: Data, + hash: Data, + signedData: Data) throws -> Bool { + let pubKey = try importKey(publicKey, format: .importKeyBinary, keyType: .keyPublic) + defer { CCECCryptorRelease!(pubKey) } + + var valid: UInt32 = 0 + let status = CCECCryptorVerifyHash!( + pubKey, + (hash as NSData).bytes, hash.count, + (signedData as NSData).bytes, signedData.count, + &valid) + guard status == noErr else { throw CCError(status) } + + return valid != 0 + } + + open static func computeSharedSecret(_ privateKey: Data, + publicKey: Data) throws -> Data { + let privKey = try importKey(privateKey, format: .importKeyBinary, keyType: .keyPrivate) + let pubKey = try importKey(publicKey, format: .importKeyBinary, keyType: .keyPublic) + defer { + CCECCryptorRelease!(privKey) + CCECCryptorRelease!(pubKey) + } + + var outSize = 8192 + var result = Data(count:outSize) + let status = result.withUnsafeMutableBytes { + (resultBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCECCryptorComputeSharedSecret!(privKey, pubKey, resultBytes, &outSize) + } + guard status == noErr else { throw CCError(status) } + + result.count = outSize + return result + } + + fileprivate static func importKey(_ key: Data, format: KeyExternalFormat, + keyType: KeyType) throws -> CCECCryptorRef { + var impKey: CCECCryptorRef? = nil + let status = CCECCryptorImportKey!(format.rawValue, + (key as NSData).bytes, key.count, + keyType.rawValue, &impKey) + guard status == noErr else { throw CCError(status) } + + return impKey! + } + + fileprivate static func exportKey(_ key: CCECCryptorRef, format: KeyExternalFormat, + type: KeyType) throws -> Data { + var expKeyLength = 8192 + var expKey = Data(count:expKeyLength) + let status = expKey.withUnsafeMutableBytes { + (expKeyBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCECCryptorExportKey!( + format.rawValue, + expKeyBytes, + &expKeyLength, + type.rawValue, + key) + } + guard status == noErr else { throw CCError(status) } + + expKey.count = expKeyLength + return expKey + } + + open static func available() -> Bool { + return CCECCryptorGeneratePair != nil && + CCECCryptorImportKey != nil && + CCECCryptorExportKey != nil && + CCECCryptorRelease != nil && + CCECCryptorSignHash != nil && + CCECCryptorVerifyHash != nil && + CCECCryptorComputeSharedSecret != nil + } + + fileprivate enum KeyType: CCECKeyType { + case keyPublic = 0, keyPrivate + case blankPublicKey = 97, blankPrivateKey + case badKey = 99 + } + fileprivate typealias CCECKeyType = UInt32 + + fileprivate typealias CCECKeyExternalFormat = UInt32 + fileprivate enum KeyExternalFormat: CCECKeyExternalFormat { + case importKeyBinary = 0, importKeyDER + } + + fileprivate typealias CCECCryptorRef = UnsafeRawPointer + fileprivate typealias CCECCryptorGeneratePairT = @convention(c) ( + _ keySize: size_t , + _ publicKey: UnsafeMutablePointer, + _ privateKey: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate static let CCECCryptorGeneratePair: CCECCryptorGeneratePairT? = + getFunc(dl!, f: "CCECCryptorGeneratePair") + + fileprivate typealias CCECCryptorImportKeyT = @convention(c) ( + _ format: CCECKeyExternalFormat, + _ keyPackage: UnsafeRawPointer, _ keyPackageLen: size_t, + _ keyType: CCECKeyType, _ key: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate static let CCECCryptorImportKey: CCECCryptorImportKeyT? = + getFunc(dl!, f: "CCECCryptorImportKey") + + fileprivate typealias CCECCryptorExportKeyT = @convention(c) ( + _ format: CCECKeyExternalFormat, + _ keyPackage: UnsafeRawPointer, + _ keyPackageLen: UnsafePointer, + _ keyType: CCECKeyType, _ key: CCECCryptorRef) -> CCCryptorStatus + fileprivate static let CCECCryptorExportKey: CCECCryptorExportKeyT? = + getFunc(dl!, f: "CCECCryptorExportKey") + + fileprivate typealias CCECCryptorReleaseT = @convention(c) ( + _ key: CCECCryptorRef) -> Void + fileprivate static let CCECCryptorRelease: CCECCryptorReleaseT? = + getFunc(dl!, f: "CCECCryptorRelease") + + fileprivate typealias CCECCryptorSignHashT = @convention(c)( + _ privateKey: CCECCryptorRef, + _ hashToSign: UnsafeRawPointer, + _ hashSignLen: size_t, + _ signedData: UnsafeMutableRawPointer, + _ signedDataLen: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate static let CCECCryptorSignHash: CCECCryptorSignHashT? = + getFunc(dl!, f: "CCECCryptorSignHash") + + fileprivate typealias CCECCryptorVerifyHashT = @convention(c)( + _ publicKey: CCECCryptorRef, + _ hash: UnsafeRawPointer, _ hashLen: size_t, + _ signedData: UnsafeRawPointer, _ signedDataLen: size_t, + _ valid: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate static let CCECCryptorVerifyHash: CCECCryptorVerifyHashT? = + getFunc(dl!, f: "CCECCryptorVerifyHash") + + fileprivate typealias CCECCryptorComputeSharedSecretT = @convention(c)( + _ privateKey: CCECCryptorRef, + _ publicKey: CCECCryptorRef, + _ out: UnsafeMutableRawPointer, + _ outLen: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate static let CCECCryptorComputeSharedSecret: CCECCryptorComputeSharedSecretT? = + getFunc(dl!, f: "CCECCryptorComputeSharedSecret") + } + + open class CRC { + + public typealias CNcrc = UInt32 + public enum Mode: CNcrc { + case crc8 = 10, + crc8ICODE = 11, + crc8ITU = 12, + crc8ROHC = 13, + crc8WCDMA = 14, + crc16 = 20, + crc16CCITTTrue = 21, + crc16CCITTFalse = 22, + crc16USB = 23, + crc16XMODEM = 24, + crc16DECTR = 25, + crc16DECTX = 26, + crc16ICODE = 27, + crc16VERIFONE = 28, + crc16A = 29, + crc16B = 30, + crc16Fletcher = 31, + crc32Adler = 40, + crc32 = 41, + crc32CASTAGNOLI = 42, + crc32BZIP2 = 43, + crc32MPEG2 = 44, + crc32POSIX = 45, + crc32XFER = 46, + crc64ECMA182 = 60 + } + + open static func crc(_ input: Data, mode: Mode) throws -> UInt64 { + var result: UInt64 = 0 + let status = CNCRC!( + mode.rawValue, + (input as NSData).bytes, input.count, + &result) + guard status == noErr else { + throw CCError(status) + } + return result + } + + open static func available() -> Bool { + return CNCRC != nil + } + + fileprivate typealias CNCRCT = @convention(c) ( + _ algorithm: CNcrc, + _ input: UnsafeRawPointer, _ inputLen: size_t, + _ result: UnsafeMutablePointer) -> CCCryptorStatus + fileprivate static let CNCRC: CNCRCT? = getFunc(dl!, f: "CNCRC") + } + + open class CMAC { + + open static func AESCMAC(_ data: Data, key: Data) -> Data { + var result = Data(count: 16) + result.withUnsafeMutableBytes { (resultBytes: UnsafeMutablePointer) -> Void in + CCAESCmac!((key as NSData).bytes, + (data as NSData).bytes, data.count, + resultBytes) + } + return result + } + + open static func available() -> Bool { + return CCAESCmac != nil + } + + fileprivate typealias CCAESCmacT = @convention(c) ( + _ key: UnsafeRawPointer, + _ data: UnsafeRawPointer, _ dataLen: size_t, + _ macOut: UnsafeMutableRawPointer) -> Void + fileprivate static let CCAESCmac: CCAESCmacT? = getFunc(dl!, f: "CCAESCmac") + } + + open class KeyDerivation { + + public typealias CCPseudoRandomAlgorithm = UInt32 + public enum PRFAlg: CCPseudoRandomAlgorithm { + case sha1 = 1, sha224, sha256, sha384, sha512 + var cc: CC.HMACAlg { + switch self { + case .sha1: return .sha1 + case .sha224: return .sha224 + case .sha256: return .sha256 + case .sha384: return .sha384 + case .sha512: return .sha512 + } + } + } + + open static func PBKDF2(_ password: String, salt: Data, + prf: PRFAlg, rounds: UInt32) throws -> Data { + + var result = Data(count:prf.cc.digestLength) + let passwData = password.data(using: String.Encoding.utf8)! + let status = result.withUnsafeMutableBytes { + (passwDataBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCKeyDerivationPBKDF!(PBKDFAlgorithm.pbkdf2.rawValue, + (passwData as NSData).bytes, passwData.count, + (salt as NSData).bytes, salt.count, + prf.rawValue, rounds, + passwDataBytes, result.count) + } + guard status == noErr else { throw CCError(status) } + + return result + } + + open static func available() -> Bool { + return CCKeyDerivationPBKDF != nil + } + + fileprivate typealias CCPBKDFAlgorithm = UInt32 + fileprivate enum PBKDFAlgorithm: CCPBKDFAlgorithm { + case pbkdf2 = 2 + } + + fileprivate typealias CCKeyDerivationPBKDFT = @convention(c) ( + _ algorithm: CCPBKDFAlgorithm, + _ password: UnsafeRawPointer, _ passwordLen: size_t, + _ salt: UnsafeRawPointer, _ saltLen: size_t, + _ prf: CCPseudoRandomAlgorithm, _ rounds: uint, + _ derivedKey: UnsafeMutableRawPointer, _ derivedKeyLen: size_t) -> CCCryptorStatus + fileprivate static let CCKeyDerivationPBKDF: CCKeyDerivationPBKDFT? = + getFunc(dl!, f: "CCKeyDerivationPBKDF") + } + + open class KeyWrap { + + fileprivate static let rfc3394IVData: [UInt8] = [0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6] + open static let rfc3394IV = Data(bytes: UnsafePointer(rfc3394IVData), count:rfc3394IVData.count) + + open static func SymmetricKeyWrap(_ iv: Data, + kek: Data, + rawKey: Data) throws -> Data { + let alg = WrapAlg.aes.rawValue + var wrappedKeyLength = CCSymmetricWrappedSize!(alg, rawKey.count) + var wrappedKey = Data(count:wrappedKeyLength) + let status = wrappedKey.withUnsafeMutableBytes { + (wrappedKeyBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCSymmetricKeyWrap!( + alg, + (iv as NSData).bytes, iv.count, + (kek as NSData).bytes, kek.count, + (rawKey as NSData).bytes, rawKey.count, + wrappedKeyBytes, &wrappedKeyLength) + } + guard status == noErr else { throw CCError(status) } + + wrappedKey.count = wrappedKeyLength + return wrappedKey + } + + open static func SymmetricKeyUnwrap(_ iv: Data, + kek: Data, + wrappedKey: Data) throws -> Data { + let alg = WrapAlg.aes.rawValue + var rawKeyLength = CCSymmetricUnwrappedSize!(alg, wrappedKey.count) + var rawKey = Data(count:rawKeyLength) + let status = rawKey.withUnsafeMutableBytes { + (rawKeyBytes: UnsafeMutablePointer) -> CCCryptorStatus in + return CCSymmetricKeyUnwrap!( + alg, + (iv as NSData).bytes, iv.count, + (kek as NSData).bytes, kek.count, + (wrappedKey as NSData).bytes, wrappedKey.count, + rawKeyBytes, &rawKeyLength) + } + guard status == noErr else { throw CCError(status) } + + rawKey.count = rawKeyLength + return rawKey + } + + open static func available() -> Bool { + return CCSymmetricKeyWrap != nil && + CCSymmetricKeyUnwrap != nil && + CCSymmetricWrappedSize != nil && + CCSymmetricUnwrappedSize != nil + } + + fileprivate enum WrapAlg: CCWrappingAlgorithm { + case aes = 1 + } + fileprivate typealias CCWrappingAlgorithm = UInt32 + + fileprivate typealias CCSymmetricKeyWrapT = @convention(c) ( + _ algorithm: CCWrappingAlgorithm, + _ iv: UnsafeRawPointer, _ ivLen: size_t, + _ kek: UnsafeRawPointer, _ kekLen: size_t, + _ rawKey: UnsafeRawPointer, _ rawKeyLen: size_t, + _ wrappedKey: UnsafeMutableRawPointer, + _ wrappedKeyLen: UnsafePointer) -> CCCryptorStatus + fileprivate static let CCSymmetricKeyWrap: CCSymmetricKeyWrapT? = getFunc(dl!, f: "CCSymmetricKeyWrap") + + fileprivate typealias CCSymmetricKeyUnwrapT = @convention(c) ( + _ algorithm: CCWrappingAlgorithm, + _ iv: UnsafeRawPointer, _ ivLen: size_t, + _ kek: UnsafeRawPointer, _ kekLen: size_t, + _ wrappedKey: UnsafeRawPointer, _ wrappedKeyLen: size_t, + _ rawKey: UnsafeMutableRawPointer, + _ rawKeyLen: UnsafePointer) -> CCCryptorStatus + fileprivate static let CCSymmetricKeyUnwrap: CCSymmetricKeyUnwrapT? = + getFunc(dl!, f: "CCSymmetricKeyUnwrap") + + fileprivate typealias CCSymmetricWrappedSizeT = @convention(c) ( + _ algorithm: CCWrappingAlgorithm, + _ rawKeyLen: size_t) -> size_t + fileprivate static let CCSymmetricWrappedSize: CCSymmetricWrappedSizeT? = + getFunc(dl!, f: "CCSymmetricWrappedSize") + + fileprivate typealias CCSymmetricUnwrappedSizeT = @convention(c) ( + _ algorithm: CCWrappingAlgorithm, + _ wrappedKeyLen: size_t) -> size_t + fileprivate static let CCSymmetricUnwrappedSize: CCSymmetricUnwrappedSizeT? = + getFunc(dl!, f: "CCSymmetricUnwrappedSize") + + } + +} + +private func getFunc(_ from: UnsafeMutableRawPointer, f: String) -> T? { + let sym = dlsym(from, f) + guard sym != nil else { + return nil + } + return unsafeBitCast(sym, to: T.self) +} + +extension Data { + /// Create hexadecimal string representation of Data object. + /// + /// - returns: String representation of this Data object. + + public func hexadecimalString() -> String { + var hexstr = String() + self.withUnsafeBytes { (data: UnsafePointer) -> Void in + for i in UnsafeBufferPointer(start: data, count: count) { + hexstr += String(format: "%02X", i) + } + } + return hexstr + } + + public func arrayOfBytes() -> [UInt8] { + let count = self.count / MemoryLayout.size + var bytesArray = [UInt8](repeating: 0, count: count) + (self as NSData).getBytes(&bytesArray, length:count * MemoryLayout.size) + return bytesArray + } + + fileprivate var bytesView: BytesView { return BytesView(self) } + + fileprivate func bytesViewRange(_ range: NSRange) -> BytesView { + return BytesView(self, range: range) + } + + fileprivate struct BytesView: Collection { + // The view retains the Data. That's on purpose. + // Data doesn't retain the view, so there's no loop. + let data: Data + init(_ data: Data) { + self.data = data + self.startIndex = 0 + self.endIndex = data.count + } + + init(_ data: Data, range: NSRange ) { + self.data = data + self.startIndex = range.location + self.endIndex = range.location + range.length + } + + subscript (position: Int) -> UInt8 { + var value: UInt8 = 0 + data.withUnsafeBytes { (dataBytes: UnsafePointer) -> Void in + value = UnsafeBufferPointer(start: dataBytes, count: data.count)[position] + } + return value + } + subscript (bounds: Range) -> Data { + return data.subdata(in: bounds) + } + fileprivate func formIndex(after i: inout Int) { + i += 1 + } + fileprivate func index(after i: Int) -> Int { + return i + 1 + } + var startIndex: Int + var endIndex: Int + var length: Int { return endIndex - startIndex } + } +} + +extension String { + + /// Create Data from hexadecimal string representation + /// + /// This takes a hexadecimal representation and creates a Data object. Note, if the string has + /// any spaces, those are removed. Also if the string started with a '<' or ended with a '>', + /// those are removed, too. This does no validation of the string to ensure it's a valid + /// hexadecimal string + /// + /// The use of `strtoul` inspired by Martin R at http://stackoverflow.com/a/26284562/1271826 + /// + /// - returns: Data represented by this hexadecimal string. + /// Returns nil if string contains characters outside the 0-9 and a-f range. + + public func dataFromHexadecimalString() -> Data? { + let trimmedString = self.trimmingCharacters( + in: CharacterSet(charactersIn: "<> ")).replacingOccurrences( + of: " ", with: "") + + // make sure the cleaned up string consists solely of hex digits, + // and that we have even number of them + + let regex = try! NSRegularExpression(pattern: "^[0-9a-f]*$", options: .caseInsensitive) + + let found = regex.firstMatch(in: trimmedString, options: [], + range: NSRange(location: 0, + length: trimmedString.characters.count)) + guard found != nil && + found?.range.location != NSNotFound && + trimmedString.characters.count % 2 == 0 else { + return nil + } + + // everything ok, so now let's build Data + + var data = Data(capacity: trimmedString.characters.count / 2) + var index: String.Index? = trimmedString.startIndex + + while let i = index { + let byteString = trimmedString.substring(with: i ..< trimmedString.index(i, offsetBy: 2)) + let num = UInt8(byteString.withCString { strtoul($0, nil, 16) }) + data.append([num] as [UInt8], count: 1) + + index = trimmedString.index(i, offsetBy: 2, limitedBy: trimmedString.endIndex) + if index == trimmedString.endIndex { break } + } + + return data + } +} diff --git a/Carthage/Checkouts/SwCrypt/SwCryptTests/Info.plist b/Carthage/Checkouts/SwCrypt/SwCryptTests/Info.plist new file mode 100644 index 0000000..aec9549 --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/SwCryptTests/Info.plist @@ -0,0 +1,153 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + testPrivPEM + -----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC8JyGJuwH8j2nY +tNTIHawVEXHrFogYLbBzCLEG80dD3Jp1zudqqwdbArytGKIwKMJUfmTa+n54L5vb +z/EwqsWxc/Wct8RUSN5tpkQvK4TjWgFJzsWRLv0rv+e1gTfQn4e/Q+JXWubsFBKl +ZFMXGRznnbNgKT9TGpKuGuO6ukrY0WXbeRX4Rd6NDbhzoexA1C8WltYccij5+tGf +zyTA1B+60EgJkF6gV5FtoYNEZAQKPtswGSSmAaZLzioVUeQTt2ICFJw4lZfc0dTG +HUQIvvc2UwXn2A3mC6/rwi4Q67U+AVUQKpu+Am8NXKjcy/0wV/3WjBriLrBAXcv2 +va/lFmKLAgMBAAECggEAMmQIh2qeTZXbMz66/h10SPAzIlMWf+M8rpJVVxcwruwW +MhcHw3mqrqU9At7mER/Za+et+ze7R1T42RYH8pDKAYyc6ywMWMZrS9KL0FZHcNxa +G/pUz11WULFEzUeeOzF+masEo4Ck9/UoSUNlPXpsU1vY/pgNbaRgRGDPPONHyGlW +2eJDuajtEtPrEB2MqdKYG3yk8LXiMIS2SQZtHpWAsAhvp1s4whLroQBxyhEwwuNw +CwoI6knc1rcfrxspkaW5q2eZCMF0fWK84PdZ9rDcaSZwUJfMPCETW33C9NXVCLdD +Yw0xihRUxyXcS4tqU+DZk3X0UfuTIdMEnPS+OpfMAQKBgQDft8ZElLjHWyF9M2dv +ywvaICmRmPF32sW5eEfaR/J8bR1ZOAUI5/dZv/rpm7X8I2dWn5qQLB+ka//AkHia +KXRYRGsdWk8YIZI/1VFxpXkzK/f+mtM7kv2KJVBhhgI0siMT4miNirlQHzcbmouJ +/xwXuyCsg1rNDtflqJ8FK2DR6wKBgQDXTZIuj3jsUWmUGogCiWVbhlfKvITZokHU +gw4V5Gx//B833UmGTJ/1lEV671h2OShzyNJMxTgDu5O+mxfv4hq8A6cY4hbu1Fii +p7EPF0V8t+/GyjeB/XMzl/0jyGRXQxzaWSe8Lv/cfM9j7tQVY/G58EEULR9sN0R2 +wfcQtw3p4QKBgGHmIt5KEp4ys/H896vFN/eJEYfEXQ6s7s+d4huUVnm6qhgr2pAu +KmDdESj/WeDvgT4388RZerNSC4Yx8oTL1Tz3G8Spi2ks77n9WHmaBvKssAZ7rCoq +xcaZU5aJtRdoSM9fyY7/AN8d+dibhaqqt5lu6vpzNN39O98lLgluFR1nAoGAGAV/ +mdJIG5W5wdxz8FSECoIiqWv/JokD70HwAGFL+buXgBQgb+t8rVmtptmtdQNLkB+H +1yjp5wC2qz2CnjEL6o49xnjzNhJbEUrEZnqiNhgPmI5XQxmUEN2UULm6+EF0pqfr +1wMnaOJEAVJUN06/WY+Es0uVhe1kphteBW9nDgECgYBp9auG5WYcjkt43KCBZxSn +PIEo0wnUVl0rQaMt69/bfulwX/zN4M4tdhqjY//LsNMWc2e8F/OJtghRmGzHkCYL +ILd1QNqqi0waaNvC8kdnUssO3jStEm1+6wzfyVoVB5cl8ob36vEzwwC8lLtyUjFt +rYl/vFV+gqGGJPzR7EqiGw== +-----END PRIVATE KEY----- + testPubPEM + -----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvCchibsB/I9p2LTUyB2s +FRFx6xaIGC2wcwixBvNHQ9yadc7naqsHWwK8rRiiMCjCVH5k2vp+eC+b28/xMKrF +sXP1nLfEVEjebaZELyuE41oBSc7FkS79K7/ntYE30J+Hv0PiV1rm7BQSpWRTFxkc +552zYCk/UxqSrhrjurpK2NFl23kV+EXejQ24c6HsQNQvFpbWHHIo+frRn88kwNQf +utBICZBeoFeRbaGDRGQECj7bMBkkpgGmS84qFVHkE7diAhScOJWX3NHUxh1ECL73 +NlMF59gN5guv68IuEOu1PgFVECqbvgJvDVyo3Mv9MFf91owa4i6wQF3L9r2v5RZi +iwIDAQAB +-----END PUBLIC KEY----- + testPrivEncryptedPEMAES128 + -----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,905D8CE3C9878467D460F9C0F2B5FEF3 + +vNM1pO9DlnfOEtHgeNZkLAydL1UnU2Q5tnq/x8uEovaJZCEwWOaUe+p9A2w4E/fl +57xzgwofj7jZcDIc2eAkAzwQju9RdhctO8d5CMHCfqAf7uKqeTLG1f4y2eWYvvYM +/a5ZZZ5RFfJ/yDUf2LE301L0lLCoNwZ1o3cLou+TUeC3Z4ClxL16lIyy5E+Gi4W4 +NNtNZlj2jfBIY/zlwcAO7BvQysUhJgXHTUEwzJW0cOVmow8vqBy7oijtYqkVnqHZ +g2SnL0w6pUe9gAfO869rUOdUcStOBdbnkmDV16iVglb8fE5VXCtnBQ9nN3tXpKum +n3hTEd6qyYINhKnGKd+E+1eWEHF2cfEBoN3rHe8FKrsFF3dd9R0Q3UqOQppt4lF+ +M8Gjd+GJbypAaBOrnRcIXfnVZNUYLDZ4O68qvc3ewsF/A3T7drA9riBxPUxDN41x +TtluCO7azZqn8FaY/Rfj6it3NcDW5UrM2TJPT3Gb/LtTaSqu4lD8p8VtfHxbOTyn +3tWRBFYRA86JSp7WsCQJEVgyOrTPeJgSZwUUwaSROFNWv2In3gmAX5l8wPGzwBOw +EOQLf8qT5gyt34fuc0IawkLKfGsG6lenS9NRorVjWzkdh8Aw4ooRfM67omglPtN7 +VFj2Z6mhPaVHUBYKfrnSQMhGmPQxueQhlDBPyo74SinbJ1xFD9xjvM1bT8jlfVdF +VGu0fBV1r2oY+Q3dn0z0sxVbUOJv9SlATj6kQmfShlw9cbpuUYWjCnPrM2hN37Q4 +7P0nXRSy3+N6RpGX/uAyK9yTM4R8uD7f2WeDSVBzoDd9gUeNk/5UGd++X7DYdaFK +dzi2KMOhoKioIoj1pey+dybcL60nq+92MG9OyIjW6syLRsW0oZByMZVDBAnv5tVu +RpcM4jiHWCmosguBA2t0YaeXSaEsJI7jHJU/fG1NUK6eHPO3gTaLC6B0Ru3okW+W +fgqT7VM81SzuJo1xHKoZ756cetSEzHi2IN4xQjdJ9EZQoQhBzqlcQyW5eYT+t8FA +I67alFYVzsUNYOy8fCqPvganzOESX75Zc6gOdgHb4ti9o2B0lnFjd0rkcZvfqqM4 +t/HkrJWPY5CjI/yaF/lgM1bNcRL84I8rScMePfPJPByaB+rIs6LYv2LFvyBQoWMx +oOh8fRQ0zqrgqQBtEwFNvv8xL5voAxDrtH9Tg25/BsPeulx+5HyeQFBVoEq90Tws +qa8/immZQZOqazrgGopA7vL1JWN00vzfXWdUZfXrO1PlhM7v/ploOtTRHTlUH0wO +P0mVZCUugxfuG2jgZGUB2Ckl8Wkm91S68EBesAsk9urWjxusSDTDMfkevoUCBBwB +A3X42p14L02F4mLIINUvKUb5OrcQMEABI/dgXlESA+Q3kA5o10Us/Mfogtq6JPyh +VtTciLSWf9h54ndY0pxpL0Gzhx14ptqVB9gI9vQIrdh2ArjclV7Pr2lFUt6onYEa +sfptyO+nuDF7Q2m09P6X/W9xo+R8UuGOaNYsaJzcymERkHfJx2BAbcO/NSn+cha2 +To40Cs1EUiXmX4WmONhdAEZprPF1ZWFEaZjyQ0kY7Ys9HHzEYfXOu9+7boNJ1rJG +-----END RSA PRIVATE KEY----- + testPrivDecryptedPEM + -----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAvCchibsB/I9p2LTUyB2sFRFx6xaIGC2wcwixBvNHQ9yadc7n +aqsHWwK8rRiiMCjCVH5k2vp+eC+b28/xMKrFsXP1nLfEVEjebaZELyuE41oBSc7F +kS79K7/ntYE30J+Hv0PiV1rm7BQSpWRTFxkc552zYCk/UxqSrhrjurpK2NFl23kV ++EXejQ24c6HsQNQvFpbWHHIo+frRn88kwNQfutBICZBeoFeRbaGDRGQECj7bMBkk +pgGmS84qFVHkE7diAhScOJWX3NHUxh1ECL73NlMF59gN5guv68IuEOu1PgFVECqb +vgJvDVyo3Mv9MFf91owa4i6wQF3L9r2v5RZiiwIDAQABAoIBADJkCIdqnk2V2zM+ +uv4ddEjwMyJTFn/jPK6SVVcXMK7sFjIXB8N5qq6lPQLe5hEf2Wvnrfs3u0dU+NkW +B/KQygGMnOssDFjGa0vSi9BWR3DcWhv6VM9dVlCxRM1HnjsxfpmrBKOApPf1KElD +ZT16bFNb2P6YDW2kYERgzzzjR8hpVtniQ7mo7RLT6xAdjKnSmBt8pPC14jCEtkkG +bR6VgLAIb6dbOMIS66EAccoRMMLjcAsKCOpJ3Na3H68bKZGluatnmQjBdH1ivOD3 +Wfaw3GkmcFCXzDwhE1t9wvTV1Qi3Q2MNMYoUVMcl3EuLalPg2ZN19FH7kyHTBJz0 +vjqXzAECgYEA37fGRJS4x1shfTNnb8sL2iApkZjxd9rFuXhH2kfyfG0dWTgFCOf3 +Wb/66Zu1/CNnVp+akCwfpGv/wJB4mil0WERrHVpPGCGSP9VRcaV5Myv3/prTO5L9 +iiVQYYYCNLIjE+JojYq5UB83G5qLif8cF7sgrINazQ7X5aifBStg0esCgYEA102S +Lo947FFplBqIAollW4ZXyryE2aJB1IMOFeRsf/wfN91Jhkyf9ZRFeu9Ydjkoc8jS +TMU4A7uTvpsX7+IavAOnGOIW7tRYoqexDxdFfLfvxso3gf1zM5f9I8hkV0Mc2lkn +vC7/3HzPY+7UFWPxufBBFC0fbDdEdsH3ELcN6eECgYBh5iLeShKeMrPx/PerxTf3 +iRGHxF0OrO7PneIblFZ5uqoYK9qQLipg3REo/1ng74E+N/PEWXqzUguGMfKEy9U8 +9xvEqYtpLO+5/Vh5mgbyrLAGe6wqKsXGmVOWibUXaEjPX8mO/wDfHfnYm4WqqreZ +bur6czTd/TvfJS4JbhUdZwKBgBgFf5nSSBuVucHcc/BUhAqCIqlr/yaJA+9B8ABh +S/m7l4AUIG/rfK1ZrabZrXUDS5Afh9co6ecAtqs9gp4xC+qOPcZ48zYSWxFKxGZ6 +ojYYD5iOV0MZlBDdlFC5uvhBdKan69cDJ2jiRAFSVDdOv1mPhLNLlYXtZKYbXgVv +Zw4BAoGAafWrhuVmHI5LeNyggWcUpzyBKNMJ1FZdK0GjLevf237pcF/8zeDOLXYa +o2P/y7DTFnNnvBfzibYIUZhsx5AmCyC3dUDaqotMGmjbwvJHZ1LLDt40rRJtfusM +38laFQeXJfKG9+rxM8MAvJS7clIxba2Jf7xVfoKhhiT80exKohs= +-----END RSA PRIVATE KEY----- + testPrivEncryptedPEMAES256 + -----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,CBB159E4726DD83B543567ABDA2D2FAF + +CNLqaMdh140G6f8ifml1F14JCg7rupUABXFwT/A4LSImZ77exUu7qjf36zBOZeaJ +A3vIeucb7xC1X9Hp8VWaQRAniDMBJEERf0GRoA2/o8PMz9bSAN/KLhONTpdrtBoy +af1L6xaOxzIUFJCS53jXQnWnf8dDFQYHSzIeaV6K6yQG2qB5ouJ3iguAlc2AzjSz +wLskOs2HIy0iIBaQC5pXR1UdagsXEm776ksEUNa5wd0++eXhorDJtOYQK5I8PUXs +ndAVwAd5f4pXL7CYfputPjEHOFXLcL1En5VGGlKcA9k+qEUmCJeuTQmOZM0giJOF +8lODv4qZRn4TPm1UJlvXTgl6QVRaoDnuNf++XwVIFgPEWfpr2iBJ0hBeurfJxSjB +wRBKao6MSrkb5KnZE+9Ccx+8E/qkfn/Lkw9A3UnPjS8pyGoh6SycvP5mJfC6WuXh +8O4ETgBF595RF/GreAHIK31YmUjVIebxPguYXxNcqTFPu0UMSN/QMhe6qg8kLkZ0 +ZJjVuMJ4EyAaLW9xUQi7k5ynTGvQN0uZwdndrLZPXxgMJR4kM0kN06ex6um2Wvww +fpRDANG8x9Jpp+c3dqDWVIH8afUI3T4dUGnu2wNpaxJsJGhZqu3wXMPk/23IezTX +81EONi8hEnWS9iXRGYF8bjZhPwQD8kU7Bw++VbhlW3DydifLpfoap48+nXN/vmlQ +jODNPR4E47jXPMC+t+6R5esYMaSVj0s5C/WnRrz3sjh/Py01WsenPeSnvyivrl5I +3JGMH4VJd58Ygy33gmnJ3wLfcMLinPMYq8XMqvhJMDQY/oAZo6a6EZTZNr6zdwQU +gkv13AK1jPZFczydzfGel8Ru+FD4mARQzjlBwA4akAJgaoQk4NFrOzXZyboFToK2 +4ulGCpKe3U+BZAZUotV3wXbi9bOoa6l/fH+tmribbOv51pSs+aiH60s4BAoqGA+v +41pwwROsdy/TNX1JqMeYNMRkFvFLNBQFlrwSJuwvPqeJyaV6KHzPQ3TnwuboHowk +Vw5NxhNHgjNRxzH909uJjzGiZ1EpFh2rbWlMg1QlZg5Dqhzye9k3VxmPwmKeRqPH +9TRKiKGprOzR11Ontns6y6OTxOsPMwwRUeu3KOuv4b5ZsjrSS5/6nbKBqscX83zL +DJthtC55XrENOAetatZBSzKwPd0ZeANQcoetZaV3DEm1+YVHlN+NUzLZzgqT33sC +Nt91zpJn5F8qhyw/jxpAAeyByBjL1s8+jRsRbNiybKiQOP5XO5MTxDiiSGLkzfqk +LpuF2k2fHRMJdO2YfHT1wurAlMlkVDYiv1Z5zBgTkTc0Kgryc5apAf18uSzxV70d +AlcCCHqJ7X72L6Cd0fQ80nAT+hHzs3djN3+AQrdj3hNVsO8bcELe4WlZq58WVdmJ +5sntXhLWqUAHV+ybSVb0XbDbESvw9ugydDmJgJda1eHTPfekEl1tekU63W2EDbr3 +virNS3vBL6AUZsDARR5bV3Koie1zuFuouG0IMoHC3dPkxK/lovokEr69qj37BFOF +PiMuzVzGugcz4JHCirzbpQOn1mL51FqBj7hVTdMA8u6IqN0OzESM95U6ZZTuwNgP +-----END RSA PRIVATE KEY----- + CFBundleVersion + 1 + + diff --git a/Carthage/Checkouts/SwCrypt/SwCryptTests/SwCryptTests.swift b/Carthage/Checkouts/SwCrypt/SwCryptTests/SwCryptTests.swift new file mode 100644 index 0000000..29e277a --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/SwCryptTests/SwCryptTests.swift @@ -0,0 +1,324 @@ +import XCTest +import SwCrypt + +let keyPair = try? SwCryptTest.createKeyPair(2048) + +class SwCryptTest: XCTestCase { + + override func setUp() { + super.setUp() + self.continueAfterFailure = false + } + + override func tearDown() { + super.tearDown() + } + + static func createKeyPair(_ size: Int) throws -> (Data, Data) { + return try CC.RSA.generateKeyPair(size) + } + + func testAvailable() { + XCTAssert(CC.digestAvailable()) + XCTAssert(CC.randomAvailable()) + XCTAssert(CC.hmacAvailable()) + XCTAssert(CC.cryptorAvailable()) + XCTAssert(CC.RSA.available()) + XCTAssert(CC.GCM.available()) + XCTAssert(CC.available()) + } + + func testDigest() { + XCTAssert(CC.digestAvailable()) + let testData = "rokafogtacsuka".data(using: String.Encoding.utf8)! + let sha1 = "9e421ffa8b2c83ac23e96bc9f9302f4a16311037".dataFromHexadecimalString()! + let sha256 = "ae6ab1cf65971f88b9cd92c2f334d6a99beaf5b40240d4b440fdb4a1231db0f0" + .dataFromHexadecimalString()! + let sha384 = ("acf011a346e96364091bd21415a2437273c7f3c84060b21ac19f2eafa1c6cde76467b0b0" + + "aba99626b18aa3da83e442db").dataFromHexadecimalString()! + let sha512 = ("016748fad47ddfba4fcd19aacc67ee031dfef40f5e9692c84f8846e520f2a827a4ea5035" + + "af8a66686c60796a362c30e6c473cfdbb9d86f43312001fc0b660734").dataFromHexadecimalString()! + let sha224 = "ec92519bb9e82a79097b0dd0618927b3262a70d6f02bd667c413009e" + .dataFromHexadecimalString()! + let md5 = "9b43f853613732cfc8531ed6bcbf6d68".dataFromHexadecimalString()! + XCTAssert(CC.digest(testData, alg: .sha1) == sha1) + XCTAssert(CC.digest(testData, alg: .sha256) == sha256) + XCTAssert(CC.digest(testData, alg: .sha384) == sha384) + XCTAssert(CC.digest(testData, alg: .sha512) == sha512) + XCTAssert(CC.digest(testData, alg: .sha224) == sha224) + XCTAssert(CC.digest(testData, alg: .md5) == md5) + } + + func testRandom() { + XCTAssert(CC.randomAvailable()) + _ = CC.generateRandom(10) + } + + func testCreateKeyPair() { + XCTAssert(keyPair != nil) + } + + func testUpsert() { + let (priv, _) = keyPair! + let privKey = SwKeyConvert.PrivateKey.derToPKCS1PEM(priv) + XCTAssertNotNil(try? SwKeyStore.upsertKey(privKey, keyTag: "priv", + options: [kSecAttrAccessible:kSecAttrAccessibleWhenUnlockedThisDeviceOnly])) + XCTAssertNotNil(try? SwKeyStore.upsertKey(privKey, keyTag: "priv")) + XCTAssert(try SwKeyStore.getKey("priv") == privKey) + } + + func testDel() throws { + let tag = "priv" + let (priv, _) = keyPair! + let privKey = SwKeyConvert.PrivateKey.derToPKCS1PEM(priv) + XCTAssertNotNil(try? SwKeyStore.upsertKey(privKey, keyTag: tag)) + XCTAssertNotNil(try? SwKeyStore.getKey(tag)) + XCTAssertNotNil(try? SwKeyStore.delKey(tag)) + XCTAssertNil(try? SwKeyStore.getKey(tag)) + } + + func encryptKey(_ enc: SwKeyConvert.PrivateKey.EncMode) { + let pass = "hello" + let (priv, _) = keyPair! + let privKey = SwKeyConvert.PrivateKey.derToPKCS1PEM(priv) + + let privEncrypted = try? SwKeyConvert.PrivateKey.encryptPEM(privKey, passphrase: pass, mode: enc) + XCTAssert(privEncrypted != nil) + let privDecrypted = try? SwKeyConvert.PrivateKey.decryptPEM(privEncrypted!, passphrase: pass) + XCTAssert(privDecrypted != nil) + XCTAssert(privDecrypted == privKey) + } + + func testEncryptKey() { + encryptKey(.aes128CBC) + encryptKey(.aes256CBC) + } + + func testKeyNotEncrypted() { + let bundle = Bundle(for: type(of: self)) + let decPEM = bundle.object(forInfoDictionaryKey: "testPrivDecryptedPEM") as! String + XCTAssertThrowsError(try SwKeyConvert.PrivateKey.decryptPEM(decPEM, passphrase: "hello")) { + XCTAssert($0 as? SwKeyConvert.SwError == SwKeyConvert.SwError.keyNotEncrypted) + } + } + + func testKeyInvalid() { + let bundle = Bundle(for: type(of: self)) + var decPEM = bundle.object(forInfoDictionaryKey: "testPrivDecryptedPEM") as! String + decPEM = "a" + decPEM + XCTAssertThrowsError(try SwKeyConvert.PrivateKey.decryptPEM(decPEM, passphrase: "hello")) { + XCTAssert($0 as? SwKeyConvert.SwError == SwKeyConvert.SwError.invalidKey) + } + } + + func decryptOpenSSLKeys(_ type: String) { + let bundle = Bundle(for: type(of: self)) + let encPEM = bundle.object(forInfoDictionaryKey: "testPrivEncryptedPEMAES" + type) as! String + let decPEM = bundle.object(forInfoDictionaryKey: "testPrivDecryptedPEM") as! String + let d = try? SwKeyConvert.PrivateKey.decryptPEM(encPEM, passphrase: "hello") + XCTAssert(d != nil) + XCTAssert(d! == decPEM) + } + + func decryptOpenSSLKeysBadPassphrase(_ type: String) { + let bundle = Bundle(for: type(of: self)) + let encPEM = bundle.object(forInfoDictionaryKey: "testPrivEncryptedPEMAES" + type) as! String + + XCTAssertThrowsError(try SwKeyConvert.PrivateKey.decryptPEM(encPEM, passphrase: "nohello")) { + XCTAssert($0 as? SwKeyConvert.SwError == SwKeyConvert.SwError.badPassphrase) + } + } + + func testOpenSSLKeyPair() { + let bundle = Bundle(for: type(of: self)) + let priv = bundle.object(forInfoDictionaryKey: "testPrivPEM") as! String + let pub = bundle.object(forInfoDictionaryKey: "testPubPEM") as! String + let privKey = try? SwKeyConvert.PrivateKey.pemToPKCS1DER(priv) + XCTAssert(privKey != nil) + let pubKey = try? SwKeyConvert.PublicKey.pemToPKCS1DER(pub) + XCTAssert(pubKey != nil) + } + + func testOpenSSLKeys() { + decryptOpenSSLKeys("128") + decryptOpenSSLKeys("256") + decryptOpenSSLKeysBadPassphrase("128") + decryptOpenSSLKeysBadPassphrase("256") + } + + func testEncryptDecryptOAEPSHA256() { + let (priv, pub) = keyPair! + let testData = "This is a test string".data(using: String.Encoding.utf8)! + + let e = try? CC.RSA.encrypt(testData, derKey: pub, tag: Data(), padding: .oaep, digest: .sha256) + XCTAssert(e != nil) + let d = try? CC.RSA.decrypt(e!, derKey: priv, tag: Data(), padding: .oaep, digest: .sha256) + XCTAssert(d != nil) + XCTAssert(testData == d!.0) + } + + func testEncryptDecryptGCM() { + let aesKey = CC.generateRandom(32) + let iv = CC.generateRandom(12) + let testData = "This is a test string".data(using: String.Encoding.utf8)! + + let e = try? CC.cryptAuth(.encrypt, blockMode: .gcm, algorithm: .aes, data: testData, aData: Data(), key: aesKey, iv: iv, tagLength: 8) + XCTAssert(e != nil) + let d = try? CC.cryptAuth(.decrypt, blockMode: .gcm, algorithm: .aes, data: e!, aData: Data(), key: aesKey, iv: iv, tagLength: 8) + XCTAssert(d != nil) + XCTAssert(testData == d!) + } + + func signVerify(_ privKey: Data, pubKey:Data, padding: CC.RSA.AsymmetricSAPadding) { + let testMessage = "rirararom_vagy_rararirom".data(using: String.Encoding.utf8)! + let sign = try? CC.RSA.sign(testMessage, derKey: privKey, padding: padding, + digest: .sha256, saltLen: 16) + XCTAssert(sign != nil) + let verified = try? CC.RSA.verify(testMessage, derKey: pubKey, padding: padding, + digest: .sha256, saltLen: 16, signedData: sign!) + XCTAssert(verified != nil && verified! == true) + } + + func testSignVerify() { + let (priv, pub) = keyPair! + signVerify(priv, pubKey: pub, padding: .pkcs15) + signVerify(priv, pubKey: pub, padding: .pss) + } + + func testCCM() { + let data = "hello".data(using: String.Encoding.utf8)! + let key = "8B142BB0FA0043C32821BB90A3453884".dataFromHexadecimalString()! + let iv = "B5863BD2ABBED31DC26C4EDB5A".dataFromHexadecimalString()! + let aData = "hello".data(using: String.Encoding.utf8)! + let tagLength = 16 + XCTAssert(CC.CCM.available()) + + let enc = try? CC.CCM.crypt(.encrypt, algorithm: .aes, data: data, key: key, iv: iv, + aData: aData, tagLength: tagLength) + XCTAssert(enc != nil) + let dec = try? CC.CCM.crypt(.decrypt, algorithm: .aes, data: enc!.0, key: key, iv: iv, + aData: aData, tagLength: tagLength) + XCTAssert(dec != nil) + XCTAssert(enc!.1 == dec!.1) + XCTAssert(dec!.0 == data) + } + + func testCCMSJCL() { + let data = "hello".data(using: String.Encoding.utf8)! + let key = "8B142BB0FA0043C32821BB90A3453884".dataFromHexadecimalString()! + let iv = "B5863BD2ABBED31DC26C4EDB5A".dataFromHexadecimalString()! + let aData = "hello".data(using: String.Encoding.utf8)! + let tagLength = 16 + let sjclCipher = Data(base64Encoded: "VqAna25S22M+yOZz57wCllx7Itql", options: [])! + XCTAssert(CC.CCM.available()) + + let enc = try? CC.cryptAuth(.encrypt, blockMode: .ccm, algorithm: .aes, data: data, + aData: aData, key: key, iv: iv, tagLength: tagLength) + XCTAssert(enc != nil) + XCTAssert(enc! == sjclCipher) + + let dec = try? CC.cryptAuth(.decrypt, blockMode: .ccm, algorithm: .aes, data: sjclCipher, + aData: aData, key: key, iv: iv, tagLength: tagLength) + XCTAssert(dec != nil) + XCTAssert(dec! == data) + } + + func testPBKDF2() { + let password = "password" + let salt = "salt".data(using: String.Encoding.utf8)! + + XCTAssert(CC.KeyDerivation.available()) + let stretched = try? CC.KeyDerivation.PBKDF2(password, salt: salt, prf: .sha256, rounds: 4096) + XCTAssert(stretched != nil) + let t = "c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a" + .dataFromHexadecimalString() + XCTAssert(t == stretched!) + } + + func testKeyWrap() { + let kek = "000102030405060708090A0B0C0D0E0F".dataFromHexadecimalString()! + let tkey = "00112233445566778899AABBCCDDEEFF".dataFromHexadecimalString()! + let wrappedKey = "1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5" + .dataFromHexadecimalString()! + + XCTAssert(CC.KeyWrap.available()) + let cipher = try? CC.KeyWrap.SymmetricKeyWrap(CC.KeyWrap.rfc3394IV, kek: kek, rawKey: tkey) + XCTAssert(cipher != nil) + XCTAssert(cipher! == wrappedKey) + + let key = try? CC.KeyWrap.SymmetricKeyUnwrap(CC.KeyWrap.rfc3394IV, kek: kek, wrappedKey: cipher!) + XCTAssert(key != nil) + XCTAssert(key! == tkey) + } + + func testECGenkey() { + XCTAssert(CC.EC.available()) + + let keys = try? CC.EC.generateKeyPair(384) + XCTAssert(keys != nil) + let keysTooLittle = try? CC.EC.generateKeyPair(128) + XCTAssert(keysTooLittle == nil) + } + + func testECSignVerify() { + let keys = try? CC.EC.generateKeyPair(256) + XCTAssert(keys != nil) + let hash = "c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a" + .dataFromHexadecimalString()! + + let signed = try? CC.EC.signHash(keys!.0, hash: hash) + XCTAssert(signed != nil) + let verified = try? CC.EC.verifyHash(keys!.1, hash: hash, signedData: signed!) + XCTAssert(verified == true) + } + + func testECSharedSecret() { + let keys1 = try? CC.EC.generateKeyPair(384) + XCTAssert(keys1 != nil) + let keys2 = try? CC.EC.generateKeyPair(384) + XCTAssert(keys2 != nil) + + let shared1 = try? CC.EC.computeSharedSecret(keys1!.0, publicKey: keys2!.1) + XCTAssert(shared1 != nil) + let shared2 = try? CC.EC.computeSharedSecret(keys2!.0, publicKey: keys1!.1) + XCTAssert(shared2 != nil) + XCTAssert(shared1! == shared2!) + } + + func testDH() { + XCTAssert(CC.DH.available()) + let dh1 = try? CC.DH.DH(dhParam: .rfc3526Group5) + XCTAssert(dh1 != nil) + let dh2 = try? CC.DH.DH(dhParam: .rfc3526Group5) + XCTAssert(dh2 != nil) + + let pub1 = try? dh1!.generateKey() + XCTAssert(pub1 != nil) + let pub2 = try? dh2!.generateKey() + XCTAssert(pub2 != nil) + + let common1 = try? dh1!.computeKey(pub2!) + XCTAssert(common1 != nil) + let common2 = try? dh2!.computeKey(pub1!) + XCTAssert(common2 != nil) + XCTAssert(common1 == common2) + } + + func testCRC() { + XCTAssert(CC.CRC.available()) + let input = "abcdefg".data(using: String.Encoding.utf8)! + let expectedOutput: UInt64 = 0x312A6AA6 + let output = try? CC.CRC.crc(input, mode: .crc32) + XCTAssert(output != nil) + XCTAssert(output == expectedOutput) + } + + func testCMAC() { + XCTAssert(CC.CMAC.available()) + let input = "abcdefg".data(using: String.Encoding.utf8)! + let key = "8B142BB0FA0043C32821BB90A3453884".dataFromHexadecimalString()! + let expectedOutput = "a7903c21aaa33db4c8ad7b23a947e0bd".dataFromHexadecimalString()! + let cmac = CC.CMAC.AESCMAC(input, key: key) + XCTAssert(cmac == expectedOutput) + } +} diff --git a/Carthage/Checkouts/SwCrypt/dummyTestApp/AppDelegate.swift b/Carthage/Checkouts/SwCrypt/dummyTestApp/AppDelegate.swift new file mode 100644 index 0000000..356764c --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/dummyTestApp/AppDelegate.swift @@ -0,0 +1,38 @@ +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/Carthage/Checkouts/SwCrypt/dummyTestApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/Carthage/Checkouts/SwCrypt/dummyTestApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..1d060ed --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/dummyTestApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,93 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Carthage/Checkouts/SwCrypt/dummyTestApp/Base.lproj/LaunchScreen.storyboard b/Carthage/Checkouts/SwCrypt/dummyTestApp/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..fdf3f97 --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/dummyTestApp/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Carthage/Checkouts/SwCrypt/dummyTestApp/Base.lproj/Main.storyboard b/Carthage/Checkouts/SwCrypt/dummyTestApp/Base.lproj/Main.storyboard new file mode 100644 index 0000000..273375f --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/dummyTestApp/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Carthage/Checkouts/SwCrypt/dummyTestApp/Info.plist b/Carthage/Checkouts/SwCrypt/dummyTestApp/Info.plist new file mode 100644 index 0000000..d052473 --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/dummyTestApp/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Carthage/Checkouts/SwCrypt/dummyTestApp/ViewController.swift b/Carthage/Checkouts/SwCrypt/dummyTestApp/ViewController.swift new file mode 100644 index 0000000..c2f73ba --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/dummyTestApp/ViewController.swift @@ -0,0 +1,17 @@ +import UIKit + +class ViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view, typically from a nib. + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + +} + diff --git a/Carthage/Checkouts/SwCrypt/dummyTestApp/dummyTestApp.entitlements b/Carthage/Checkouts/SwCrypt/dummyTestApp/dummyTestApp.entitlements new file mode 100644 index 0000000..00f5f01 --- /dev/null +++ b/Carthage/Checkouts/SwCrypt/dummyTestApp/dummyTestApp.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)hu.irl.dummyTestApp + + + diff --git a/JWT.xcodeproj/project.pbxproj b/JWT.xcodeproj/project.pbxproj index 4158229..1753e14 100644 --- a/JWT.xcodeproj/project.pbxproj +++ b/JWT.xcodeproj/project.pbxproj @@ -29,6 +29,10 @@ 520A71181C469F010005C709 /* Claims.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520A71141C469F010005C709 /* Claims.swift */; }; 520A71191C469F010005C709 /* Decode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520A71151C469F010005C709 /* Decode.swift */; }; 520A711A1C469F010005C709 /* JWT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520A71161C469F010005C709 /* JWT.swift */; }; + 891AF2231ECDC9B30002800C /* SwCrypt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 891AF21E1ECDC9A90002800C /* SwCrypt.framework */; }; + 891AF2241ECDC9BB0002800C /* SwCrypt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 891AF21E1ECDC9A90002800C /* SwCrypt.framework */; }; + 891AF2251ECDC9C00002800C /* SwCrypt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 891AF21E1ECDC9A90002800C /* SwCrypt.framework */; }; + 891AF2261ECDC9C70002800C /* SwCrypt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 891AF21E1ECDC9A90002800C /* SwCrypt.framework */; }; CD9B62171C7753D8005D4844 /* Claims.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520A71141C469F010005C709 /* Claims.swift */; }; CD9B62181C7753D8005D4844 /* JWT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520A71161C469F010005C709 /* JWT.swift */; }; CD9B62191C7753D8005D4844 /* Decode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 520A71151C469F010005C709 /* Decode.swift */; }; @@ -69,6 +73,27 @@ remoteGlobalIDString = 754BE46019693E190098E6F3; remoteInfo = CryptoSwiftTests; }; + 891AF21D1ECDC9A90002800C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 891AF2161ECDC9A90002800C /* SwCrypt.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = A0A71E781CBC7D74002C5C88; + remoteInfo = SwCrypt; + }; + 891AF21F1ECDC9A90002800C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 891AF2161ECDC9A90002800C /* SwCrypt.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = A0A71E821CBC7D74002C5C88; + remoteInfo = SwCryptTests; + }; + 891AF2211ECDC9A90002800C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 891AF2161ECDC9A90002800C /* SwCrypt.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = A051E37B1DBA5DA3004293E9; + remoteInfo = dummyTestApp; + }; CD9B628A1C7758CA005D4844 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 66725DA21C59202E00FC32F4 /* CryptoSwift.xcodeproj */; @@ -95,6 +120,7 @@ 520A711B1C469F440005C709 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 540942F3614C41E3827F2013 /* Pods_JWT.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_JWT.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 66725DA21C59202E00FC32F4 /* CryptoSwift.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CryptoSwift.xcodeproj; path = Carthage/Checkouts/CryptoSwift/CryptoSwift.xcodeproj; sourceTree = ""; }; + 891AF2161ECDC9A90002800C /* SwCrypt.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SwCrypt.xcodeproj; path = Carthage/Checkouts/SwCrypt/SwCrypt.xcodeproj; sourceTree = ""; }; CD9B62231C7753D8005D4844 /* JWT.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = JWT.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CD9B62351C7753EC005D4844 /* JWT.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = JWT.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CD9B62471C7753FB005D4844 /* JWT.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = JWT.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -106,6 +132,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 891AF2231ECDC9B30002800C /* SwCrypt.framework in Frameworks */, 2734C6A81D88001F00BFF9F1 /* CryptoSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -122,6 +149,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 891AF2241ECDC9BB0002800C /* SwCrypt.framework in Frameworks */, CD9B62891C7758BB005D4844 /* CryptoSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -130,6 +158,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 891AF2251ECDC9C00002800C /* SwCrypt.framework in Frameworks */, 2734C6A91D88002900BFF9F1 /* CryptoSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -138,6 +167,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 891AF2261ECDC9C70002800C /* SwCrypt.framework in Frameworks */, 2734C6AA1D88003000BFF9F1 /* CryptoSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -148,6 +178,7 @@ 279D63921AD07FFF0024E2BC = { isa = PBXGroup; children = ( + 891AF2161ECDC9A90002800C /* SwCrypt.xcodeproj */, 66725DA21C59202E00FC32F4 /* CryptoSwift.xcodeproj */, 520A711B1C469F440005C709 /* Package.swift */, 520A71121C469F010005C709 /* Sources */, @@ -231,6 +262,16 @@ name = Products; sourceTree = ""; }; + 891AF2171ECDC9A90002800C /* Products */ = { + isa = PBXGroup; + children = ( + 891AF21E1ECDC9A90002800C /* SwCrypt.framework */, + 891AF2201ECDC9A90002800C /* SwCryptTests.xctest */, + 891AF2221ECDC9A90002800C /* dummyTestApp.app */, + ); + name = Products; + sourceTree = ""; + }; AC8AE547FDAF3DD80EB4DB2F /* Frameworks */ = { isa = PBXGroup; children = ( @@ -403,6 +444,10 @@ ProductGroup = 66725DA31C59202E00FC32F4 /* Products */; ProjectRef = 66725DA21C59202E00FC32F4 /* CryptoSwift.xcodeproj */; }, + { + ProductGroup = 891AF2171ECDC9A90002800C /* Products */; + ProjectRef = 891AF2161ECDC9A90002800C /* SwCrypt.xcodeproj */; + }, ); projectRoot = ""; targets = ( @@ -430,6 +475,27 @@ remoteRef = 66725DB21C59202E00FC32F4 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 891AF21E1ECDC9A90002800C /* SwCrypt.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = SwCrypt.framework; + remoteRef = 891AF21D1ECDC9A90002800C /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 891AF2201ECDC9A90002800C /* SwCryptTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = SwCryptTests.xctest; + remoteRef = 891AF21F1ECDC9A90002800C /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 891AF2221ECDC9A90002800C /* dummyTestApp.app */ = { + isa = PBXReferenceProxy; + fileType = wrapper.application; + path = dummyTestApp.app; + remoteRef = 891AF2211ECDC9A90002800C /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ @@ -656,6 +722,7 @@ INFOPLIST_FILE = JWT/Info.plist; INSTALL_PATH = "@rpath"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = "org.cocode.$(PRODUCT_NAME:rfc1034identifier)"; SKIP_INSTALL = YES; }; @@ -673,6 +740,7 @@ INFOPLIST_FILE = JWT/Info.plist; INSTALL_PATH = "@rpath"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = "org.cocode.$(PRODUCT_NAME:rfc1034identifier)"; SKIP_INSTALL = YES; }; @@ -692,6 +760,7 @@ ); INFOPLIST_FILE = Tests/JWTTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = "org.cocode.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -707,6 +776,7 @@ ); INFOPLIST_FILE = Tests/JWTTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = "org.cocode.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -722,7 +792,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = JWT/Info.plist; INSTALL_PATH = "@rpath"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocode.$(PRODUCT_NAME:rfc1034identifier)"; SDKROOT = iphoneos; @@ -741,7 +811,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = JWT/Info.plist; INSTALL_PATH = "@rpath"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocode.$(PRODUCT_NAME:rfc1034identifier)"; SDKROOT = iphoneos; @@ -766,7 +836,7 @@ SDKROOT = appletvos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.0; + TVOS_DEPLOYMENT_TARGET = 9.2; }; name = Debug; }; @@ -785,7 +855,7 @@ SDKROOT = appletvos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.0; + TVOS_DEPLOYMENT_TARGET = 9.2; VALIDATE_PRODUCT = YES; }; name = Release; diff --git a/Sources/JWT.swift b/Sources/JWT.swift index 3b3e4a3..008ed3f 100644 --- a/Sources/JWT.swift +++ b/Sources/JWT.swift @@ -1,5 +1,6 @@ import Foundation import CryptoSwift +import SwCrypt public typealias Payload = [String: Any] @@ -17,6 +18,25 @@ public enum Algorithm: CustomStringConvertible { /// HMAC using SHA-512 hash algorithm case hs512(Data) + /// RSA PKCS#1 using SHA-256 hash algorithm + case rs256(Data) + + /// RSA PKCS#1 using SHA-384 hash algorithm + case rs384(Data) + + /// RSA PKCS#1 using SHA-512 hash algorithm + case rs512(Data) + + /// RSA PSS using SHA-256 hash algorithm + case ps256(Data) + + /// RSA PSS using SHA-384 hash algorithm + case ps384(Data) + + /// RSA PSS using SHA-512 hash algorithm + case ps512(Data) + + public var description: String { switch self { case .none: @@ -27,6 +47,18 @@ public enum Algorithm: CustomStringConvertible { return "HS384" case .hs512: return "HS512" + case .rs256: + return "RS256" + case .rs384: + return "RS384" + case .rs512: + return "RS512" + case .ps256: + return "PS256" + case .ps384: + return "PS384" + case .ps512: + return "PS512" } } @@ -43,6 +75,17 @@ public enum Algorithm: CustomStringConvertible { } return base64encode(Data(bytes: result)) } + + func signRS(_ key: Data, variant: CC.DigestAlgorithm, padding: CC.RSA.AsymmetricSAPadding) -> String { + let messageData = message.data(using: String.Encoding.utf8, allowLossyConversion: false)! + let result: Data + do { + result = try CC.RSA.sign(messageData, derKey: key, padding: padding, digest: variant, saltLen:16) + } catch { + result = Data() + } + return base64encode(result) + } switch self { case .none: @@ -56,6 +99,25 @@ public enum Algorithm: CustomStringConvertible { case .hs512(let key): return signHS(key, variant: .sha512) + + case .rs256(let key): + return signRS(key, variant: .sha256, padding: .pkcs15) + + case .rs384(let key): + return signRS(key, variant: .sha384, padding: .pkcs15) + + case .rs512(let key): + return signRS(key, variant: .sha256, padding: .pkcs15) + + case .ps256(let key): + return signRS(key, variant: .sha256, padding: .pss) + + case .ps384(let key): + return signRS(key, variant: .sha384, padding: .pss) + + case .ps512(let key): + return signRS(key, variant: .sha512, padding: .pss) + } }