From: liangzhenying <867174124@qq.com> Date: Fri, 12 Jul 2024 09:11:58 +0000 (+0800) Subject: 注册登录增加aes加解密 X-Git-Tag: deepseek~17 X-Git-Url: http://git.snicker.com.cn/Users/apple/Desktop/static/gitweb.css?a=commitdiff_plain;h=0e650d45ec3bda7b28992b86d9ffb1383491a530;p=mirrors%2FMixSdk-iOS.git 注册登录增加aes加解密 --- diff --git a/MixSdk/Example/Example.xcodeproj/project.pbxproj b/MixSdk/Example/Example.xcodeproj/project.pbxproj index ba9a837..6810cea 100644 --- a/MixSdk/Example/Example.xcodeproj/project.pbxproj +++ b/MixSdk/Example/Example.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 184FFC492C218FC4006F664D /* PlayerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184FFC482C218FC4006F664D /* PlayerManager.swift */; }; 184FFC4D2C2199E5006F664D /* SendToolBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184FFC4C2C2199E5006F664D /* SendToolBar.swift */; }; 184FFC512C21A4EB006F664D /* TtsLongViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184FFC502C21A4EB006F664D /* TtsLongViewController.swift */; }; + 18A2DB6C2C40C5CC00A8731B /* AIAudio.sbc in Resources */ = {isa = PBXBuildFile; fileRef = 18A2DB6B2C40C5CC00A8731B /* AIAudio.sbc */; }; 18ABF57A2C3F884600C60E9B /* 2.pcm in Resources */ = {isa = PBXBuildFile; fileRef = 18ABF5792C3F884600C60E9B /* 2.pcm */; }; 18B74A212C201CD600FABBA9 /* TtsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B74A202C201CD600FABBA9 /* TtsViewController.swift */; }; 18B74A242C201CE900FABBA9 /* SttViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B74A232C201CE900FABBA9 /* SttViewController.swift */; }; @@ -27,7 +28,6 @@ 18B74A282C201F2500FABBA9 /* TranslateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B74A272C201F2500FABBA9 /* TranslateViewController.swift */; }; 18B74A2A2C201FF200FABBA9 /* AigcViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B74A292C201FF200FABBA9 /* AigcViewController.swift */; }; 18C42B412C230A15000D8860 /* TtsFileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18C42B402C230A15000D8860 /* TtsFileViewController.swift */; }; - 18C42B592C24166D000D8860 /* input.msbc in Resources */ = {isa = PBXBuildFile; fileRef = 18C42B582C24166D000D8860 /* input.msbc */; }; 18C42C802C242AAC000D8860 /* liblzma.5.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 18C42C7E2C242AAC000D8860 /* liblzma.5.tbd */; }; 18C42C822C242AD3000D8860 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18C42C812C242AD3000D8860 /* CoreAudio.framework */; }; 18C42C842C242B01000D8860 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 18C42C832C242B01000D8860 /* libiconv.tbd */; }; @@ -67,6 +67,7 @@ 184FFC482C218FC4006F664D /* PlayerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerManager.swift; sourceTree = ""; }; 184FFC4C2C2199E5006F664D /* SendToolBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToolBar.swift; sourceTree = ""; }; 184FFC502C21A4EB006F664D /* TtsLongViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TtsLongViewController.swift; sourceTree = ""; }; + 18A2DB6B2C40C5CC00A8731B /* AIAudio.sbc */ = {isa = PBXFileReference; lastKnownFileType = file; path = AIAudio.sbc; sourceTree = ""; }; 18ABF5792C3F884600C60E9B /* 2.pcm */ = {isa = PBXFileReference; lastKnownFileType = file; path = 2.pcm; sourceTree = ""; }; 18B74A202C201CD600FABBA9 /* TtsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TtsViewController.swift; sourceTree = ""; }; 18B74A232C201CE900FABBA9 /* SttViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SttViewController.swift; sourceTree = ""; }; @@ -74,7 +75,6 @@ 18B74A272C201F2500FABBA9 /* TranslateViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslateViewController.swift; sourceTree = ""; }; 18B74A292C201FF200FABBA9 /* AigcViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AigcViewController.swift; sourceTree = ""; }; 18C42B402C230A15000D8860 /* TtsFileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TtsFileViewController.swift; sourceTree = ""; }; - 18C42B582C24166D000D8860 /* input.msbc */ = {isa = PBXFileReference; lastKnownFileType = file; name = input.msbc; path = ../../../../../../input.msbc; sourceTree = ""; }; 18C42C7E2C242AAC000D8860 /* liblzma.5.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = liblzma.5.tbd; path = usr/lib/liblzma.5.tbd; sourceTree = SDKROOT; }; 18C42C812C242AD3000D8860 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; 18C42C832C242B01000D8860 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; }; @@ -115,7 +115,7 @@ isa = PBXGroup; children = ( 18FF930A2C39263A0006452C /* cat.png */, - 18C42B582C24166D000D8860 /* input.msbc */, + 18A2DB6B2C40C5CC00A8731B /* AIAudio.sbc */, 181188592C1BE80B00C23FAB /* 1.pcm */, 18ABF5792C3F884600C60E9B /* 2.pcm */, 18C8561E2C3B81B800EAE874 /* tts.mp3 */, @@ -303,9 +303,9 @@ 18204BE02C193F9D00F94E69 /* LaunchScreen.storyboard in Resources */, 18C856202C3B81B900EAE874 /* tts.mp3 in Resources */, 18204BDD2C193F9D00F94E69 /* Assets.xcassets in Resources */, + 18A2DB6C2C40C5CC00A8731B /* AIAudio.sbc in Resources */, 18ABF57A2C3F884600C60E9B /* 2.pcm in Resources */, 18204BDB2C193F9A00F94E69 /* Main.storyboard in Resources */, - 18C42B592C24166D000D8860 /* input.msbc in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MixSdk/Example/Example/Resource/AIAudio.sbc b/MixSdk/Example/Example/Resource/AIAudio.sbc new file mode 100644 index 0000000..d96322c Binary files /dev/null and b/MixSdk/Example/Example/Resource/AIAudio.sbc differ diff --git a/MixSdk/Example/Example/Sbc2PcmViewController.swift b/MixSdk/Example/Example/Sbc2PcmViewController.swift index aaa396b..8cfefbc 100644 --- a/MixSdk/Example/Example/Sbc2PcmViewController.swift +++ b/MixSdk/Example/Example/Sbc2PcmViewController.swift @@ -78,7 +78,7 @@ extension Sbc2PcmViewController{ } //3 @objc func startTask(){ - guard let path = Bundle.main.path(forResource: "input", ofType: "msbc") else {return} + guard let path = Bundle.main.path(forResource: "AIAudio", ofType: "sbc") else {return} let data = readFile(from: path)! // MSAudioConvert.sbcTopcm(data){ pcm in // MixSdk.shared.stt.sendVoice(pcmData: pcm) diff --git a/MixSdk/Example/Example/TtsFileViewController.swift b/MixSdk/Example/Example/TtsFileViewController.swift index 9451ad8..616f64a 100644 --- a/MixSdk/Example/Example/TtsFileViewController.swift +++ b/MixSdk/Example/Example/TtsFileViewController.swift @@ -60,9 +60,22 @@ class SttFileViewController: UIViewController{ extension SttFileViewController{ @objc func selectFileAction() { + +// guard let sbcPath = Bundle.main.path(forResource: "AIAudio", ofType: "sbc") else {return} +// let pcmPath = NSTemporaryDirectory() + "temp.pcm" +// let mp3Path = NSTemporaryDirectory() + "temp.mp3" +// +// var result1 = MSAudioConvert.sbcToPcm(sbcPath: sbcPath, pcmPath: pcmPath) +// var result2 = MSAudioConvert.sbcToMp3(sbcPath: sbcPath, mp3Path: mp3Path) +// print(result1) +// print(pcmPath) +// +// print(result2) +// print(mp3Path) + + guard let pcmPath = Bundle.main.path(forResource: "1", ofType: "pcm") else {return} view.makeToastActivity(.center) - guard let path = Bundle.main.path(forResource: "1", ofType: "pcm") else {return} - MixSdk.shared.stt.sentenceRecognize(filePath: path,format:"pcm",sample_rate:"16000",lang: "zh-cn") {[weak self] text, error in + MixSdk.shared.stt.sentenceRecognize(filePath: pcmPath,format:"pcm",sample_rate:"16000",lang: "zh-cn") {[weak self] text, error in guard let self = self else {return} self.view.hideToastActivity() if let err = error{ diff --git a/MixSdk/Example/Example/ViewController.swift b/MixSdk/Example/Example/ViewController.swift index 529722a..05ff4b2 100644 --- a/MixSdk/Example/Example/ViewController.swift +++ b/MixSdk/Example/Example/ViewController.swift @@ -66,9 +66,9 @@ extension ViewController{ @objc func registerAction() { view.makeToastActivity(.center) let deviceId = "281106AFB0F7" - let productKey = "pafOCKLiUbeVHbdZlddH" - let secretKey = "HHosiYUNdG42t1cTgKOzIUPOvAVMI2QT" - MixSdk.shared.auth.register(productKey: productKey, secretKey: secretKey, deviceId: deviceId){[weak self] model,error in + let productId = "pafOCKLiUbeVHbdZlddH" + let productKey = "HHosiYUNdG42t1cTgKOzIUPOvAVMI2QT" + MixSdk.shared.auth.register(productId: productId, productKey: productKey, deviceId: deviceId){[weak self] password,error in self?.view.hideToastActivity() if let err = error{ print("\(err)") @@ -76,24 +76,24 @@ extension ViewController{ } else{ self?.view.makeToast("register success!") - print(model!.deviceId) - print(model!.password) - print(model!.nonce) + print(password!) } } } @objc func loginAction() { view.makeToastActivity(.center) let deviceId = "281106AFB0F7" - let password = "RDSr+qCM3zLqTLVh3L/74B4Rhh5CsBU7D3/0KZMCknM="//注册获得 - let nonce = "WFY51NbA4lUff3Jd"//注册获得 - MixSdk.shared.auth.login(deviceId: deviceId, password: password,nonce:nonce){[weak self] isSuccess,error in + let productKey = "HHosiYUNdG42t1cTgKOzIUPOvAVMI2QT" + let password = "F1xdqQL2fXjcmD4dV4To42dUUiBb2L"//注册获得的密码 + + MixSdk.shared.auth.login(productKey:productKey,deviceId: deviceId, password: password){[weak self] token,error in self?.view.hideToastActivity() if let err = error{ self?.view.makeToast(err.msg) } else{ self?.view.makeToast("Login success!") + print(token ?? "") } } } diff --git a/MixSdk/MixSdk.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate b/MixSdk/MixSdk.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate index e740a7d..6bc1705 100644 Binary files a/MixSdk/MixSdk.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate and b/MixSdk/MixSdk.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/MixSdk/MixSdk/MixSdk.xcodeproj/project.pbxproj b/MixSdk/MixSdk/MixSdk.xcodeproj/project.pbxproj index 641f674..8cff385 100644 --- a/MixSdk/MixSdk/MixSdk.xcodeproj/project.pbxproj +++ b/MixSdk/MixSdk/MixSdk.xcodeproj/project.pbxproj @@ -43,6 +43,120 @@ 18314AA32C3D37EE0028CE50 /* AudioTransform.h in Headers */ = {isa = PBXBuildFile; fileRef = 18314A9F2C3D37ED0028CE50 /* AudioTransform.h */; }; 18314AA42C3D37EE0028CE50 /* AudioTransform.m in Sources */ = {isa = PBXBuildFile; fileRef = 18314AA02C3D37ED0028CE50 /* AudioTransform.m */; }; 184FFC542C21B091006F664D /* MSAigcModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184FFC532C21B091006F664D /* MSAigcModel.swift */; }; + 18A2DCC92C40D94800A8731B /* AES.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC4F2C40D94700A8731B /* AES.swift */; }; + 18A2DCCA2C40D94800A8731B /* ASN1Scanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC512C40D94700A8731B /* ASN1Scanner.swift */; }; + 18A2DCCB2C40D94800A8731B /* ASN1Encoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC522C40D94700A8731B /* ASN1Encoder.swift */; }; + 18A2DCCC2C40D94800A8731B /* ASN1Decoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC532C40D94700A8731B /* ASN1Decoder.swift */; }; + 18A2DCCD2C40D94800A8731B /* ASN1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC542C40D94700A8731B /* ASN1.swift */; }; + 18A2DCCE2C40D94800A8731B /* StreamDecryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC552C40D94700A8731B /* StreamDecryptor.swift */; }; + 18A2DCCF2C40D94800A8731B /* DER.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC572C40D94700A8731B /* DER.swift */; }; + 18A2DCD02C40D94800A8731B /* BlockEncryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC582C40D94700A8731B /* BlockEncryptor.swift */; }; + 18A2DCD12C40D94800A8731B /* UInt64+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC592C40D94700A8731B /* UInt64+Extension.swift */; }; + 18A2DCD22C40D94800A8731B /* Cryptors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC5A2C40D94700A8731B /* Cryptors.swift */; }; + 18A2DCD32C40D94800A8731B /* HKDF.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC5B2C40D94700A8731B /* HKDF.swift */; }; + 18A2DCD42C40D94800A8731B /* ISO10126Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC5C2C40D94700A8731B /* ISO10126Padding.swift */; }; + 18A2DCD52C40D94800A8731B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC5D2C40D94700A8731B /* Cipher.swift */; }; + 18A2DCD62C40D94800A8731B /* AEADXChaCha20Poly1305.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC5F2C40D94700A8731B /* AEADXChaCha20Poly1305.swift */; }; + 18A2DCD72C40D94800A8731B /* AEADChaCha20Poly1305.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC602C40D94700A8731B /* AEADChaCha20Poly1305.swift */; }; + 18A2DCD82C40D94800A8731B /* AEAD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC612C40D94700A8731B /* AEAD.swift */; }; + 18A2DCD92C40D94800A8731B /* CBCMAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC622C40D94700A8731B /* CBCMAC.swift */; }; + 18A2DCDA2C40D94800A8731B /* Int+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC632C40D94700A8731B /* Int+Extension.swift */; }; + 18A2DCDB2C40D94800A8731B /* AES.Cryptors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC642C40D94700A8731B /* AES.Cryptors.swift */; }; + 18A2DCDC2C40D94800A8731B /* Bit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC652C40D94700A8731B /* Bit.swift */; }; + 18A2DCDD2C40D94800A8731B /* HMAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC662C40D94700A8731B /* HMAC.swift */; }; + 18A2DCDE2C40D94800A8731B /* CMAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC672C40D94700A8731B /* CMAC.swift */; }; + 18A2DCDF2C40D94800A8731B /* UInt16+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC682C40D94700A8731B /* UInt16+Extension.swift */; }; + 18A2DCE02C40D94800A8731B /* Collection+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC692C40D94700A8731B /* Collection+Extension.swift */; }; + 18A2DCE12C40D94800A8731B /* XChaCha20.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC6A2C40D94700A8731B /* XChaCha20.swift */; }; + 18A2DCE22C40D94800A8731B /* UInt32+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC6B2C40D94700A8731B /* UInt32+Extension.swift */; }; + 18A2DCE32C40D94800A8731B /* Digest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC6C2C40D94700A8731B /* Digest.swift */; }; + 18A2DCE42C40D94800A8731B /* UInt128.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC6D2C40D94700A8731B /* UInt128.swift */; }; + 18A2DCE52C40D94800A8731B /* RSA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC6F2C40D94700A8731B /* RSA.swift */; }; + 18A2DCE62C40D94800A8731B /* RSA+Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC702C40D94700A8731B /* RSA+Signature.swift */; }; + 18A2DCE72C40D94800A8731B /* RSA+Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC712C40D94700A8731B /* RSA+Cipher.swift */; }; + 18A2DCE82C40D94800A8731B /* Scrypt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC722C40D94700A8731B /* Scrypt.swift */; }; + 18A2DCE92C40D94800A8731B /* NoPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC732C40D94700A8731B /* NoPadding.swift */; }; + 18A2DCEA2C40D94800A8731B /* String+FoundationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC752C40D94700A8731B /* String+FoundationExtension.swift */; }; + 18A2DCEB2C40D94800A8731B /* XChaCha20+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC762C40D94700A8731B /* XChaCha20+Foundation.swift */; }; + 18A2DCEC2C40D94800A8731B /* Blowfish+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC772C40D94700A8731B /* Blowfish+Foundation.swift */; }; + 18A2DCED2C40D94800A8731B /* Utils+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC782C40D94700A8731B /* Utils+Foundation.swift */; }; + 18A2DCEE2C40D94800A8731B /* HMAC+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC792C40D94700A8731B /* HMAC+Foundation.swift */; }; + 18A2DCEF2C40D94800A8731B /* Data+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC7A2C40D94700A8731B /* Data+Extension.swift */; }; + 18A2DCF02C40D94800A8731B /* ChaCha20+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC7B2C40D94700A8731B /* ChaCha20+Foundation.swift */; }; + 18A2DCF12C40D94800A8731B /* Array+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC7C2C40D94700A8731B /* Array+Foundation.swift */; }; + 18A2DCF22C40D94800A8731B /* AES+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC7D2C40D94700A8731B /* AES+Foundation.swift */; }; + 18A2DCF32C40D94800A8731B /* Rabbit+Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC7E2C40D94700A8731B /* Rabbit+Foundation.swift */; }; + 18A2DCF42C40D94800A8731B /* UInt8+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC7F2C40D94700A8731B /* UInt8+Extension.swift */; }; + 18A2DCF52C40D94800A8731B /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC802C40D94700A8731B /* MD5.swift */; }; + 18A2DCF62C40D94800A8731B /* Cryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC812C40D94700A8731B /* Cryptor.swift */; }; + 18A2DCF72C40D94800A8731B /* CompactMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC822C40D94700A8731B /* CompactMap.swift */; }; + 18A2DCF82C40D94800A8731B /* BatchedCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC832C40D94700A8731B /* BatchedCollection.swift */; }; + 18A2DCF92C40D94800A8731B /* ISO78164Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC842C40D94700A8731B /* ISO78164Padding.swift */; }; + 18A2DCFA2C40D94800A8731B /* SecureBytes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC852C40D94700A8731B /* SecureBytes.swift */; }; + 18A2DCFB2C40D94800A8731B /* Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC862C40D94700A8731B /* Signature.swift */; }; + 18A2DCFC2C40D94800A8731B /* ZeroPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC872C40D94700A8731B /* ZeroPadding.swift */; }; + 18A2DCFD2C40D94800A8731B /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC882C40D94700A8731B /* String+Extension.swift */; }; + 18A2DCFE2C40D94800A8731B /* Authenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC892C40D94700A8731B /* Authenticator.swift */; }; + 18A2DCFF2C40D94800A8731B /* SHA3.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC8A2C40D94700A8731B /* SHA3.swift */; }; + 18A2DD002C40D94800A8731B /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC8B2C40D94700A8731B /* Operators.swift */; }; + 18A2DD012C40D94800A8731B /* Rabbit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC8C2C40D94700A8731B /* Rabbit.swift */; }; + 18A2DD022C40D94800A8731B /* SHA1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC8D2C40D94700A8731B /* SHA1.swift */; }; + 18A2DD032C40D94800A8731B /* Poly1305.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC8E2C40D94700A8731B /* Poly1305.swift */; }; + 18A2DD042C40D94800A8731B /* BlockCipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC8F2C40D94700A8731B /* BlockCipher.swift */; }; + 18A2DD052C40D94800A8731B /* Updatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC902C40D94700A8731B /* Updatable.swift */; }; + 18A2DD062C40D94800A8731B /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 18A2DC912C40D94700A8731B /* PrivacyInfo.xcprivacy */; }; + 18A2DD072C40D94800A8731B /* Blowfish.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC922C40D94700A8731B /* Blowfish.swift */; }; + 18A2DD082C40D94800A8731B /* BlockDecryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC932C40D94700A8731B /* BlockDecryptor.swift */; }; + 18A2DD092C40D94800A8731B /* StreamEncryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC942C40D94700A8731B /* StreamEncryptor.swift */; }; + 18A2DD0A2C40D94800A8731B /* DigestType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC952C40D94700A8731B /* DigestType.swift */; }; + 18A2DD0B2C40D94800A8731B /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC962C40D94700A8731B /* Utils.swift */; }; + 18A2DD0C2C40D94800A8731B /* Generics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC972C40D94700A8731B /* Generics.swift */; }; + 18A2DD0D2C40D94800A8731B /* Checksum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC982C40D94700A8731B /* Checksum.swift */; }; + 18A2DD0E2C40D94900A8731B /* SHA2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC992C40D94700A8731B /* SHA2.swift */; }; + 18A2DD0F2C40D94900A8731B /* Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC9A2C40D94700A8731B /* Padding.swift */; }; + 18A2DD102C40D94900A8731B /* PKCS7Padding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC9C2C40D94700A8731B /* PKCS7Padding.swift */; }; + 18A2DD112C40D94900A8731B /* PKCS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC9D2C40D94700A8731B /* PKCS5.swift */; }; + 18A2DD122C40D94900A8731B /* PKCS7.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC9E2C40D94700A8731B /* PKCS7.swift */; }; + 18A2DD132C40D94900A8731B /* PKCS1v15.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DC9F2C40D94700A8731B /* PKCS1v15.swift */; }; + 18A2DD142C40D94900A8731B /* PBKDF2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCA02C40D94700A8731B /* PBKDF2.swift */; }; + 18A2DD152C40D94900A8731B /* PBKDF1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCA12C40D94700A8731B /* PBKDF1.swift */; }; + 18A2DD162C40D94900A8731B /* BlockModeOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCA32C40D94700A8731B /* BlockModeOptions.swift */; }; + 18A2DD172C40D94900A8731B /* OCB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCA42C40D94700A8731B /* OCB.swift */; }; + 18A2DD182C40D94900A8731B /* CFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCA52C40D94700A8731B /* CFB.swift */; }; + 18A2DD192C40D94900A8731B /* CBC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCA62C40D94700A8731B /* CBC.swift */; }; + 18A2DD1A2C40D94900A8731B /* ECB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCA72C40D94700A8731B /* ECB.swift */; }; + 18A2DD1B2C40D94900A8731B /* CCM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCA82C40D94700A8731B /* CCM.swift */; }; + 18A2DD1C2C40D94900A8731B /* PCBC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCA92C40D94700A8731B /* PCBC.swift */; }; + 18A2DD1D2C40D94900A8731B /* BlockMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCAA2C40D94700A8731B /* BlockMode.swift */; }; + 18A2DD1E2C40D94900A8731B /* GCM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCAB2C40D94700A8731B /* GCM.swift */; }; + 18A2DD1F2C40D94900A8731B /* CipherModeWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCAC2C40D94700A8731B /* CipherModeWorker.swift */; }; + 18A2DD202C40D94900A8731B /* CTR.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCAD2C40D94700A8731B /* CTR.swift */; }; + 18A2DD212C40D94900A8731B /* OFB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCAE2C40D94700A8731B /* OFB.swift */; }; + 18A2DD222C40D94900A8731B /* ChaCha20.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCAF2C40D94700A8731B /* ChaCha20.swift */; }; + 18A2DD232C40D94900A8731B /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCB12C40D94700A8731B /* Random.swift */; }; + 18A2DD242C40D94900A8731B /* GCD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCB22C40D94700A8731B /* GCD.swift */; }; + 18A2DD252C40D94900A8731B /* Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCB32C40D94700A8731B /* Hashable.swift */; }; + 18A2DD262C40D94900A8731B /* Strideable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCB42C40D94700A8731B /* Strideable.swift */; }; + 18A2DD272C40D94900A8731B /* DataConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCB52C40D94700A8731B /* DataConversion.swift */; }; + 18A2DD282C40D94900A8731B /* Addition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCB62C40D94700A8731B /* Addition.swift */; }; + 18A2DD292C40D94900A8731B /* StringConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCB72C40D94700A8731B /* StringConversion.swift */; }; + 18A2DD2A2C40D94900A8731B /* Comparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCB82C40D94700A8731B /* Comparable.swift */; }; + 18A2DD2B2C40D94900A8731B /* WordsAndBits.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCB92C40D94700A8731B /* WordsAndBits.swift */; }; + 18A2DD2C2C40D94900A8731B /* IntegerConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCBA2C40D94700A8731B /* IntegerConversion.swift */; }; + 18A2DD2D2C40D94900A8731B /* Division.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCBB2C40D94700A8731B /* Division.swift */; }; + 18A2DD2E2C40D94900A8731B /* PrimeTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCBC2C40D94700A8731B /* PrimeTest.swift */; }; + 18A2DD2F2C40D94900A8731B /* Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCBD2C40D94700A8731B /* Codable.swift */; }; + 18A2DD302C40D94900A8731B /* SquareRoot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCBE2C40D94700A8731B /* SquareRoot.swift */; }; + 18A2DD312C40D94900A8731B /* BigUInt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCBF2C40D94700A8731B /* BigUInt.swift */; }; + 18A2DD322C40D94900A8731B /* BitwiseOps.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCC02C40D94700A8731B /* BitwiseOps.swift */; }; + 18A2DD332C40D94900A8731B /* Multiplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCC12C40D94700A8731B /* Multiplication.swift */; }; + 18A2DD342C40D94900A8731B /* CS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCC22C40D94700A8731B /* CS.swift */; }; + 18A2DD352C40D94900A8731B /* Exponentiation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCC32C40D94700A8731B /* Exponentiation.swift */; }; + 18A2DD362C40D94900A8731B /* Shifts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCC42C40D94700A8731B /* Shifts.swift */; }; + 18A2DD372C40D94900A8731B /* Subtraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCC52C40D94700A8731B /* Subtraction.swift */; }; + 18A2DD382C40D94900A8731B /* BigInt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCC62C40D94700A8731B /* BigInt.swift */; }; + 18A2DD392C40D94900A8731B /* FloatingPointConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCC72C40D94700A8731B /* FloatingPointConversion.swift */; }; + 18A2DD3A2C40D94900A8731B /* Array+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A2DCC82C40D94700A8731B /* Array+Extension.swift */; }; 18B74A1C2C1FD44A00FABBA9 /* MSTts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B74A1B2C1FD44A00FABBA9 /* MSTts.swift */; }; 18B74A1E2C1FDDB500FABBA9 /* MSChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B74A1D2C1FDDB400FABBA9 /* MSChat.swift */; }; 18C42B392C22F1FA000D8860 /* MSSttModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18C42B382C22F1FA000D8860 /* MSSttModel.swift */; }; @@ -227,6 +341,120 @@ 18314A9F2C3D37ED0028CE50 /* AudioTransform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioTransform.h; sourceTree = ""; }; 18314AA02C3D37ED0028CE50 /* AudioTransform.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioTransform.m; sourceTree = ""; }; 184FFC532C21B091006F664D /* MSAigcModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSAigcModel.swift; sourceTree = ""; }; + 18A2DC4F2C40D94700A8731B /* AES.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AES.swift; sourceTree = ""; }; + 18A2DC512C40D94700A8731B /* ASN1Scanner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASN1Scanner.swift; sourceTree = ""; }; + 18A2DC522C40D94700A8731B /* ASN1Encoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASN1Encoder.swift; sourceTree = ""; }; + 18A2DC532C40D94700A8731B /* ASN1Decoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASN1Decoder.swift; sourceTree = ""; }; + 18A2DC542C40D94700A8731B /* ASN1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASN1.swift; sourceTree = ""; }; + 18A2DC552C40D94700A8731B /* StreamDecryptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StreamDecryptor.swift; sourceTree = ""; }; + 18A2DC572C40D94700A8731B /* DER.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DER.swift; sourceTree = ""; }; + 18A2DC582C40D94700A8731B /* BlockEncryptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockEncryptor.swift; sourceTree = ""; }; + 18A2DC592C40D94700A8731B /* UInt64+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UInt64+Extension.swift"; sourceTree = ""; }; + 18A2DC5A2C40D94700A8731B /* Cryptors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cryptors.swift; sourceTree = ""; }; + 18A2DC5B2C40D94700A8731B /* HKDF.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HKDF.swift; sourceTree = ""; }; + 18A2DC5C2C40D94700A8731B /* ISO10126Padding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ISO10126Padding.swift; sourceTree = ""; }; + 18A2DC5D2C40D94700A8731B /* Cipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cipher.swift; sourceTree = ""; }; + 18A2DC5F2C40D94700A8731B /* AEADXChaCha20Poly1305.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AEADXChaCha20Poly1305.swift; sourceTree = ""; }; + 18A2DC602C40D94700A8731B /* AEADChaCha20Poly1305.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AEADChaCha20Poly1305.swift; sourceTree = ""; }; + 18A2DC612C40D94700A8731B /* AEAD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AEAD.swift; sourceTree = ""; }; + 18A2DC622C40D94700A8731B /* CBCMAC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBCMAC.swift; sourceTree = ""; }; + 18A2DC632C40D94700A8731B /* Int+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Int+Extension.swift"; sourceTree = ""; }; + 18A2DC642C40D94700A8731B /* AES.Cryptors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AES.Cryptors.swift; sourceTree = ""; }; + 18A2DC652C40D94700A8731B /* Bit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bit.swift; sourceTree = ""; }; + 18A2DC662C40D94700A8731B /* HMAC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HMAC.swift; sourceTree = ""; }; + 18A2DC672C40D94700A8731B /* CMAC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CMAC.swift; sourceTree = ""; }; + 18A2DC682C40D94700A8731B /* UInt16+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UInt16+Extension.swift"; sourceTree = ""; }; + 18A2DC692C40D94700A8731B /* Collection+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Collection+Extension.swift"; sourceTree = ""; }; + 18A2DC6A2C40D94700A8731B /* XChaCha20.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XChaCha20.swift; sourceTree = ""; }; + 18A2DC6B2C40D94700A8731B /* UInt32+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UInt32+Extension.swift"; sourceTree = ""; }; + 18A2DC6C2C40D94700A8731B /* Digest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Digest.swift; sourceTree = ""; }; + 18A2DC6D2C40D94700A8731B /* UInt128.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UInt128.swift; sourceTree = ""; }; + 18A2DC6F2C40D94700A8731B /* RSA.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSA.swift; sourceTree = ""; }; + 18A2DC702C40D94700A8731B /* RSA+Signature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RSA+Signature.swift"; sourceTree = ""; }; + 18A2DC712C40D94700A8731B /* RSA+Cipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RSA+Cipher.swift"; sourceTree = ""; }; + 18A2DC722C40D94700A8731B /* Scrypt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scrypt.swift; sourceTree = ""; }; + 18A2DC732C40D94700A8731B /* NoPadding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoPadding.swift; sourceTree = ""; }; + 18A2DC752C40D94700A8731B /* String+FoundationExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+FoundationExtension.swift"; sourceTree = ""; }; + 18A2DC762C40D94700A8731B /* XChaCha20+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XChaCha20+Foundation.swift"; sourceTree = ""; }; + 18A2DC772C40D94700A8731B /* Blowfish+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Blowfish+Foundation.swift"; sourceTree = ""; }; + 18A2DC782C40D94700A8731B /* Utils+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Utils+Foundation.swift"; sourceTree = ""; }; + 18A2DC792C40D94700A8731B /* HMAC+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HMAC+Foundation.swift"; sourceTree = ""; }; + 18A2DC7A2C40D94700A8731B /* Data+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Extension.swift"; sourceTree = ""; }; + 18A2DC7B2C40D94700A8731B /* ChaCha20+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ChaCha20+Foundation.swift"; sourceTree = ""; }; + 18A2DC7C2C40D94700A8731B /* Array+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Foundation.swift"; sourceTree = ""; }; + 18A2DC7D2C40D94700A8731B /* AES+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AES+Foundation.swift"; sourceTree = ""; }; + 18A2DC7E2C40D94700A8731B /* Rabbit+Foundation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Rabbit+Foundation.swift"; sourceTree = ""; }; + 18A2DC7F2C40D94700A8731B /* UInt8+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UInt8+Extension.swift"; sourceTree = ""; }; + 18A2DC802C40D94700A8731B /* MD5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MD5.swift; sourceTree = ""; }; + 18A2DC812C40D94700A8731B /* Cryptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cryptor.swift; sourceTree = ""; }; + 18A2DC822C40D94700A8731B /* CompactMap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompactMap.swift; sourceTree = ""; }; + 18A2DC832C40D94700A8731B /* BatchedCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatchedCollection.swift; sourceTree = ""; }; + 18A2DC842C40D94700A8731B /* ISO78164Padding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ISO78164Padding.swift; sourceTree = ""; }; + 18A2DC852C40D94700A8731B /* SecureBytes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecureBytes.swift; sourceTree = ""; }; + 18A2DC862C40D94700A8731B /* Signature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Signature.swift; sourceTree = ""; }; + 18A2DC872C40D94700A8731B /* ZeroPadding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZeroPadding.swift; sourceTree = ""; }; + 18A2DC882C40D94700A8731B /* String+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; + 18A2DC892C40D94700A8731B /* Authenticator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Authenticator.swift; sourceTree = ""; }; + 18A2DC8A2C40D94700A8731B /* SHA3.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SHA3.swift; sourceTree = ""; }; + 18A2DC8B2C40D94700A8731B /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = ""; }; + 18A2DC8C2C40D94700A8731B /* Rabbit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rabbit.swift; sourceTree = ""; }; + 18A2DC8D2C40D94700A8731B /* SHA1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SHA1.swift; sourceTree = ""; }; + 18A2DC8E2C40D94700A8731B /* Poly1305.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Poly1305.swift; sourceTree = ""; }; + 18A2DC8F2C40D94700A8731B /* BlockCipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockCipher.swift; sourceTree = ""; }; + 18A2DC902C40D94700A8731B /* Updatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Updatable.swift; sourceTree = ""; }; + 18A2DC912C40D94700A8731B /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + 18A2DC922C40D94700A8731B /* Blowfish.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Blowfish.swift; sourceTree = ""; }; + 18A2DC932C40D94700A8731B /* BlockDecryptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockDecryptor.swift; sourceTree = ""; }; + 18A2DC942C40D94700A8731B /* StreamEncryptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StreamEncryptor.swift; sourceTree = ""; }; + 18A2DC952C40D94700A8731B /* DigestType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DigestType.swift; sourceTree = ""; }; + 18A2DC962C40D94700A8731B /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; + 18A2DC972C40D94700A8731B /* Generics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Generics.swift; sourceTree = ""; }; + 18A2DC982C40D94700A8731B /* Checksum.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Checksum.swift; sourceTree = ""; }; + 18A2DC992C40D94700A8731B /* SHA2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SHA2.swift; sourceTree = ""; }; + 18A2DC9A2C40D94700A8731B /* Padding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Padding.swift; sourceTree = ""; }; + 18A2DC9C2C40D94700A8731B /* PKCS7Padding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PKCS7Padding.swift; sourceTree = ""; }; + 18A2DC9D2C40D94700A8731B /* PKCS5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PKCS5.swift; sourceTree = ""; }; + 18A2DC9E2C40D94700A8731B /* PKCS7.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PKCS7.swift; sourceTree = ""; }; + 18A2DC9F2C40D94700A8731B /* PKCS1v15.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PKCS1v15.swift; sourceTree = ""; }; + 18A2DCA02C40D94700A8731B /* PBKDF2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PBKDF2.swift; sourceTree = ""; }; + 18A2DCA12C40D94700A8731B /* PBKDF1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PBKDF1.swift; sourceTree = ""; }; + 18A2DCA32C40D94700A8731B /* BlockModeOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockModeOptions.swift; sourceTree = ""; }; + 18A2DCA42C40D94700A8731B /* OCB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OCB.swift; sourceTree = ""; }; + 18A2DCA52C40D94700A8731B /* CFB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CFB.swift; sourceTree = ""; }; + 18A2DCA62C40D94700A8731B /* CBC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBC.swift; sourceTree = ""; }; + 18A2DCA72C40D94700A8731B /* ECB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ECB.swift; sourceTree = ""; }; + 18A2DCA82C40D94700A8731B /* CCM.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CCM.swift; sourceTree = ""; }; + 18A2DCA92C40D94700A8731B /* PCBC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PCBC.swift; sourceTree = ""; }; + 18A2DCAA2C40D94700A8731B /* BlockMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockMode.swift; sourceTree = ""; }; + 18A2DCAB2C40D94700A8731B /* GCM.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GCM.swift; sourceTree = ""; }; + 18A2DCAC2C40D94700A8731B /* CipherModeWorker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CipherModeWorker.swift; sourceTree = ""; }; + 18A2DCAD2C40D94700A8731B /* CTR.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CTR.swift; sourceTree = ""; }; + 18A2DCAE2C40D94700A8731B /* OFB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OFB.swift; sourceTree = ""; }; + 18A2DCAF2C40D94700A8731B /* ChaCha20.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChaCha20.swift; sourceTree = ""; }; + 18A2DCB12C40D94700A8731B /* Random.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Random.swift; sourceTree = ""; }; + 18A2DCB22C40D94700A8731B /* GCD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GCD.swift; sourceTree = ""; }; + 18A2DCB32C40D94700A8731B /* Hashable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Hashable.swift; sourceTree = ""; }; + 18A2DCB42C40D94700A8731B /* Strideable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Strideable.swift; sourceTree = ""; }; + 18A2DCB52C40D94700A8731B /* DataConversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataConversion.swift; sourceTree = ""; }; + 18A2DCB62C40D94700A8731B /* Addition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Addition.swift; sourceTree = ""; }; + 18A2DCB72C40D94700A8731B /* StringConversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringConversion.swift; sourceTree = ""; }; + 18A2DCB82C40D94700A8731B /* Comparable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Comparable.swift; sourceTree = ""; }; + 18A2DCB92C40D94700A8731B /* WordsAndBits.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WordsAndBits.swift; sourceTree = ""; }; + 18A2DCBA2C40D94700A8731B /* IntegerConversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntegerConversion.swift; sourceTree = ""; }; + 18A2DCBB2C40D94700A8731B /* Division.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Division.swift; sourceTree = ""; }; + 18A2DCBC2C40D94700A8731B /* PrimeTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimeTest.swift; sourceTree = ""; }; + 18A2DCBD2C40D94700A8731B /* Codable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Codable.swift; sourceTree = ""; }; + 18A2DCBE2C40D94700A8731B /* SquareRoot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SquareRoot.swift; sourceTree = ""; }; + 18A2DCBF2C40D94700A8731B /* BigUInt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BigUInt.swift; sourceTree = ""; }; + 18A2DCC02C40D94700A8731B /* BitwiseOps.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitwiseOps.swift; sourceTree = ""; }; + 18A2DCC12C40D94700A8731B /* Multiplication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Multiplication.swift; sourceTree = ""; }; + 18A2DCC22C40D94700A8731B /* CS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CS.swift; sourceTree = ""; }; + 18A2DCC32C40D94700A8731B /* Exponentiation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Exponentiation.swift; sourceTree = ""; }; + 18A2DCC42C40D94700A8731B /* Shifts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Shifts.swift; sourceTree = ""; }; + 18A2DCC52C40D94700A8731B /* Subtraction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Subtraction.swift; sourceTree = ""; }; + 18A2DCC62C40D94700A8731B /* BigInt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BigInt.swift; sourceTree = ""; }; + 18A2DCC72C40D94700A8731B /* FloatingPointConversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FloatingPointConversion.swift; sourceTree = ""; }; + 18A2DCC82C40D94700A8731B /* Array+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Extension.swift"; sourceTree = ""; }; 18B74A1B2C1FD44A00FABBA9 /* MSTts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSTts.swift; sourceTree = ""; }; 18B74A1D2C1FDDB400FABBA9 /* MSChat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSChat.swift; sourceTree = ""; }; 18C42B382C22F1FA000D8860 /* MSSttModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSSttModel.swift; sourceTree = ""; }; @@ -443,6 +671,7 @@ 18204BBD2C193E3D00F94E69 /* MixSdk */ = { isa = PBXGroup; children = ( + 18A2DC4E2C40D94700A8731B /* CryptoSwift */, 18204BF12C19488800F94E69 /* SwiftHTTP */, 18314A982C3D37ED0028CE50 /* AudioTransform */, 18C42C692C2422C2000D8860 /* AudioDecode */, @@ -528,6 +757,191 @@ path = lib; sourceTree = ""; }; + 18A2DC4E2C40D94700A8731B /* CryptoSwift */ = { + isa = PBXGroup; + children = ( + 18A2DC4F2C40D94700A8731B /* AES.swift */, + 18A2DC502C40D94700A8731B /* ASN1 */, + 18A2DC552C40D94700A8731B /* StreamDecryptor.swift */, + 18A2DC562C40D94700A8731B /* PEM */, + 18A2DC582C40D94700A8731B /* BlockEncryptor.swift */, + 18A2DC592C40D94700A8731B /* UInt64+Extension.swift */, + 18A2DC5A2C40D94700A8731B /* Cryptors.swift */, + 18A2DC5B2C40D94700A8731B /* HKDF.swift */, + 18A2DC5C2C40D94700A8731B /* ISO10126Padding.swift */, + 18A2DC5D2C40D94700A8731B /* Cipher.swift */, + 18A2DC5E2C40D94700A8731B /* AEAD */, + 18A2DC622C40D94700A8731B /* CBCMAC.swift */, + 18A2DC632C40D94700A8731B /* Int+Extension.swift */, + 18A2DC642C40D94700A8731B /* AES.Cryptors.swift */, + 18A2DC652C40D94700A8731B /* Bit.swift */, + 18A2DC662C40D94700A8731B /* HMAC.swift */, + 18A2DC672C40D94700A8731B /* CMAC.swift */, + 18A2DC682C40D94700A8731B /* UInt16+Extension.swift */, + 18A2DC692C40D94700A8731B /* Collection+Extension.swift */, + 18A2DC6A2C40D94700A8731B /* XChaCha20.swift */, + 18A2DC6B2C40D94700A8731B /* UInt32+Extension.swift */, + 18A2DC6C2C40D94700A8731B /* Digest.swift */, + 18A2DC6D2C40D94700A8731B /* UInt128.swift */, + 18A2DC6E2C40D94700A8731B /* RSA */, + 18A2DC722C40D94700A8731B /* Scrypt.swift */, + 18A2DC732C40D94700A8731B /* NoPadding.swift */, + 18A2DC742C40D94700A8731B /* Foundation */, + 18A2DC7F2C40D94700A8731B /* UInt8+Extension.swift */, + 18A2DC802C40D94700A8731B /* MD5.swift */, + 18A2DC812C40D94700A8731B /* Cryptor.swift */, + 18A2DC822C40D94700A8731B /* CompactMap.swift */, + 18A2DC832C40D94700A8731B /* BatchedCollection.swift */, + 18A2DC842C40D94700A8731B /* ISO78164Padding.swift */, + 18A2DC852C40D94700A8731B /* SecureBytes.swift */, + 18A2DC862C40D94700A8731B /* Signature.swift */, + 18A2DC872C40D94700A8731B /* ZeroPadding.swift */, + 18A2DC882C40D94700A8731B /* String+Extension.swift */, + 18A2DC892C40D94700A8731B /* Authenticator.swift */, + 18A2DC8A2C40D94700A8731B /* SHA3.swift */, + 18A2DC8B2C40D94700A8731B /* Operators.swift */, + 18A2DC8C2C40D94700A8731B /* Rabbit.swift */, + 18A2DC8D2C40D94700A8731B /* SHA1.swift */, + 18A2DC8E2C40D94700A8731B /* Poly1305.swift */, + 18A2DC8F2C40D94700A8731B /* BlockCipher.swift */, + 18A2DC902C40D94700A8731B /* Updatable.swift */, + 18A2DC912C40D94700A8731B /* PrivacyInfo.xcprivacy */, + 18A2DC922C40D94700A8731B /* Blowfish.swift */, + 18A2DC932C40D94700A8731B /* BlockDecryptor.swift */, + 18A2DC942C40D94700A8731B /* StreamEncryptor.swift */, + 18A2DC952C40D94700A8731B /* DigestType.swift */, + 18A2DC962C40D94700A8731B /* Utils.swift */, + 18A2DC972C40D94700A8731B /* Generics.swift */, + 18A2DC982C40D94700A8731B /* Checksum.swift */, + 18A2DC992C40D94700A8731B /* SHA2.swift */, + 18A2DC9A2C40D94700A8731B /* Padding.swift */, + 18A2DC9B2C40D94700A8731B /* PKCS */, + 18A2DCA22C40D94700A8731B /* BlockMode */, + 18A2DCAF2C40D94700A8731B /* ChaCha20.swift */, + 18A2DCB02C40D94700A8731B /* CS_BigInt */, + 18A2DCC82C40D94700A8731B /* Array+Extension.swift */, + ); + path = CryptoSwift; + sourceTree = ""; + }; + 18A2DC502C40D94700A8731B /* ASN1 */ = { + isa = PBXGroup; + children = ( + 18A2DC512C40D94700A8731B /* ASN1Scanner.swift */, + 18A2DC522C40D94700A8731B /* ASN1Encoder.swift */, + 18A2DC532C40D94700A8731B /* ASN1Decoder.swift */, + 18A2DC542C40D94700A8731B /* ASN1.swift */, + ); + path = ASN1; + sourceTree = ""; + }; + 18A2DC562C40D94700A8731B /* PEM */ = { + isa = PBXGroup; + children = ( + 18A2DC572C40D94700A8731B /* DER.swift */, + ); + path = PEM; + sourceTree = ""; + }; + 18A2DC5E2C40D94700A8731B /* AEAD */ = { + isa = PBXGroup; + children = ( + 18A2DC5F2C40D94700A8731B /* AEADXChaCha20Poly1305.swift */, + 18A2DC602C40D94700A8731B /* AEADChaCha20Poly1305.swift */, + 18A2DC612C40D94700A8731B /* AEAD.swift */, + ); + path = AEAD; + sourceTree = ""; + }; + 18A2DC6E2C40D94700A8731B /* RSA */ = { + isa = PBXGroup; + children = ( + 18A2DC6F2C40D94700A8731B /* RSA.swift */, + 18A2DC702C40D94700A8731B /* RSA+Signature.swift */, + 18A2DC712C40D94700A8731B /* RSA+Cipher.swift */, + ); + path = RSA; + sourceTree = ""; + }; + 18A2DC742C40D94700A8731B /* Foundation */ = { + isa = PBXGroup; + children = ( + 18A2DC752C40D94700A8731B /* String+FoundationExtension.swift */, + 18A2DC762C40D94700A8731B /* XChaCha20+Foundation.swift */, + 18A2DC772C40D94700A8731B /* Blowfish+Foundation.swift */, + 18A2DC782C40D94700A8731B /* Utils+Foundation.swift */, + 18A2DC792C40D94700A8731B /* HMAC+Foundation.swift */, + 18A2DC7A2C40D94700A8731B /* Data+Extension.swift */, + 18A2DC7B2C40D94700A8731B /* ChaCha20+Foundation.swift */, + 18A2DC7C2C40D94700A8731B /* Array+Foundation.swift */, + 18A2DC7D2C40D94700A8731B /* AES+Foundation.swift */, + 18A2DC7E2C40D94700A8731B /* Rabbit+Foundation.swift */, + ); + path = Foundation; + sourceTree = ""; + }; + 18A2DC9B2C40D94700A8731B /* PKCS */ = { + isa = PBXGroup; + children = ( + 18A2DC9C2C40D94700A8731B /* PKCS7Padding.swift */, + 18A2DC9D2C40D94700A8731B /* PKCS5.swift */, + 18A2DC9E2C40D94700A8731B /* PKCS7.swift */, + 18A2DC9F2C40D94700A8731B /* PKCS1v15.swift */, + 18A2DCA02C40D94700A8731B /* PBKDF2.swift */, + 18A2DCA12C40D94700A8731B /* PBKDF1.swift */, + ); + path = PKCS; + sourceTree = ""; + }; + 18A2DCA22C40D94700A8731B /* BlockMode */ = { + isa = PBXGroup; + children = ( + 18A2DCA32C40D94700A8731B /* BlockModeOptions.swift */, + 18A2DCA42C40D94700A8731B /* OCB.swift */, + 18A2DCA52C40D94700A8731B /* CFB.swift */, + 18A2DCA62C40D94700A8731B /* CBC.swift */, + 18A2DCA72C40D94700A8731B /* ECB.swift */, + 18A2DCA82C40D94700A8731B /* CCM.swift */, + 18A2DCA92C40D94700A8731B /* PCBC.swift */, + 18A2DCAA2C40D94700A8731B /* BlockMode.swift */, + 18A2DCAB2C40D94700A8731B /* GCM.swift */, + 18A2DCAC2C40D94700A8731B /* CipherModeWorker.swift */, + 18A2DCAD2C40D94700A8731B /* CTR.swift */, + 18A2DCAE2C40D94700A8731B /* OFB.swift */, + ); + path = BlockMode; + sourceTree = ""; + }; + 18A2DCB02C40D94700A8731B /* CS_BigInt */ = { + isa = PBXGroup; + children = ( + 18A2DCB12C40D94700A8731B /* Random.swift */, + 18A2DCB22C40D94700A8731B /* GCD.swift */, + 18A2DCB32C40D94700A8731B /* Hashable.swift */, + 18A2DCB42C40D94700A8731B /* Strideable.swift */, + 18A2DCB52C40D94700A8731B /* DataConversion.swift */, + 18A2DCB62C40D94700A8731B /* Addition.swift */, + 18A2DCB72C40D94700A8731B /* StringConversion.swift */, + 18A2DCB82C40D94700A8731B /* Comparable.swift */, + 18A2DCB92C40D94700A8731B /* WordsAndBits.swift */, + 18A2DCBA2C40D94700A8731B /* IntegerConversion.swift */, + 18A2DCBB2C40D94700A8731B /* Division.swift */, + 18A2DCBC2C40D94700A8731B /* PrimeTest.swift */, + 18A2DCBD2C40D94700A8731B /* Codable.swift */, + 18A2DCBE2C40D94700A8731B /* SquareRoot.swift */, + 18A2DCBF2C40D94700A8731B /* BigUInt.swift */, + 18A2DCC02C40D94700A8731B /* BitwiseOps.swift */, + 18A2DCC12C40D94700A8731B /* Multiplication.swift */, + 18A2DCC22C40D94700A8731B /* CS.swift */, + 18A2DCC32C40D94700A8731B /* Exponentiation.swift */, + 18A2DCC42C40D94700A8731B /* Shifts.swift */, + 18A2DCC52C40D94700A8731B /* Subtraction.swift */, + 18A2DCC62C40D94700A8731B /* BigInt.swift */, + 18A2DCC72C40D94700A8731B /* FloatingPointConversion.swift */, + ); + path = CS_BigInt; + sourceTree = ""; + }; 18C42B5B2C242202000D8860 /* FFmpeg-iOS */ = { isa = PBXGroup; children = ( @@ -949,6 +1363,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 18A2DD062C40D94800A8731B /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -959,45 +1374,158 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 18A2DCED2C40D94800A8731B /* Utils+Foundation.swift in Sources */, + 18A2DCDA2C40D94800A8731B /* Int+Extension.swift in Sources */, + 18A2DCFE2C40D94800A8731B /* Authenticator.swift in Sources */, + 18A2DD172C40D94900A8731B /* OCB.swift in Sources */, 18C42C7B2C24270D000D8860 /* MSAudioConvert.swift in Sources */, + 18A2DCCC2C40D94800A8731B /* ASN1Decoder.swift in Sources */, + 18A2DCDE2C40D94800A8731B /* CMAC.swift in Sources */, + 18A2DD002C40D94800A8731B /* Operators.swift in Sources */, 18204BC62C193E4B00F94E69 /* MSAuth.swift in Sources */, + 18A2DCCA2C40D94800A8731B /* ASN1Scanner.swift in Sources */, + 18A2DCCD2C40D94800A8731B /* ASN1.swift in Sources */, 18132AED2C1C1EA8002F6B52 /* MSTool.swift in Sources */, + 18A2DCD52C40D94800A8731B /* Cipher.swift in Sources */, 181188472C1ACA6100C23FAB /* String+Crypto.swift in Sources */, + 18A2DD262C40D94900A8731B /* Strideable.swift in Sources */, 18132AF22C1C4938002F6B52 /* SwiftWebSocket.swift in Sources */, + 18A2DD032C40D94800A8731B /* Poly1305.swift in Sources */, + 18A2DCFD2C40D94800A8731B /* String+Extension.swift in Sources */, + 18A2DCE32C40D94800A8731B /* Digest.swift in Sources */, + 18A2DD072C40D94800A8731B /* Blowfish.swift in Sources */, 1811883F2C1A8B5200C23FAB /* CRC16.swift in Sources */, + 18A2DCF52C40D94800A8731B /* MD5.swift in Sources */, + 18A2DCCF2C40D94800A8731B /* DER.swift in Sources */, 181188452C1AC35F00C23FAB /* MSError.swift in Sources */, 1811883B2C1A8B5200C23FAB /* Logger.swift in Sources */, + 18A2DD0E2C40D94900A8731B /* SHA2.swift in Sources */, 18C42B3D2C22F2C4000D8860 /* MSTtsModel.swift in Sources */, + 18A2DCFA2C40D94800A8731B /* SecureBytes.swift in Sources */, + 18A2DD392C40D94900A8731B /* FloatingPointConversion.swift in Sources */, + 18A2DCF12C40D94800A8731B /* Array+Foundation.swift in Sources */, + 18A2DD082C40D94800A8731B /* BlockDecryptor.swift in Sources */, + 18A2DD1B2C40D94900A8731B /* CCM.swift in Sources */, + 18A2DD2E2C40D94900A8731B /* PrimeTest.swift in Sources */, + 18A2DCDF2C40D94800A8731B /* UInt16+Extension.swift in Sources */, + 18A2DD162C40D94900A8731B /* BlockModeOptions.swift in Sources */, 181188572C1BE24200C23FAB /* MSStt.swift in Sources */, 18314A912C3D13A60028CE50 /* AudioConvert.m in Sources */, + 18A2DCF42C40D94800A8731B /* UInt8+Extension.swift in Sources */, + 18A2DD1D2C40D94900A8731B /* BlockMode.swift in Sources */, + 18A2DD282C40D94900A8731B /* Addition.swift in Sources */, + 18A2DCD92C40D94800A8731B /* CBCMAC.swift in Sources */, + 18A2DD132C40D94900A8731B /* PKCS1v15.swift in Sources */, + 18A2DCCE2C40D94800A8731B /* StreamDecryptor.swift in Sources */, + 18A2DD0F2C40D94900A8731B /* Padding.swift in Sources */, + 18A2DD242C40D94900A8731B /* GCD.swift in Sources */, + 18A2DCE92C40D94800A8731B /* NoPadding.swift in Sources */, 18204BFD2C19488900F94E69 /* Operation.swift in Sources */, 1811883C2C1A8B5200C23FAB /* JSON.swift in Sources */, + 18A2DCE82C40D94800A8731B /* Scrypt.swift in Sources */, + 18A2DD0A2C40D94800A8731B /* DigestType.swift in Sources */, + 18A2DCD72C40D94800A8731B /* AEADChaCha20Poly1305.swift in Sources */, 181188402C1A8B5200C23FAB /* LRC.swift in Sources */, + 18A2DCF82C40D94800A8731B /* BatchedCollection.swift in Sources */, + 18A2DD1A2C40D94900A8731B /* ECB.swift in Sources */, + 18A2DCFF2C40D94800A8731B /* SHA3.swift in Sources */, + 18A2DD102C40D94900A8731B /* PKCS7Padding.swift in Sources */, + 18A2DCF02C40D94800A8731B /* ChaCha20+Foundation.swift in Sources */, 18314A892C3CD55E0028CE50 /* MSAuthModel.swift in Sources */, 18FBC2B92C38DA1D002240AF /* MSChatGptModel.swift in Sources */, + 18A2DCD02C40D94800A8731B /* BlockEncryptor.swift in Sources */, + 18A2DCDD2C40D94800A8731B /* HMAC.swift in Sources */, 18204BFC2C19488900F94E69 /* HTTPSecurity.swift in Sources */, + 18A2DD042C40D94800A8731B /* BlockCipher.swift in Sources */, + 18A2DCD62C40D94800A8731B /* AEADXChaCha20Poly1305.swift in Sources */, + 18A2DD202C40D94900A8731B /* CTR.swift in Sources */, + 18A2DD142C40D94900A8731B /* PBKDF2.swift in Sources */, + 18A2DD312C40D94900A8731B /* BigUInt.swift in Sources */, + 18A2DD332C40D94900A8731B /* Multiplication.swift in Sources */, 18204BFB2C19488900F94E69 /* Upload.swift in Sources */, + 18A2DD2B2C40D94900A8731B /* WordsAndBits.swift in Sources */, 18204BFA2C19488900F94E69 /* Request.swift in Sources */, 18314A862C3CD24E0028CE50 /* MSUtils.m in Sources */, 1811882F2C1A89E600C23FAB /* MixSdk.swift in Sources */, 18C42B3B2C22F284000D8860 /* MsTranslateModel.swift in Sources */, + 18A2DCD32C40D94800A8731B /* HKDF.swift in Sources */, + 18A2DD092C40D94800A8731B /* StreamEncryptor.swift in Sources */, + 18A2DD352C40D94900A8731B /* Exponentiation.swift in Sources */, + 18A2DD362C40D94900A8731B /* Shifts.swift in Sources */, + 18A2DD1E2C40D94900A8731B /* GCM.swift in Sources */, 18C42B3F2C22F2E1000D8860 /* MSChatModel.swift in Sources */, + 18A2DD372C40D94900A8731B /* Subtraction.swift in Sources */, + 18A2DD252C40D94900A8731B /* Hashable.swift in Sources */, + 18A2DCFB2C40D94800A8731B /* Signature.swift in Sources */, + 18A2DCEB2C40D94800A8731B /* XChaCha20+Foundation.swift in Sources */, + 18A2DCEC2C40D94800A8731B /* Blowfish+Foundation.swift in Sources */, + 18A2DCF72C40D94800A8731B /* CompactMap.swift in Sources */, 18204BF02C19457600F94E69 /* MSHttpManager.swift in Sources */, + 18A2DD3A2C40D94900A8731B /* Array+Extension.swift in Sources */, + 18A2DD212C40D94900A8731B /* OFB.swift in Sources */, 18B74A1C2C1FD44A00FABBA9 /* MSTts.swift in Sources */, + 18A2DD342C40D94900A8731B /* CS.swift in Sources */, + 18A2DD152C40D94900A8731B /* PBKDF1.swift in Sources */, + 18A2DCF92C40D94800A8731B /* ISO78164Padding.swift in Sources */, 181188392C1A8B5200C23FAB /* Date.swift in Sources */, + 18A2DD302C40D94900A8731B /* SquareRoot.swift in Sources */, + 18A2DCE52C40D94800A8731B /* RSA.swift in Sources */, + 18A2DCF62C40D94800A8731B /* Cryptor.swift in Sources */, + 18A2DCF22C40D94800A8731B /* AES+Foundation.swift in Sources */, 18132AEF2C1C25CC002F6B52 /* MSAigc.swift in Sources */, 18C42B4B2C23DA29000D8860 /* SbcToPcm.swift in Sources */, + 18A2DCE02C40D94800A8731B /* Collection+Extension.swift in Sources */, + 18A2DD112C40D94900A8731B /* PKCS5.swift in Sources */, + 18A2DCD22C40D94800A8731B /* Cryptors.swift in Sources */, + 18A2DCD82C40D94800A8731B /* AEAD.swift in Sources */, + 18A2DD1F2C40D94900A8731B /* CipherModeWorker.swift in Sources */, + 18A2DD122C40D94900A8731B /* PKCS7.swift in Sources */, 18314AA42C3D37EE0028CE50 /* AudioTransform.m in Sources */, + 18A2DCE42C40D94800A8731B /* UInt128.swift in Sources */, + 18A2DD052C40D94800A8731B /* Updatable.swift in Sources */, + 18A2DD182C40D94900A8731B /* CFB.swift in Sources */, + 18A2DCD12C40D94800A8731B /* UInt64+Extension.swift in Sources */, + 18A2DD292C40D94900A8731B /* StringConversion.swift in Sources */, + 18A2DCFC2C40D94800A8731B /* ZeroPadding.swift in Sources */, + 18A2DCDC2C40D94800A8731B /* Bit.swift in Sources */, + 18A2DD0B2C40D94800A8731B /* Utils.swift in Sources */, + 18A2DD222C40D94900A8731B /* ChaCha20.swift in Sources */, + 18A2DCC92C40D94800A8731B /* AES.swift in Sources */, + 18A2DD272C40D94900A8731B /* DataConversion.swift in Sources */, 1811883A2C1A8B5200C23FAB /* Any.swift in Sources */, + 18A2DD0C2C40D94800A8731B /* Generics.swift in Sources */, 180255B82C3BD881005AFC5F /* MSTranscription.swift in Sources */, + 18A2DD192C40D94900A8731B /* CBC.swift in Sources */, + 18A2DD2F2C40D94900A8731B /* Codable.swift in Sources */, + 18A2DD2C2C40D94900A8731B /* IntegerConversion.swift in Sources */, + 18A2DCE22C40D94800A8731B /* UInt32+Extension.swift in Sources */, + 18A2DD022C40D94800A8731B /* SHA1.swift in Sources */, + 18A2DD2A2C40D94900A8731B /* Comparable.swift in Sources */, + 18A2DCCB2C40D94800A8731B /* ASN1Encoder.swift in Sources */, 1811883E2C1A8B5200C23FAB /* UTF8.swift in Sources */, 1811884E2C1AEF0F00C23FAB /* MSTranslate.swift in Sources */, + 18A2DCE72C40D94800A8731B /* RSA+Cipher.swift in Sources */, + 18A2DD1C2C40D94900A8731B /* PCBC.swift in Sources */, + 18A2DCEF2C40D94800A8731B /* Data+Extension.swift in Sources */, 18204BF82C19488800F94E69 /* StatusCode.swift in Sources */, 18C42B392C22F1FA000D8860 /* MSSttModel.swift in Sources */, 18C42C6F2C2422C2000D8860 /* AiAudioDecoder.m in Sources */, + 18A2DD382C40D94900A8731B /* BigInt.swift in Sources */, + 18A2DCF32C40D94800A8731B /* Rabbit+Foundation.swift in Sources */, + 18A2DCEA2C40D94800A8731B /* String+FoundationExtension.swift in Sources */, 18B74A1E2C1FDDB500FABBA9 /* MSChat.swift in Sources */, + 18A2DD322C40D94900A8731B /* BitwiseOps.swift in Sources */, + 18A2DCDB2C40D94800A8731B /* AES.Cryptors.swift in Sources */, + 18A2DD0D2C40D94800A8731B /* Checksum.swift in Sources */, + 18A2DCE12C40D94800A8731B /* XChaCha20.swift in Sources */, 18FBC2B72C38DA05002240AF /* MSChatGpt.swift in Sources */, + 18A2DD2D2C40D94900A8731B /* Division.swift in Sources */, + 18A2DD012C40D94800A8731B /* Rabbit.swift in Sources */, + 18A2DCE62C40D94800A8731B /* RSA+Signature.swift in Sources */, + 18A2DD232C40D94900A8731B /* Random.swift in Sources */, 184FFC542C21B091006F664D /* MSAigcModel.swift in Sources */, + 18A2DCD42C40D94800A8731B /* ISO10126Padding.swift in Sources */, + 18A2DCEE2C40D94800A8731B /* HMAC+Foundation.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/AEAD/AEAD.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/AEAD/AEAD.swift new file mode 100644 index 0000000..628f84c --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/AEAD/AEAD.swift @@ -0,0 +1,40 @@ +// +// AEAD.swift +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// +// + +// https://www.iana.org/assignments/aead-parameters/aead-parameters.xhtml + +/// Authenticated Encryption with Associated Data (AEAD) +internal protocol AEAD { + static var kLen: Int { get } // key length + static var ivRange: Range { get } // nonce length +} + +extension AEAD { + static func calculateAuthenticationTag(authenticator: Authenticator, cipherText: Array, authenticationHeader: Array) throws -> Array { + let headerPadding = ((16 - (authenticationHeader.count & 0xf)) & 0xf) + let cipherPadding = ((16 - (cipherText.count & 0xf)) & 0xf) + + var mac = authenticationHeader + mac += Array(repeating: 0, count: headerPadding) + mac += cipherText + mac += Array(repeating: 0, count: cipherPadding) + mac += UInt64(bigEndian: UInt64(authenticationHeader.count)).bytes() + mac += UInt64(bigEndian: UInt64(cipherText.count)).bytes() + + return try authenticator.authenticate(mac) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/AEAD/AEADChaCha20Poly1305.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/AEAD/AEADChaCha20Poly1305.swift new file mode 100644 index 0000000..f4c27b7 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/AEAD/AEADChaCha20Poly1305.swift @@ -0,0 +1,66 @@ +// +// AEADChaCha20Poly1305.swift +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// +// +// https://tools.ietf.org/html/rfc7539#section-2.8.1 + +/// AEAD_CHACHA20_POLY1305 +internal final class AEADChaCha20Poly1305: AEAD { + internal static let kLen = 32 // key length + internal static var ivRange = Range(12...12) + + /// Authenticated encryption + internal static func encrypt(_ plainText: Array, key: Array, iv: Array, authenticationHeader: Array) throws -> (cipherText: Array, authenticationTag: Array) { + let cipher = try ChaCha20(key: key, iv: iv) + return try self.encrypt(cipher: cipher, plainText, key: key, iv: iv, authenticationHeader: authenticationHeader) + } + + internal static func encrypt(cipher: Cipher, _ plainText: Array, key: Array, iv: Array, authenticationHeader: Array) throws -> (cipherText: Array, authenticationTag: Array) { + var polykey = Array(repeating: 0, count: kLen) + var toEncrypt = polykey + polykey = try cipher.encrypt(polykey) + toEncrypt += polykey + toEncrypt += plainText + + let fullCipherText = try cipher.encrypt(toEncrypt) + let cipherText = Array(fullCipherText.dropFirst(64)) + + let tag = try calculateAuthenticationTag(authenticator: Poly1305(key: polykey), cipherText: cipherText, authenticationHeader: authenticationHeader) + return (cipherText, tag) + } + + /// Authenticated decryption + internal static func decrypt(_ cipherText: Array, key: Array, iv: Array, authenticationHeader: Array, authenticationTag: Array) throws -> (plainText: Array, success: Bool) { + let cipher = try ChaCha20(key: key, iv: iv) + return try self.decrypt(cipher: cipher, cipherText: cipherText, key: key, iv: iv, authenticationHeader: authenticationHeader, authenticationTag: authenticationTag) + } + + static func decrypt(cipher: Cipher, cipherText: Array, key: Array, iv: Array, authenticationHeader: Array, authenticationTag: Array) throws -> (plainText: Array, success: Bool) { + + let polykey = try cipher.encrypt(Array(repeating: 0, count: self.kLen)) + let mac = try calculateAuthenticationTag(authenticator: Poly1305(key: polykey), cipherText: cipherText, authenticationHeader: authenticationHeader) + guard mac == authenticationTag else { + return (cipherText, false) + } + + var toDecrypt = Array(reserveCapacity: cipherText.count + 64) + toDecrypt += polykey + toDecrypt += polykey + toDecrypt += cipherText + let fullPlainText = try cipher.decrypt(toDecrypt) + let plainText = Array(fullPlainText.dropFirst(64)) + return (plainText, true) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/AEAD/AEADXChaCha20Poly1305.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/AEAD/AEADXChaCha20Poly1305.swift new file mode 100644 index 0000000..ca399d7 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/AEAD/AEADXChaCha20Poly1305.swift @@ -0,0 +1,83 @@ +// +// CryptoSwift +// +// Copyright (C) Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +/// This class implements the XChaCha20-Poly1305 Authenticated Encryption with +/// Associated Data (AEAD_XCHACHA20_POLY1305) construction, providing both encryption and authentication. +/// +/// For more information about the XChaCha20-Poly1305 algorithm, refer to the IETF draft: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha +internal final class AEADXChaCha20Poly1305: AEAD { + + /// The key length (in bytes) required for the XChaCha20 cipher (32 bytes). + internal static let kLen = 32 // key length + + /// The valid range of initialization vector lengths for the XChaCha20 cipher (12 bytes). + internal static var ivRange = Range(12...12) + + /// Encrypts the given plaintext using the XChaCha20 cipher and generates an authentication + /// tag using the Poly1305 MAC. + /// + /// - Parameters: + /// - plainText: The plaintext to be encrypted. + /// - key: The encryption key. + /// - iv: The initialization vector. + /// - authenticationHeader: The authentication header. + /// - Returns: A tuple containing the ciphertext and authentication tag. + /// - Throws: An error if encryption fails. + internal static func encrypt( + _ plainText: Array, + key: Array, + iv: Array, + authenticationHeader: Array + ) throws -> (cipherText: Array, authenticationTag: Array) { + try AEADChaCha20Poly1305.encrypt( + cipher: XChaCha20(key: key, iv: iv), + plainText, + key: key, + iv: iv, + authenticationHeader: authenticationHeader + ) + } + + /// Decrypts the given ciphertext using the XChaCha20 cipher and verifies the authentication + /// tag using the Poly1305 MAC. + /// + /// - Parameters: + /// - cipherText: The ciphertext to be decrypted. + /// - key: The decryption key. + /// - iv: The initialization vector. + /// - authenticationHeader: The authentication header. + /// - authenticationTag: The authentication tag. + /// - Returns: A tuple containing the decrypted plaintext and a boolean value indicating + /// the success of the decryption and authentication process. + /// - Throws: An error if decryption fails. + internal static func decrypt( + _ cipherText: Array, + key: Array, + iv: Array, + authenticationHeader: Array, + authenticationTag: Array + ) throws -> (plainText: Array, success: Bool) { + try AEADChaCha20Poly1305.decrypt( + cipher: XChaCha20(key: key, iv: iv), + cipherText: cipherText, + key: key, + iv: iv, + authenticationHeader: authenticationHeader, + authenticationTag: authenticationTag + ) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/AES.Cryptors.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/AES.Cryptors.swift new file mode 100644 index 0000000..f474ad3 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/AES.Cryptors.swift @@ -0,0 +1,39 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// MARK: Cryptors + +extension AES: Cryptors { + + internal func makeEncryptor() throws -> Cryptor & Updatable { + let blockSize = blockMode.customBlockSize ?? AES.blockSize + let worker = try blockMode.worker(blockSize: blockSize, cipherOperation: encrypt, encryptionOperation: encrypt) + if worker is StreamModeWorker { + return try StreamEncryptor(blockSize: blockSize, padding: padding, worker) + } + return try BlockEncryptor(blockSize: blockSize, padding: padding, worker) + } + + + internal func makeDecryptor() throws -> Cryptor & Updatable { + let blockSize = blockMode.customBlockSize ?? AES.blockSize + let cipherOperation: CipherOperationOnBlock = blockMode.options.contains(.useEncryptToDecrypt) == true ? encrypt : decrypt + let worker = try blockMode.worker(blockSize: blockSize, cipherOperation: cipherOperation, encryptionOperation: encrypt) + if worker is StreamModeWorker { + return try StreamDecryptor(blockSize: blockSize, padding: padding, worker) + } + return try BlockDecryptor(blockSize: blockSize, padding: padding, worker) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/AES.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/AES.swift new file mode 100644 index 0000000..883b963 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/AES.swift @@ -0,0 +1,556 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// Implementation of Gladman algorithm http://www.gladman.me.uk/AES +// + +/// The Advanced Encryption Standard (AES) +internal final class AES: BlockCipher { + internal enum Error: Swift.Error { + /// Invalid key + case invalidKeySize + /// Data padding is required + case dataPaddingRequired + /// Invalid Data + case invalidData + } + + internal enum Variant: Int { + case aes128 = 1, aes192, aes256 + + var Nk: Int { // Nk words + [4, 6, 8][self.rawValue - 1] + } + + var Nb: Int { // Nb words + 4 + } + + var Nr: Int { // Nr + self.Nk + 6 + } + } + + @usableFromInline + internal let variantNr: Int + + @usableFromInline + internal let variantNb: Int + + @usableFromInline + internal let variantNk: Int + + internal static let blockSize: Int = 16 // 128 /8 + internal let keySize: Int + + /// AES Variant + internal let variant: Variant + + // Parameters + let key: Key + + @usableFromInline + let blockMode: BlockMode + + @usableFromInline + let padding: Padding + + // + @usableFromInline + internal lazy var expandedKey: Array> = self.expandKey(self.key, variant: self.variant) + + @usableFromInline + internal lazy var expandedKeyInv: Array> = self.expandKeyInv(self.key, variant: self.variant) + + private lazy var sBoxes: (sBox: Array, invSBox: Array) = self.calculateSBox() + private lazy var sBox: Array = self.sBoxes.sBox + private lazy var sBoxInv: Array = self.sBoxes.invSBox + + // Parameters for Linear Congruence Generators + private static let Rcon: Array = [ + 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, + 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, + 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, + 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, + 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, + 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, + 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, + 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, + 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, + 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, + 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, + 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, + 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, + 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, + 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d + ] + + @usableFromInline static let T0: Array = [0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0xdf2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x3010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0xbf0f0fb, 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x2f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x8f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0xc040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0xf05050a, 0xb59a9a2f, 0x907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x0, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x6020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x4f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, 0xef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0xa06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac, 0x7f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x5030306, 0x1f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c] + @usableFromInline static let T0_INV: Array = [0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x2752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, 0x728ebb2, 0x3c2b52f, 0x9a7bc586, 0xa50837d3, 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x69f715e, 0x51106ebd, 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x55dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x0, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0xfe75793, 0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, 0xaba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0xb0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0xd927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x97826cd, 0xf418596e, 0x1b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x8cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, 0xf7daec41, 0xe50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546, 0x4ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0xc25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0] + @usableFromInline static let T1: Array = [0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x1010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x5050a0f, 0x9a9a2fb5, 0x7070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x0, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, 0xf9f9e910, 0x2020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0xc0c1814, 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0xb0b161d, 0xdbdbad76, 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0xa0a141e, 0x494992db, 0x6060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x8081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x3030605, 0xf6f6f701, 0xe0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, 0x8c8c038f, 0xa1a159f8, 0x89890980, 0xd0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, 0x2d2d5a77, 0xf0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a] + @usableFromInline static let T1_INV: Array = [0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, 0x459d1ff1, 0x58faacab, 0x3e34b93, 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0xeea4598, 0xc0fe5de1, 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b, 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x24b72e2, 0x8f1fe357, 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x837d3a5, 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5, 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51, 0x8a213ef9, 0x6dd963d, 0x53eddae, 0xbde64d46, 0x8d5491b5, 0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db, 0xa7ca147, 0xf427ce9, 0x1e84f8c9, 0x0, 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb, 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2, 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0xd090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x775af4c, 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3, 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef, 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0xbd49836, 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x99fead4, 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c, 0xca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, 0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x1a83971, 0xb30c08de, 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042] + @usableFromInline static let T2: Array = [0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x1020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x4080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x50a0f05, 0x9a2fb59a, 0x70e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x9121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x0, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, 0xf9e910f9, 0x2040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0xc18140c, 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0xb161d0b, 0xdbad76db, 0xe0db3be0, 0x32645632, 0x3a744e3a, 0xa141e0a, 0x4992db49, 0x60c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x8101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x3060503, 0xf6f701f6, 0xe1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, 0x8c038f8c, 0xa159f8a1, 0x89098089, 0xd1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, 0x2d5a772d, 0xf1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16] + @usableFromInline static let T2_INV: Array = [0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, 0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, 0xcc889176, 0x2f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e, 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x8f9942b, 0x48705868, 0x458f19fd, 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, 0x2830f287, 0xbf23b2a5, 0x302ba6a, 0x16ed5c82, 0xcf8a2b1c, 0x79a792b4, 0x7f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x506d5be, 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110, 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, 0xc471055d, 0x6046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x0, 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0xefdfbff, 0x850f5638, 0xae3d1ed5, 0x2d362739, 0xf0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, 0xa0cb167, 0x57930fe7, 0xeeb4d296, 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x90e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60, 0x1f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330, 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90, 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312, 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409, 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x4f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0xbfb2e41, 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, 0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0xdff4195, 0xa8397101, 0xc08deb3, 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257] + @usableFromInline static let T3: Array = [0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x2030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x80c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0xa0f0505, 0x2fb59a9a, 0xe090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x0, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, 0xe910f9f9, 0x4060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x58a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, 0x3bab9090, 0xb838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0xc0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x18c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, 0x61dcbdbd, 0xd868b8b, 0xf858a8a, 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x6050303, 0xf701f6f6, 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x7898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, 0x38f8c8c, 0x59f8a1a1, 0x9808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616] + @usableFromInline static let T3_INV: Array = [0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x3e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58, 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, 0x30f28728, 0x23b2a5bf, 0x2ba6a03, 0xed5c8216, 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x6d5be05, 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x532e18a, 0xa475ebf6, 0xb39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e, 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, 0x71055dc4, 0x46fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x7888b89, 0xe7385b19, 0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x0, 0x9838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e, 0xf563885, 0x3d1ed5ae, 0x3627392d, 0xa64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, 0xcb1670a, 0x930fe757, 0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0xe0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, 0xdec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f, 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, 0x467f5165, 0x9d04ea5e, 0x15d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, 0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x8deb30c, 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8] + @usableFromInline static let U1: Array = [0x0, 0xb0d090e, 0x161a121c, 0x1d171b12, 0x2c342438, 0x27392d36, 0x3a2e3624, 0x31233f2a, 0x58684870, 0x5365417e, 0x4e725a6c, 0x457f5362, 0x745c6c48, 0x7f516546, 0x62467e54, 0x694b775a, 0xb0d090e0, 0xbbdd99ee, 0xa6ca82fc, 0xadc78bf2, 0x9ce4b4d8, 0x97e9bdd6, 0x8afea6c4, 0x81f3afca, 0xe8b8d890, 0xe3b5d19e, 0xfea2ca8c, 0xf5afc382, 0xc48cfca8, 0xcf81f5a6, 0xd296eeb4, 0xd99be7ba, 0x7bbb3bdb, 0x70b632d5, 0x6da129c7, 0x66ac20c9, 0x578f1fe3, 0x5c8216ed, 0x41950dff, 0x4a9804f1, 0x23d373ab, 0x28de7aa5, 0x35c961b7, 0x3ec468b9, 0xfe75793, 0x4ea5e9d, 0x19fd458f, 0x12f04c81, 0xcb6bab3b, 0xc066a235, 0xdd71b927, 0xd67cb029, 0xe75f8f03, 0xec52860d, 0xf1459d1f, 0xfa489411, 0x9303e34b, 0x980eea45, 0x8519f157, 0x8e14f859, 0xbf37c773, 0xb43ace7d, 0xa92dd56f, 0xa220dc61, 0xf66d76ad, 0xfd607fa3, 0xe07764b1, 0xeb7a6dbf, 0xda595295, 0xd1545b9b, 0xcc434089, 0xc74e4987, 0xae053edd, 0xa50837d3, 0xb81f2cc1, 0xb31225cf, 0x82311ae5, 0x893c13eb, 0x942b08f9, 0x9f2601f7, 0x46bde64d, 0x4db0ef43, 0x50a7f451, 0x5baafd5f, 0x6a89c275, 0x6184cb7b, 0x7c93d069, 0x779ed967, 0x1ed5ae3d, 0x15d8a733, 0x8cfbc21, 0x3c2b52f, 0x32e18a05, 0x39ec830b, 0x24fb9819, 0x2ff69117, 0x8dd64d76, 0x86db4478, 0x9bcc5f6a, 0x90c15664, 0xa1e2694e, 0xaaef6040, 0xb7f87b52, 0xbcf5725c, 0xd5be0506, 0xdeb30c08, 0xc3a4171a, 0xc8a91e14, 0xf98a213e, 0xf2872830, 0xef903322, 0xe49d3a2c, 0x3d06dd96, 0x360bd498, 0x2b1ccf8a, 0x2011c684, 0x1132f9ae, 0x1a3ff0a0, 0x728ebb2, 0xc25e2bc, 0x656e95e6, 0x6e639ce8, 0x737487fa, 0x78798ef4, 0x495ab1de, 0x4257b8d0, 0x5f40a3c2, 0x544daacc, 0xf7daec41, 0xfcd7e54f, 0xe1c0fe5d, 0xeacdf753, 0xdbeec879, 0xd0e3c177, 0xcdf4da65, 0xc6f9d36b, 0xafb2a431, 0xa4bfad3f, 0xb9a8b62d, 0xb2a5bf23, 0x83868009, 0x888b8907, 0x959c9215, 0x9e919b1b, 0x470a7ca1, 0x4c0775af, 0x51106ebd, 0x5a1d67b3, 0x6b3e5899, 0x60335197, 0x7d244a85, 0x7629438b, 0x1f6234d1, 0x146f3ddf, 0x97826cd, 0x2752fc3, 0x335610e9, 0x385b19e7, 0x254c02f5, 0x2e410bfb, 0x8c61d79a, 0x876cde94, 0x9a7bc586, 0x9176cc88, 0xa055f3a2, 0xab58faac, 0xb64fe1be, 0xbd42e8b0, 0xd4099fea, 0xdf0496e4, 0xc2138df6, 0xc91e84f8, 0xf83dbbd2, 0xf330b2dc, 0xee27a9ce, 0xe52aa0c0, 0x3cb1477a, 0x37bc4e74, 0x2aab5566, 0x21a65c68, 0x10856342, 0x1b886a4c, 0x69f715e, 0xd927850, 0x64d90f0a, 0x6fd40604, 0x72c31d16, 0x79ce1418, 0x48ed2b32, 0x43e0223c, 0x5ef7392e, 0x55fa3020, 0x1b79aec, 0xaba93e2, 0x17ad88f0, 0x1ca081fe, 0x2d83bed4, 0x268eb7da, 0x3b99acc8, 0x3094a5c6, 0x59dfd29c, 0x52d2db92, 0x4fc5c080, 0x44c8c98e, 0x75ebf6a4, 0x7ee6ffaa, 0x63f1e4b8, 0x68fcedb6, 0xb1670a0c, 0xba6a0302, 0xa77d1810, 0xac70111e, 0x9d532e34, 0x965e273a, 0x8b493c28, 0x80443526, 0xe90f427c, 0xe2024b72, 0xff155060, 0xf418596e, 0xc53b6644, 0xce366f4a, 0xd3217458, 0xd82c7d56, 0x7a0ca137, 0x7101a839, 0x6c16b32b, 0x671bba25, 0x5638850f, 0x5d358c01, 0x40229713, 0x4b2f9e1d, 0x2264e947, 0x2969e049, 0x347efb5b, 0x3f73f255, 0xe50cd7f, 0x55dc471, 0x184adf63, 0x1347d66d, 0xcadc31d7, 0xc1d138d9, 0xdcc623cb, 0xd7cb2ac5, 0xe6e815ef, 0xede51ce1, 0xf0f207f3, 0xfbff0efd, 0x92b479a7, 0x99b970a9, 0x84ae6bbb, 0x8fa362b5, 0xbe805d9f, 0xb58d5491, 0xa89a4f83, 0xa397468d] + @usableFromInline static let U2: Array = [0x0, 0xd090e0b, 0x1a121c16, 0x171b121d, 0x3424382c, 0x392d3627, 0x2e36243a, 0x233f2a31, 0x68487058, 0x65417e53, 0x725a6c4e, 0x7f536245, 0x5c6c4874, 0x5165467f, 0x467e5462, 0x4b775a69, 0xd090e0b0, 0xdd99eebb, 0xca82fca6, 0xc78bf2ad, 0xe4b4d89c, 0xe9bdd697, 0xfea6c48a, 0xf3afca81, 0xb8d890e8, 0xb5d19ee3, 0xa2ca8cfe, 0xafc382f5, 0x8cfca8c4, 0x81f5a6cf, 0x96eeb4d2, 0x9be7bad9, 0xbb3bdb7b, 0xb632d570, 0xa129c76d, 0xac20c966, 0x8f1fe357, 0x8216ed5c, 0x950dff41, 0x9804f14a, 0xd373ab23, 0xde7aa528, 0xc961b735, 0xc468b93e, 0xe757930f, 0xea5e9d04, 0xfd458f19, 0xf04c8112, 0x6bab3bcb, 0x66a235c0, 0x71b927dd, 0x7cb029d6, 0x5f8f03e7, 0x52860dec, 0x459d1ff1, 0x489411fa, 0x3e34b93, 0xeea4598, 0x19f15785, 0x14f8598e, 0x37c773bf, 0x3ace7db4, 0x2dd56fa9, 0x20dc61a2, 0x6d76adf6, 0x607fa3fd, 0x7764b1e0, 0x7a6dbfeb, 0x595295da, 0x545b9bd1, 0x434089cc, 0x4e4987c7, 0x53eddae, 0x837d3a5, 0x1f2cc1b8, 0x1225cfb3, 0x311ae582, 0x3c13eb89, 0x2b08f994, 0x2601f79f, 0xbde64d46, 0xb0ef434d, 0xa7f45150, 0xaafd5f5b, 0x89c2756a, 0x84cb7b61, 0x93d0697c, 0x9ed96777, 0xd5ae3d1e, 0xd8a73315, 0xcfbc2108, 0xc2b52f03, 0xe18a0532, 0xec830b39, 0xfb981924, 0xf691172f, 0xd64d768d, 0xdb447886, 0xcc5f6a9b, 0xc1566490, 0xe2694ea1, 0xef6040aa, 0xf87b52b7, 0xf5725cbc, 0xbe0506d5, 0xb30c08de, 0xa4171ac3, 0xa91e14c8, 0x8a213ef9, 0x872830f2, 0x903322ef, 0x9d3a2ce4, 0x6dd963d, 0xbd49836, 0x1ccf8a2b, 0x11c68420, 0x32f9ae11, 0x3ff0a01a, 0x28ebb207, 0x25e2bc0c, 0x6e95e665, 0x639ce86e, 0x7487fa73, 0x798ef478, 0x5ab1de49, 0x57b8d042, 0x40a3c25f, 0x4daacc54, 0xdaec41f7, 0xd7e54ffc, 0xc0fe5de1, 0xcdf753ea, 0xeec879db, 0xe3c177d0, 0xf4da65cd, 0xf9d36bc6, 0xb2a431af, 0xbfad3fa4, 0xa8b62db9, 0xa5bf23b2, 0x86800983, 0x8b890788, 0x9c921595, 0x919b1b9e, 0xa7ca147, 0x775af4c, 0x106ebd51, 0x1d67b35a, 0x3e58996b, 0x33519760, 0x244a857d, 0x29438b76, 0x6234d11f, 0x6f3ddf14, 0x7826cd09, 0x752fc302, 0x5610e933, 0x5b19e738, 0x4c02f525, 0x410bfb2e, 0x61d79a8c, 0x6cde9487, 0x7bc5869a, 0x76cc8891, 0x55f3a2a0, 0x58faacab, 0x4fe1beb6, 0x42e8b0bd, 0x99fead4, 0x496e4df, 0x138df6c2, 0x1e84f8c9, 0x3dbbd2f8, 0x30b2dcf3, 0x27a9ceee, 0x2aa0c0e5, 0xb1477a3c, 0xbc4e7437, 0xab55662a, 0xa65c6821, 0x85634210, 0x886a4c1b, 0x9f715e06, 0x9278500d, 0xd90f0a64, 0xd406046f, 0xc31d1672, 0xce141879, 0xed2b3248, 0xe0223c43, 0xf7392e5e, 0xfa302055, 0xb79aec01, 0xba93e20a, 0xad88f017, 0xa081fe1c, 0x83bed42d, 0x8eb7da26, 0x99acc83b, 0x94a5c630, 0xdfd29c59, 0xd2db9252, 0xc5c0804f, 0xc8c98e44, 0xebf6a475, 0xe6ffaa7e, 0xf1e4b863, 0xfcedb668, 0x670a0cb1, 0x6a0302ba, 0x7d1810a7, 0x70111eac, 0x532e349d, 0x5e273a96, 0x493c288b, 0x44352680, 0xf427ce9, 0x24b72e2, 0x155060ff, 0x18596ef4, 0x3b6644c5, 0x366f4ace, 0x217458d3, 0x2c7d56d8, 0xca1377a, 0x1a83971, 0x16b32b6c, 0x1bba2567, 0x38850f56, 0x358c015d, 0x22971340, 0x2f9e1d4b, 0x64e94722, 0x69e04929, 0x7efb5b34, 0x73f2553f, 0x50cd7f0e, 0x5dc47105, 0x4adf6318, 0x47d66d13, 0xdc31d7ca, 0xd138d9c1, 0xc623cbdc, 0xcb2ac5d7, 0xe815efe6, 0xe51ce1ed, 0xf207f3f0, 0xff0efdfb, 0xb479a792, 0xb970a999, 0xae6bbb84, 0xa362b58f, 0x805d9fbe, 0x8d5491b5, 0x9a4f83a8, 0x97468da3] + @usableFromInline static let U3: Array = [0x0, 0x90e0b0d, 0x121c161a, 0x1b121d17, 0x24382c34, 0x2d362739, 0x36243a2e, 0x3f2a3123, 0x48705868, 0x417e5365, 0x5a6c4e72, 0x5362457f, 0x6c48745c, 0x65467f51, 0x7e546246, 0x775a694b, 0x90e0b0d0, 0x99eebbdd, 0x82fca6ca, 0x8bf2adc7, 0xb4d89ce4, 0xbdd697e9, 0xa6c48afe, 0xafca81f3, 0xd890e8b8, 0xd19ee3b5, 0xca8cfea2, 0xc382f5af, 0xfca8c48c, 0xf5a6cf81, 0xeeb4d296, 0xe7bad99b, 0x3bdb7bbb, 0x32d570b6, 0x29c76da1, 0x20c966ac, 0x1fe3578f, 0x16ed5c82, 0xdff4195, 0x4f14a98, 0x73ab23d3, 0x7aa528de, 0x61b735c9, 0x68b93ec4, 0x57930fe7, 0x5e9d04ea, 0x458f19fd, 0x4c8112f0, 0xab3bcb6b, 0xa235c066, 0xb927dd71, 0xb029d67c, 0x8f03e75f, 0x860dec52, 0x9d1ff145, 0x9411fa48, 0xe34b9303, 0xea45980e, 0xf1578519, 0xf8598e14, 0xc773bf37, 0xce7db43a, 0xd56fa92d, 0xdc61a220, 0x76adf66d, 0x7fa3fd60, 0x64b1e077, 0x6dbfeb7a, 0x5295da59, 0x5b9bd154, 0x4089cc43, 0x4987c74e, 0x3eddae05, 0x37d3a508, 0x2cc1b81f, 0x25cfb312, 0x1ae58231, 0x13eb893c, 0x8f9942b, 0x1f79f26, 0xe64d46bd, 0xef434db0, 0xf45150a7, 0xfd5f5baa, 0xc2756a89, 0xcb7b6184, 0xd0697c93, 0xd967779e, 0xae3d1ed5, 0xa73315d8, 0xbc2108cf, 0xb52f03c2, 0x8a0532e1, 0x830b39ec, 0x981924fb, 0x91172ff6, 0x4d768dd6, 0x447886db, 0x5f6a9bcc, 0x566490c1, 0x694ea1e2, 0x6040aaef, 0x7b52b7f8, 0x725cbcf5, 0x506d5be, 0xc08deb3, 0x171ac3a4, 0x1e14c8a9, 0x213ef98a, 0x2830f287, 0x3322ef90, 0x3a2ce49d, 0xdd963d06, 0xd498360b, 0xcf8a2b1c, 0xc6842011, 0xf9ae1132, 0xf0a01a3f, 0xebb20728, 0xe2bc0c25, 0x95e6656e, 0x9ce86e63, 0x87fa7374, 0x8ef47879, 0xb1de495a, 0xb8d04257, 0xa3c25f40, 0xaacc544d, 0xec41f7da, 0xe54ffcd7, 0xfe5de1c0, 0xf753eacd, 0xc879dbee, 0xc177d0e3, 0xda65cdf4, 0xd36bc6f9, 0xa431afb2, 0xad3fa4bf, 0xb62db9a8, 0xbf23b2a5, 0x80098386, 0x8907888b, 0x9215959c, 0x9b1b9e91, 0x7ca1470a, 0x75af4c07, 0x6ebd5110, 0x67b35a1d, 0x58996b3e, 0x51976033, 0x4a857d24, 0x438b7629, 0x34d11f62, 0x3ddf146f, 0x26cd0978, 0x2fc30275, 0x10e93356, 0x19e7385b, 0x2f5254c, 0xbfb2e41, 0xd79a8c61, 0xde94876c, 0xc5869a7b, 0xcc889176, 0xf3a2a055, 0xfaacab58, 0xe1beb64f, 0xe8b0bd42, 0x9fead409, 0x96e4df04, 0x8df6c213, 0x84f8c91e, 0xbbd2f83d, 0xb2dcf330, 0xa9ceee27, 0xa0c0e52a, 0x477a3cb1, 0x4e7437bc, 0x55662aab, 0x5c6821a6, 0x63421085, 0x6a4c1b88, 0x715e069f, 0x78500d92, 0xf0a64d9, 0x6046fd4, 0x1d1672c3, 0x141879ce, 0x2b3248ed, 0x223c43e0, 0x392e5ef7, 0x302055fa, 0x9aec01b7, 0x93e20aba, 0x88f017ad, 0x81fe1ca0, 0xbed42d83, 0xb7da268e, 0xacc83b99, 0xa5c63094, 0xd29c59df, 0xdb9252d2, 0xc0804fc5, 0xc98e44c8, 0xf6a475eb, 0xffaa7ee6, 0xe4b863f1, 0xedb668fc, 0xa0cb167, 0x302ba6a, 0x1810a77d, 0x111eac70, 0x2e349d53, 0x273a965e, 0x3c288b49, 0x35268044, 0x427ce90f, 0x4b72e202, 0x5060ff15, 0x596ef418, 0x6644c53b, 0x6f4ace36, 0x7458d321, 0x7d56d82c, 0xa1377a0c, 0xa8397101, 0xb32b6c16, 0xba25671b, 0x850f5638, 0x8c015d35, 0x97134022, 0x9e1d4b2f, 0xe9472264, 0xe0492969, 0xfb5b347e, 0xf2553f73, 0xcd7f0e50, 0xc471055d, 0xdf63184a, 0xd66d1347, 0x31d7cadc, 0x38d9c1d1, 0x23cbdcc6, 0x2ac5d7cb, 0x15efe6e8, 0x1ce1ede5, 0x7f3f0f2, 0xefdfbff, 0x79a792b4, 0x70a999b9, 0x6bbb84ae, 0x62b58fa3, 0x5d9fbe80, 0x5491b58d, 0x4f83a89a, 0x468da397] + @usableFromInline static let U4: Array = [0x0, 0xe0b0d09, 0x1c161a12, 0x121d171b, 0x382c3424, 0x3627392d, 0x243a2e36, 0x2a31233f, 0x70586848, 0x7e536541, 0x6c4e725a, 0x62457f53, 0x48745c6c, 0x467f5165, 0x5462467e, 0x5a694b77, 0xe0b0d090, 0xeebbdd99, 0xfca6ca82, 0xf2adc78b, 0xd89ce4b4, 0xd697e9bd, 0xc48afea6, 0xca81f3af, 0x90e8b8d8, 0x9ee3b5d1, 0x8cfea2ca, 0x82f5afc3, 0xa8c48cfc, 0xa6cf81f5, 0xb4d296ee, 0xbad99be7, 0xdb7bbb3b, 0xd570b632, 0xc76da129, 0xc966ac20, 0xe3578f1f, 0xed5c8216, 0xff41950d, 0xf14a9804, 0xab23d373, 0xa528de7a, 0xb735c961, 0xb93ec468, 0x930fe757, 0x9d04ea5e, 0x8f19fd45, 0x8112f04c, 0x3bcb6bab, 0x35c066a2, 0x27dd71b9, 0x29d67cb0, 0x3e75f8f, 0xdec5286, 0x1ff1459d, 0x11fa4894, 0x4b9303e3, 0x45980eea, 0x578519f1, 0x598e14f8, 0x73bf37c7, 0x7db43ace, 0x6fa92dd5, 0x61a220dc, 0xadf66d76, 0xa3fd607f, 0xb1e07764, 0xbfeb7a6d, 0x95da5952, 0x9bd1545b, 0x89cc4340, 0x87c74e49, 0xddae053e, 0xd3a50837, 0xc1b81f2c, 0xcfb31225, 0xe582311a, 0xeb893c13, 0xf9942b08, 0xf79f2601, 0x4d46bde6, 0x434db0ef, 0x5150a7f4, 0x5f5baafd, 0x756a89c2, 0x7b6184cb, 0x697c93d0, 0x67779ed9, 0x3d1ed5ae, 0x3315d8a7, 0x2108cfbc, 0x2f03c2b5, 0x532e18a, 0xb39ec83, 0x1924fb98, 0x172ff691, 0x768dd64d, 0x7886db44, 0x6a9bcc5f, 0x6490c156, 0x4ea1e269, 0x40aaef60, 0x52b7f87b, 0x5cbcf572, 0x6d5be05, 0x8deb30c, 0x1ac3a417, 0x14c8a91e, 0x3ef98a21, 0x30f28728, 0x22ef9033, 0x2ce49d3a, 0x963d06dd, 0x98360bd4, 0x8a2b1ccf, 0x842011c6, 0xae1132f9, 0xa01a3ff0, 0xb20728eb, 0xbc0c25e2, 0xe6656e95, 0xe86e639c, 0xfa737487, 0xf478798e, 0xde495ab1, 0xd04257b8, 0xc25f40a3, 0xcc544daa, 0x41f7daec, 0x4ffcd7e5, 0x5de1c0fe, 0x53eacdf7, 0x79dbeec8, 0x77d0e3c1, 0x65cdf4da, 0x6bc6f9d3, 0x31afb2a4, 0x3fa4bfad, 0x2db9a8b6, 0x23b2a5bf, 0x9838680, 0x7888b89, 0x15959c92, 0x1b9e919b, 0xa1470a7c, 0xaf4c0775, 0xbd51106e, 0xb35a1d67, 0x996b3e58, 0x97603351, 0x857d244a, 0x8b762943, 0xd11f6234, 0xdf146f3d, 0xcd097826, 0xc302752f, 0xe9335610, 0xe7385b19, 0xf5254c02, 0xfb2e410b, 0x9a8c61d7, 0x94876cde, 0x869a7bc5, 0x889176cc, 0xa2a055f3, 0xacab58fa, 0xbeb64fe1, 0xb0bd42e8, 0xead4099f, 0xe4df0496, 0xf6c2138d, 0xf8c91e84, 0xd2f83dbb, 0xdcf330b2, 0xceee27a9, 0xc0e52aa0, 0x7a3cb147, 0x7437bc4e, 0x662aab55, 0x6821a65c, 0x42108563, 0x4c1b886a, 0x5e069f71, 0x500d9278, 0xa64d90f, 0x46fd406, 0x1672c31d, 0x1879ce14, 0x3248ed2b, 0x3c43e022, 0x2e5ef739, 0x2055fa30, 0xec01b79a, 0xe20aba93, 0xf017ad88, 0xfe1ca081, 0xd42d83be, 0xda268eb7, 0xc83b99ac, 0xc63094a5, 0x9c59dfd2, 0x9252d2db, 0x804fc5c0, 0x8e44c8c9, 0xa475ebf6, 0xaa7ee6ff, 0xb863f1e4, 0xb668fced, 0xcb1670a, 0x2ba6a03, 0x10a77d18, 0x1eac7011, 0x349d532e, 0x3a965e27, 0x288b493c, 0x26804435, 0x7ce90f42, 0x72e2024b, 0x60ff1550, 0x6ef41859, 0x44c53b66, 0x4ace366f, 0x58d32174, 0x56d82c7d, 0x377a0ca1, 0x397101a8, 0x2b6c16b3, 0x25671bba, 0xf563885, 0x15d358c, 0x13402297, 0x1d4b2f9e, 0x472264e9, 0x492969e0, 0x5b347efb, 0x553f73f2, 0x7f0e50cd, 0x71055dc4, 0x63184adf, 0x6d1347d6, 0xd7cadc31, 0xd9c1d138, 0xcbdcc623, 0xc5d7cb2a, 0xefe6e815, 0xe1ede51c, 0xf3f0f207, 0xfdfbff0e, 0xa792b479, 0xa999b970, 0xbb84ae6b, 0xb58fa362, 0x9fbe805d, 0x91b58d54, 0x83a89a4f, 0x8da39746] + + /// Initialize AES with variant calculated out of key length: + /// - 16 bytes (AES-128) + /// - 24 bytes (AES-192) + /// - 32 bytes (AES-256) + /// + /// - parameter key: Key. Length of the key decides on AES variant. + /// - parameter iv: Initialization Vector (Optional for some blockMode values) + /// - parameter blockMode: Cipher mode of operation + /// - parameter padding: Padding method. .pkcs7, .noPadding, .zeroPadding, ... + /// + /// - throws: AES.Error + /// + /// - returns: Instance + internal init(key: Array, blockMode: BlockMode, padding: Padding = .pkcs7) throws { + self.key = Key(bytes: key) + self.blockMode = blockMode + self.padding = padding + self.keySize = self.key.count + + // Validate key size + switch self.keySize * 8 { + case 128: + self.variant = .aes128 + case 192: + self.variant = .aes192 + case 256: + self.variant = .aes256 + default: + throw Error.invalidKeySize + } + + self.variantNb = self.variant.Nb + self.variantNk = self.variant.Nk + self.variantNr = self.variant.Nr + } + + + internal func encrypt(block: ArraySlice) -> Array? { + if self.blockMode.options.contains(.paddingRequired) && block.count != AES.blockSize { + return Array(block) + } + + let rounds = self.variantNr + let rk = self.expandedKey + + let b00 = UInt32(block[block.startIndex.advanced(by: 0)]) + let b01 = UInt32(block[block.startIndex.advanced(by: 1)]) << 8 + let b02 = UInt32(block[block.startIndex.advanced(by: 2)]) << 16 + let b03 = UInt32(block[block.startIndex.advanced(by: 3)]) << 24 + var b0 = b00 | b01 | b02 | b03 + + let b10 = UInt32(block[block.startIndex.advanced(by: 4)]) + let b11 = UInt32(block[block.startIndex.advanced(by: 5)]) << 8 + let b12 = UInt32(block[block.startIndex.advanced(by: 6)]) << 16 + let b13 = UInt32(block[block.startIndex.advanced(by: 7)]) << 24 + var b1 = b10 | b11 | b12 | b13 + + let b20 = UInt32(block[block.startIndex.advanced(by: 8)]) + let b21 = UInt32(block[block.startIndex.advanced(by: 9)]) << 8 + let b22 = UInt32(block[block.startIndex.advanced(by: 10)]) << 16 + let b23 = UInt32(block[block.startIndex.advanced(by: 11)]) << 24 + var b2 = b20 | b21 | b22 | b23 + + let b30 = UInt32(block[block.startIndex.advanced(by: 12)]) + let b31 = UInt32(block[block.startIndex.advanced(by: 13)]) << 8 + let b32 = UInt32(block[block.startIndex.advanced(by: 14)]) << 16 + let b33 = UInt32(block[block.startIndex.advanced(by: 15)]) << 24 + var b3 = b30 | b31 | b32 | b33 + + let tLength = 4 + let t = UnsafeMutablePointer.allocate(capacity: tLength) + t.initialize(repeating: 0, count: tLength) + defer { + t.deinitialize(count: tLength) + t.deallocate() + } + + for r in 0..> 8) & 0xff)] + let lb02 = AES.T2[Int((t[2] >> 16) & 0xff)] + let lb03 = AES.T3[Int(t[3] >> 24)] + b0 = lb00 ^ lb01 ^ lb02 ^ lb03 + + let lb10 = AES.T0[Int(t[1] & 0xff)] + let lb11 = AES.T1[Int((t[2] >> 8) & 0xff)] + let lb12 = AES.T2[Int((t[3] >> 16) & 0xff)] + let lb13 = AES.T3[Int(t[0] >> 24)] + b1 = lb10 ^ lb11 ^ lb12 ^ lb13 + + let lb20 = AES.T0[Int(t[2] & 0xff)] + let lb21 = AES.T1[Int((t[3] >> 8) & 0xff)] + let lb22 = AES.T2[Int((t[0] >> 16) & 0xff)] + let lb23 = AES.T3[Int(t[1] >> 24)] + b2 = lb20 ^ lb21 ^ lb22 ^ lb23 + + let lb30 = AES.T0[Int(t[3] & 0xff)] + let lb31 = AES.T1[Int((t[0] >> 8) & 0xff)] + let lb32 = AES.T2[Int((t[1] >> 16) & 0xff)] + let lb33 = AES.T3[Int(t[2] >> 24)] + b3 = lb30 ^ lb31 ^ lb32 ^ lb33 + } + + // last round + let r = rounds - 1 + + t[0] = b0 ^ rk[r][0] + t[1] = b1 ^ rk[r][1] + t[2] = b2 ^ rk[r][2] + t[3] = b3 ^ rk[r][3] + + // rounds + b0 = F1(t[0], t[1], t[2], t[3]) ^ rk[rounds][0] + b1 = F1(t[1], t[2], t[3], t[0]) ^ rk[rounds][1] + b2 = F1(t[2], t[3], t[0], t[1]) ^ rk[rounds][2] + b3 = F1(t[3], t[0], t[1], t[2]) ^ rk[rounds][3] + + let encrypted: Array = [ + UInt8(b0 & 0xff), UInt8((b0 >> 8) & 0xff), UInt8((b0 >> 16) & 0xff), UInt8((b0 >> 24) & 0xff), + UInt8(b1 & 0xff), UInt8((b1 >> 8) & 0xff), UInt8((b1 >> 16) & 0xff), UInt8((b1 >> 24) & 0xff), + UInt8(b2 & 0xff), UInt8((b2 >> 8) & 0xff), UInt8((b2 >> 16) & 0xff), UInt8((b2 >> 24) & 0xff), + UInt8(b3 & 0xff), UInt8((b3 >> 8) & 0xff), UInt8((b3 >> 16) & 0xff), UInt8((b3 >> 24) & 0xff) + ] + return encrypted + } + + @usableFromInline + internal func decrypt(block: ArraySlice) -> Array? { + if self.blockMode.options.contains(.paddingRequired) && block.count != AES.blockSize { + return Array(block) + } + + let rounds = self.variantNr + let rk = self.expandedKeyInv + + // Save miliseconds by not using `block.toUInt32Array()` + let b00 = UInt32(block[block.startIndex.advanced(by: 0)]) + let b01 = UInt32(block[block.startIndex.advanced(by: 1)]) << 8 + let b02 = UInt32(block[block.startIndex.advanced(by: 2)]) << 16 + let b03 = UInt32(block[block.startIndex.advanced(by: 3)]) << 24 + var b0 = b00 | b01 | b02 | b03 + + let b10 = UInt32(block[block.startIndex.advanced(by: 4)]) + let b11 = UInt32(block[block.startIndex.advanced(by: 5)]) << 8 + let b12 = UInt32(block[block.startIndex.advanced(by: 6)]) << 16 + let b13 = UInt32(block[block.startIndex.advanced(by: 7)]) << 24 + var b1 = b10 | b11 | b12 | b13 + + let b20 = UInt32(block[block.startIndex.advanced(by: 8)]) + let b21 = UInt32(block[block.startIndex.advanced(by: 9)]) << 8 + let b22 = UInt32(block[block.startIndex.advanced(by: 10)]) << 16 + let b23 = UInt32(block[block.startIndex.advanced(by: 11)]) << 24 + var b2 = b20 | b21 | b22 | b23 + + let b30 = UInt32(block[block.startIndex.advanced(by: 12)]) + let b31 = UInt32(block[block.startIndex.advanced(by: 13)]) << 8 + let b32 = UInt32(block[block.startIndex.advanced(by: 14)]) << 16 + let b33 = UInt32(block[block.startIndex.advanced(by: 15)]) << 24 + var b3 = b30 | b31 | b32 | b33 + + let tLength = 4 + let t = UnsafeMutablePointer.allocate(capacity: tLength) + t.initialize(repeating: 0, count: tLength) + defer { + t.deinitialize(count: tLength) + t.deallocate() + } + + for r in (2...rounds).reversed() { + t[0] = b0 ^ rk[r][0] + t[1] = b1 ^ rk[r][1] + t[2] = b2 ^ rk[r][2] + t[3] = b3 ^ rk[r][3] + + let b00 = AES.T0_INV[Int(t[0] & 0xff)] + let b01 = AES.T1_INV[Int((t[3] >> 8) & 0xff)] + let b02 = AES.T2_INV[Int((t[2] >> 16) & 0xff)] + let b03 = AES.T3_INV[Int(t[1] >> 24)] + b0 = b00 ^ b01 ^ b02 ^ b03 + + let b10 = AES.T0_INV[Int(t[1] & 0xff)] + let b11 = AES.T1_INV[Int((t[0] >> 8) & 0xff)] + let b12 = AES.T2_INV[Int((t[3] >> 16) & 0xff)] + let b13 = AES.T3_INV[Int(t[2] >> 24)] + b1 = b10 ^ b11 ^ b12 ^ b13 + + let b20 = AES.T0_INV[Int(t[2] & 0xff)] + let b21 = AES.T1_INV[Int((t[1] >> 8) & 0xff)] + let b22 = AES.T2_INV[Int((t[0] >> 16) & 0xff)] + let b23 = AES.T3_INV[Int(t[3] >> 24)] + b2 = b20 ^ b21 ^ b22 ^ b23 + + let b30 = AES.T0_INV[Int(t[3] & 0xff)] + let b31 = AES.T1_INV[Int((t[2] >> 8) & 0xff)] + let b32 = AES.T2_INV[Int((t[1] >> 16) & 0xff)] + let b33 = AES.T3_INV[Int(t[0] >> 24)] + b3 = b30 ^ b31 ^ b32 ^ b33 + } + + // last round + t[0] = b0 ^ rk[1][0] + t[1] = b1 ^ rk[1][1] + t[2] = b2 ^ rk[1][2] + t[3] = b3 ^ rk[1][3] + + // rounds + + let lb00 = self.sBoxInv[Int(B0(t[0]))] + let lb01 = (sBoxInv[Int(B1(t[3]))] << 8) + let lb02 = (sBoxInv[Int(B2(t[2]))] << 16) + let lb03 = (sBoxInv[Int(B3(t[1]))] << 24) + b0 = lb00 | lb01 | lb02 | lb03 ^ rk[0][0] + + let lb10 = self.sBoxInv[Int(B0(t[1]))] + let lb11 = (sBoxInv[Int(B1(t[0]))] << 8) + let lb12 = (sBoxInv[Int(B2(t[3]))] << 16) + let lb13 = (sBoxInv[Int(B3(t[2]))] << 24) + b1 = lb10 | lb11 | lb12 | lb13 ^ rk[0][1] + + let lb20 = self.sBoxInv[Int(B0(t[2]))] + let lb21 = (sBoxInv[Int(B1(t[1]))] << 8) + let lb22 = (sBoxInv[Int(B2(t[0]))] << 16) + let lb23 = (sBoxInv[Int(B3(t[3]))] << 24) + b2 = lb20 | lb21 | lb22 | lb23 ^ rk[0][2] + + let lb30 = self.sBoxInv[Int(B0(t[3]))] + let lb31 = (sBoxInv[Int(B1(t[2]))] << 8) + let lb32 = (sBoxInv[Int(B2(t[1]))] << 16) + let lb33 = (sBoxInv[Int(B3(t[0]))] << 24) + b3 = lb30 | lb31 | lb32 | lb33 ^ rk[0][3] + + let result: Array = [ + UInt8(b0 & 0xff), UInt8((b0 >> 8) & 0xff), UInt8((b0 >> 16) & 0xff), UInt8((b0 >> 24) & 0xff), + UInt8(b1 & 0xff), UInt8((b1 >> 8) & 0xff), UInt8((b1 >> 16) & 0xff), UInt8((b1 >> 24) & 0xff), + UInt8(b2 & 0xff), UInt8((b2 >> 8) & 0xff), UInt8((b2 >> 16) & 0xff), UInt8((b2 >> 24) & 0xff), + UInt8(b3 & 0xff), UInt8((b3 >> 8) & 0xff), UInt8((b3 >> 16) & 0xff), UInt8((b3 >> 24) & 0xff) + ] + return result + } +} + +extension AES { + private func expandKeyInv(_ key: Key, variant: Variant) -> Array> { + let rounds = self.variantNr + var rk2: Array> = self.expandKey(key, variant: variant) + + for r in 1.. Array> { + func convertExpandedKey(_ expanded: Array) -> Array> { + expanded.batched(by: 4).map({ UInt32(bytes: $0.reversed()) }).batched(by: 4).map { Array($0) } + } + + /* + * Function used in the Key Expansion routine that takes a four-byte + * input word and applies an S-box to each of the four bytes to + * produce an output word. + */ + func subWord(_ word: Array) -> Array { + precondition(word.count == 4) + + var result = word + for i in 0..<4 { + result[i] = UInt8(self.sBox[Int(word[i])]) + } + return result + } + + @inline(__always) + func subWordInPlace(_ word: inout Array) { + precondition(word.count == 4) + word[0] = UInt8(self.sBox[Int(word[0])]) + word[1] = UInt8(self.sBox[Int(word[1])]) + word[2] = UInt8(self.sBox[Int(word[2])]) + word[3] = UInt8(self.sBox[Int(word[3])]) + } + + let wLength = self.variantNb * (self.variantNr + 1) * 4 + let w = UnsafeMutablePointer.allocate(capacity: wLength) + w.initialize(repeating: 0, count: wLength) + defer { + w.deinitialize(count: wLength) + w.deallocate() + } + + for i in 0.. + + for i in self.variantNk..(repeating: 0, count: 4) + + for wordIdx in 0..<4 { + tmp[wordIdx] = w[4 * (i - 1) + wordIdx] + } + if (i % self.variantNk) == 0 { + tmp = subWord(rotateLeft(UInt32(bytes: tmp), by: 8).bytes(totalBytes: 4)) + tmp[0] = tmp.first! ^ AES.Rcon[i / variantNk] + } else if self.variantNk > 6 && (i % self.variantNk) == 4 { + subWordInPlace(&tmp) + } + + // xor array of bytes + for wordIdx in 0..<4 { + w[4 * i + wordIdx] = w[4 * (i - variantNk) + wordIdx] ^ tmp[wordIdx] + } + } + return convertExpandedKey(Array(UnsafeBufferPointer(start: w, count: wLength))) + } + + @inline(__always) + private func B0(_ x: UInt32) -> UInt32 { + x & 0xff + } + + @inline(__always) + private func B1(_ x: UInt32) -> UInt32 { + (x >> 8) & 0xff + } + + @inline(__always) + private func B2(_ x: UInt32) -> UInt32 { + (x >> 16) & 0xff + } + + @inline(__always) + private func B3(_ x: UInt32) -> UInt32 { + (x >> 24) & 0xff + } + + @inline(__always) @usableFromInline + internal func F1(_ x0: UInt32, _ x1: UInt32, _ x2: UInt32, _ x3: UInt32) -> UInt32 { + var result: UInt32 = 0 + result |= UInt32(self.B1(AES.T0[Int(x0 & 255)])) + result |= UInt32(self.B1(AES.T0[Int((x1 >> 8) & 255)])) << 8 + result |= UInt32(self.B1(AES.T0[Int((x2 >> 16) & 255)])) << 16 + result |= UInt32(self.B1(AES.T0[Int(x3 >> 24)])) << 24 + return result + } + + private func calculateSBox() -> (sBox: Array, invSBox: Array) { + let sboxLength = 256 + let sbox = UnsafeMutablePointer.allocate(capacity: sboxLength) + let invsbox = UnsafeMutablePointer.allocate(capacity: sboxLength) + sbox.initialize(repeating: 0, count: sboxLength) + invsbox.initialize(repeating: 0, count: sboxLength) + defer { + sbox.deinitialize(count: sboxLength) + sbox.deallocate() + invsbox.deinitialize(count: sboxLength) + invsbox.deallocate() + } + + sbox[0] = 0x63 + + var p: UInt8 = 1, q: UInt8 = 1 + + repeat { + p = p ^ (UInt8(truncatingIfNeeded: Int(p) << 1) ^ ((p & 0x80) == 0x80 ? 0x1b : 0)) + q ^= q << 1 + q ^= q << 2 + q ^= q << 4 + q ^= (q & 0x80) == 0x80 ? 0x09 : 0 + + let s = 0x63 ^ q ^ rotateLeft(q, by: 1) ^ rotateLeft(q, by: 2) ^ rotateLeft(q, by: 3) ^ rotateLeft(q, by: 4) + + sbox[Int(p)] = UInt32(s) + invsbox[Int(s)] = UInt32(p) + } while p != 1 + + return (sBox: Array(UnsafeBufferPointer(start: sbox, count: sboxLength)), invSBox: Array(UnsafeBufferPointer(start: invsbox, count: sboxLength))) + } +} + +// MARK: Cipher + +extension AES: Cipher { + + internal func encrypt(_ bytes: ArraySlice) throws -> Array { + let blockSize = self.blockMode.customBlockSize ?? AES.blockSize + let chunks = bytes.batched(by: blockSize) + + var oneTimeCryptor = try makeEncryptor() + var out = Array(reserveCapacity: bytes.count) + for chunk in chunks { + out += try oneTimeCryptor.update(withBytes: chunk, isLast: false) + } + // Padding may be added at the very end + out += try oneTimeCryptor.finish() + + if self.blockMode.options.contains(.paddingRequired) && (out.count % AES.blockSize != 0) { + throw Error.dataPaddingRequired + } + + return out + } + + + internal func decrypt(_ bytes: ArraySlice) throws -> Array { + if self.blockMode.options.contains(.paddingRequired) && (bytes.count % AES.blockSize != 0) { + throw Error.dataPaddingRequired + } + + var oneTimeCryptor = try makeDecryptor() + let chunks = bytes.batched(by: AES.blockSize) + if chunks.isEmpty { + throw Error.invalidData + } + + var out = Array(reserveCapacity: bytes.count) + + var lastIdx = chunks.startIndex + chunks.indices.formIndex(&lastIdx, offsetBy: chunks.count - 1) + + // To properly remove padding, `isLast` has to be known when called with the last chunk of ciphertext + // Last chunk of ciphertext may contains padded data so next call to update(..) won't be able to remove it + for idx in chunks.indices { + out += try oneTimeCryptor.update(withBytes: chunks[idx], isLast: idx == lastIdx) + } + return out + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/ASN1/ASN1.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/ASN1/ASN1.swift new file mode 100644 index 0000000..e1e289a --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/ASN1/ASN1.swift @@ -0,0 +1,87 @@ +// +// CryptoSwift +// +// Copyright (C) Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// +// ASN1 Code inspired by Asn1Parser.swift from SwiftyRSA + +import Foundation + +/// A Partial ASN.1 (Abstract Syntax Notation 1) Encoder & Decoder Implementation. +/// +/// - Note: This implementation is limited to a few core types and is not an exhaustive / complete ASN1 implementation +/// - Warning: This implementation has been developed for encoding and decoding DER & PEM files specifically. If you're using this Encoder/Decoder on other ASN1 structures, make sure you test the expected behavior appropriately. +enum ASN1 { + internal enum IDENTIFIERS: UInt8, Equatable { + case SEQUENCE = 0x30 + case INTERGER = 0x02 + case OBJECTID = 0x06 + case NULL = 0x05 + case BITSTRING = 0x03 + case OCTETSTRING = 0x04 + + static func == (lhs: UInt8, rhs: IDENTIFIERS) -> Bool { + lhs == rhs.rawValue + } + + var bytes: [UInt8] { + switch self { + case .NULL: + return [self.rawValue, 0x00] + default: + return [self.rawValue] + } + } + } + + /// An ASN1 node + internal enum Node: CustomStringConvertible { + /// An array of more `ASN1.Node`s + case sequence(nodes: [Node]) + /// An integer + /// - Note: This ASN1 Encoder makes no assumptions about the sign and bit order of the integers passed in. The conversion from Integer to Data is your responsiblity. + case integer(data: Data) + /// An objectIdentifier + case objectIdentifier(data: Data) + /// A null object + case null + /// A bitString + case bitString(data: Data) + /// An octetString + case octetString(data: Data) + + var description: String { + ASN1.printNode(self, level: 0) + } + } + + internal static func printNode(_ node: ASN1.Node, level: Int) -> String { + var str: [String] = [] + let prefix = String(repeating: "\t", count: level) + switch node { + case .integer(let int): + str.append("\(prefix)Integer: \(int.toHexString())") + case .bitString(let bs): + str.append("\(prefix)BitString: \(bs.toHexString())") + case .null: + str.append("\(prefix)NULL") + case .objectIdentifier(let oid): + str.append("\(prefix)ObjectID: \(oid.toHexString())") + case .octetString(let os): + str.append("\(prefix)OctetString: \(os.toHexString())") + case .sequence(let nodes): + str.append("\(prefix)Sequence:") + nodes.forEach { str.append(printNode($0, level: level + 1)) } + } + return str.joined(separator: "\n") + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/ASN1/ASN1Decoder.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/ASN1/ASN1Decoder.swift new file mode 100644 index 0000000..dfcb225 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/ASN1/ASN1Decoder.swift @@ -0,0 +1,107 @@ +// +// CryptoSwift +// +// Copyright (C) Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// +// ASN1 Code inspired by Asn1Parser.swift from SwiftyRSA + +import Foundation + +extension ASN1 { + /// A simple ASN1 parser that will recursively iterate over a root node and return a Node tree. + /// The root node can be any of the supported nodes described in `Node`. If the parser encounters a sequence + /// it will recursively parse its children. + enum Decoder { + + enum DecodingError: Error { + case noType + case invalidType(value: UInt8) + } + + /// Parses ASN1 data and returns its root node. + /// + /// - Parameter data: ASN1 data to parse + /// - Returns: Root ASN1 Node + /// - Throws: A DecodingError if anything goes wrong, or if an unknown node was encountered + static func decode(data: Data) throws -> Node { + let scanner = ASN1.Scanner(data: data) + let node = try decodeNode(scanner: scanner) + return node + } + + /// Parses an ASN1 given an existing scanner. + /// @warning: this will modify the state (ie: position) of the provided scanner. + /// + /// - Parameter scanner: Scanner to use to consume the data + /// - Returns: Parsed node + /// - Throws: A DecodingError if anything goes wrong, or if an unknown node was encountered + private static func decodeNode(scanner: ASN1.Scanner) throws -> Node { + + let firstByte = try scanner.consume(length: 1).firstByte + + switch firstByte { + case IDENTIFIERS.SEQUENCE.rawValue: + let length = try scanner.consumeLength() + let data = try scanner.consume(length: length) + let nodes = try decodeSequence(data: data) + return .sequence(nodes: nodes) + + case IDENTIFIERS.INTERGER.rawValue: + let length = try scanner.consumeLength() + let data = try scanner.consume(length: length) + return .integer(data: data) + + case IDENTIFIERS.OBJECTID.rawValue: + let length = try scanner.consumeLength() + let data = try scanner.consume(length: length) + return .objectIdentifier(data: data) + + case IDENTIFIERS.NULL.rawValue: + _ = try scanner.consume(length: 1) + return .null + + case IDENTIFIERS.BITSTRING.rawValue: + let length = try scanner.consumeLength() + + // There's an extra byte (0x00) after the bit string length in all the keys I've encountered. + // I couldn't find a specification that referenced this extra byte, but let's consume it and discard it. + _ = try scanner.consume(length: 1) + + let data = try scanner.consume(length: length - 1) + return .bitString(data: data) + + case IDENTIFIERS.OCTETSTRING.rawValue: + let length = try scanner.consumeLength() + let data = try scanner.consume(length: length) + return .octetString(data: data) + + default: + throw DecodingError.invalidType(value: firstByte) + } + } + + /// Parses an ASN1 sequence and returns its child nodes + /// + /// - Parameter data: ASN1 data + /// - Returns: A list of ASN1 nodes + /// - Throws: A DecodingError if anything goes wrong, or if an unknown node was encountered + private static func decodeSequence(data: Data) throws -> [Node] { + let scanner = ASN1.Scanner(data: data) + var nodes: [Node] = [] + while !scanner.isComplete { + let node = try decodeNode(scanner: scanner) + nodes.append(node) + } + return nodes + } + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/ASN1/ASN1Encoder.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/ASN1/ASN1Encoder.swift new file mode 100644 index 0000000..3748d12 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/ASN1/ASN1Encoder.swift @@ -0,0 +1,69 @@ +// +// CryptoSwift +// +// Copyright (C) Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// +// ASN1 Code inspired by Asn1Parser.swift from SwiftyRSA + +import Foundation + +extension ASN1 { + enum Encoder { + /// Encodes an ASN1Node into it's byte representation + /// + /// - Parameter node: The Node to encode + /// - Returns: The encoded bytes as a UInt8 array + /// + /// - Warning: This ASN.1 encoder has only been tested to work on certain ASN.1 data structures such as DER and PEM files. Before using this encoder for another application, ensure you test it's behavior accordingly. + /// - Warning: This encoder makes no assumptions regarding Integer bit layout and signage. The proper serialization of Integers is left up to the user. + internal static func encode(_ node: ASN1.Node) -> [UInt8] { + switch node { + case .integer(let integer): + return IDENTIFIERS.INTERGER.bytes + self.asn1LengthPrefixed(integer.bytes) + case .bitString(let bits): + return IDENTIFIERS.BITSTRING.bytes + self.asn1LengthPrefixed([0x00] + bits.bytes) + case .octetString(let octet): + return IDENTIFIERS.OCTETSTRING.bytes + self.asn1LengthPrefixed(octet.bytes) + case .null: + return IDENTIFIERS.NULL.bytes + case .objectIdentifier(let oid): + return IDENTIFIERS.OBJECTID.bytes + self.asn1LengthPrefixed(oid.bytes) + case .sequence(let nodes): + return IDENTIFIERS.SEQUENCE.bytes + self.asn1LengthPrefixed( nodes.reduce(into: Array(), { partialResult, node in + partialResult += encode(node) + })) + } + } + + /// Calculates and returns the ASN.1 length Prefix for a chunk of data + /// + /// - Parameter bytes: The bytes to be length prefixed + /// - Returns: The ASN.1 length Prefix for this chuck of data (excluding the passed in data) + private static func asn1LengthPrefix(_ bytes: [UInt8]) -> [UInt8] { + if bytes.count >= 0x80 { + var lengthAsBytes = withUnsafeBytes(of: bytes.count.bigEndian, Array.init) + while lengthAsBytes.first == 0 { lengthAsBytes.removeFirst() } + return [0x80 + UInt8(lengthAsBytes.count)] + lengthAsBytes + } else { + return [UInt8(bytes.count)] + } + } + + /// Prefixes the provided bytes with the appropriate ASN.1 length prefix + /// + /// - Parameter bytes: The bytes to be length prefixed + /// - Returns: The provided bytes with the appropriate ASN.1 length prefix prepended + private static func asn1LengthPrefixed(_ bytes: [UInt8]) -> [UInt8] { + self.asn1LengthPrefix(bytes) + bytes + } + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/ASN1/ASN1Scanner.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/ASN1/ASN1Scanner.swift new file mode 100644 index 0000000..886354a --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/ASN1/ASN1Scanner.swift @@ -0,0 +1,123 @@ +// +// CryptoSwift +// +// Copyright (C) Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// +// ASN1 Scanner code is from Asn1Parser.swift from SwiftyRSA + +import Foundation + +extension ASN1 { + /// Simple data scanner that consumes bytes from a raw data and keeps an updated position. + internal class Scanner { + + enum ScannerError: Error { + case outOfBounds + } + + let data: Data + var index: Int = 0 + + /// Returns whether there is no more data to consume + var isComplete: Bool { + return self.index >= self.data.count + } + + /// Creates a scanner with provided data + /// + /// - Parameter data: Data to consume + init(data: Data) { + self.data = data + } + + /// Consumes data of provided length and returns it + /// + /// - Parameter length: length of the data to consume + /// - Returns: data consumed + /// - Throws: ScannerError.outOfBounds error if asked to consume too many bytes + func consume(length: Int) throws -> Data { + + guard length > 0 else { + return Data() + } + + guard self.index + length <= self.data.count else { + throw ScannerError.outOfBounds + } + + let subdata = self.data.subdata(in: self.index.. Int { + + let lengthByte = try consume(length: 1).firstByte + + // If the first byte's value is less than 0x80, it directly contains the length + // so we can return it + guard lengthByte >= 0x80 else { + return Int(lengthByte) + } + + // If the first byte's value is more than 0x80, it indicates how many following bytes + // will describe the length. For instance, 0x85 indicates that 0x85 - 0x80 = 0x05 = 5 + // bytes will describe the length, so we need to read the 5 next bytes and get their integer + // value to determine the length. + let nextByteCount = lengthByte - 0x80 + let length = try consume(length: Int(nextByteCount)) + + return length.integer + } + } +} + +internal extension Data { + + /// Returns the first byte of the current data + var firstByte: UInt8 { + var byte: UInt8 = 0 + copyBytes(to: &byte, count: MemoryLayout.size) + return byte + } + + /// Returns the integer value of the current data. + /// - Warning: This only supports data up to 4 bytes, as we can only extract 32-bit integers. + var integer: Int { + + guard count > 0 else { + return 0 + } + + var int: UInt32 = 0 + var offset: Int32 = Int32(count - 1) + forEach { byte in + let byte32 = UInt32(byte) + let shifted = byte32 << (UInt32(offset) * 8) + int = int | shifted + offset -= 1 + } + + return Int(int) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Array+Extension.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Array+Extension.swift new file mode 100644 index 0000000..02e7584 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Array+Extension.swift @@ -0,0 +1,155 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +extension Array { + + init(reserveCapacity: Int) { + self = Array() + self.reserveCapacity(reserveCapacity) + } + + + var slice: ArraySlice { + self[self.startIndex ..< self.endIndex] + } + + + subscript (safe index: Index) -> Element? { + return indices.contains(index) ? self[index] : nil + } +} + +extension Array where Element == UInt8 { + internal init(hex: String) { + self.init(reserveCapacity: hex.unicodeScalars.lazy.underestimatedCount) + var buffer: UInt8? + var skip = hex.hasPrefix("0x") ? 2 : 0 + for char in hex.unicodeScalars.lazy { + guard skip == 0 else { + skip -= 1 + continue + } + guard char.value >= 48 && char.value <= 102 else { + removeAll() + return + } + let v: UInt8 + let c: UInt8 = UInt8(char.value) + switch c { + case let c where c <= 57: + v = c - 48 + case let c where c >= 65 && c <= 70: + v = c - 55 + case let c where c >= 97: + v = c - 87 + default: + removeAll() + return + } + if let b = buffer { + append(b << 4 | v) + buffer = nil + } else { + buffer = v + } + } + if let b = buffer { + append(b) + } + } + + internal func toHexString() -> String { + `lazy`.reduce(into: "") { + var s = String($1, radix: 16) + if s.count == 1 { + s = "0" + s + } + $0 += s + } + } +} + +extension Array where Element == UInt8 { + /// split in chunks with given chunk size + @available(*, deprecated) + internal func chunks(size chunksize: Int) -> Array> { + var words = Array>() + words.reserveCapacity(count / chunksize) + for idx in stride(from: chunksize, through: count, by: chunksize) { + words.append(Array(self[idx - chunksize ..< idx])) // slow for large table + } + let remainder = suffix(count % chunksize) + if !remainder.isEmpty { + words.append(Array(remainder)) + } + return words + } + + internal func md5() -> [Element] { + Digest.md5(self) + } + + internal func sha1() -> [Element] { + Digest.sha1(self) + } + + internal func sha224() -> [Element] { + Digest.sha224(self) + } + + internal func sha256() -> [Element] { + Digest.sha256(self) + } + + internal func sha384() -> [Element] { + Digest.sha384(self) + } + + internal func sha512() -> [Element] { + Digest.sha512(self) + } + + internal func sha2(_ variant: SHA2.Variant) -> [Element] { + Digest.sha2(self, variant: variant) + } + + internal func sha3(_ variant: SHA3.Variant) -> [Element] { + Digest.sha3(self, variant: variant) + } + + internal func crc32(seed: UInt32? = nil, reflect: Bool = true) -> UInt32 { + Checksum.crc32(self, seed: seed, reflect: reflect) + } + + internal func crc32c(seed: UInt32? = nil, reflect: Bool = true) -> UInt32 { + Checksum.crc32c(self, seed: seed, reflect: reflect) + } + + internal func crc16(seed: UInt16? = nil) -> UInt16 { + Checksum.crc16(self, seed: seed) + } + + internal func encrypt(cipher: Cipher) throws -> [Element] { + try cipher.encrypt(self.slice) + } + + internal func decrypt(cipher: Cipher) throws -> [Element] { + try cipher.decrypt(self.slice) + } + + internal func authenticate(with authenticator: A) throws -> [Element] { + try authenticator.authenticate(self) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Authenticator.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Authenticator.swift new file mode 100644 index 0000000..9b99313 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Authenticator.swift @@ -0,0 +1,20 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +/// Message authentication code. +internal protocol Authenticator { + /// Calculate Message Authentication Code (MAC) for message. + func authenticate(_ bytes: Array) throws -> Array +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/BatchedCollection.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/BatchedCollection.swift new file mode 100644 index 0000000..d285536 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/BatchedCollection.swift @@ -0,0 +1,81 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +@usableFromInline +struct BatchedCollectionIndex { + let range: Range +} + +extension BatchedCollectionIndex: Comparable { + @usableFromInline + static func == (lhs: BatchedCollectionIndex, rhs: BatchedCollectionIndex) -> Bool { + lhs.range.lowerBound == rhs.range.lowerBound + } + + @usableFromInline + static func < (lhs: BatchedCollectionIndex, rhs: BatchedCollectionIndex) -> Bool { + lhs.range.lowerBound < rhs.range.lowerBound + } +} + +protocol BatchedCollectionType: Collection { + associatedtype Base: Collection +} + +@usableFromInline +struct BatchedCollection: Collection { + let base: Base + let size: Int + + @usableFromInline + init(base: Base, size: Int) { + self.base = base + self.size = size + } + + @usableFromInline + typealias Index = BatchedCollectionIndex + + private func nextBreak(after idx: Base.Index) -> Base.Index { + self.base.index(idx, offsetBy: self.size, limitedBy: self.base.endIndex) ?? self.base.endIndex + } + + @usableFromInline + var startIndex: Index { + Index(range: self.base.startIndex.. Index { + Index(range: idx.range.upperBound.. Base.SubSequence { + self.base[idx.range] + } +} + +extension Collection { + + func batched(by size: Int) -> BatchedCollection { + BatchedCollection(base: self, size: size) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Bit.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Bit.swift new file mode 100644 index 0000000..6bebdb2 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Bit.swift @@ -0,0 +1,26 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal enum Bit: Int { + case zero + case one +} + +extension Bit { + + func inverted() -> Bit { + self == .zero ? .one : .zero + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockCipher.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockCipher.swift new file mode 100644 index 0000000..740b046 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockCipher.swift @@ -0,0 +1,18 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +protocol BlockCipher: Cipher { + static var blockSize: Int { get } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockDecryptor.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockDecryptor.swift new file mode 100644 index 0000000..f8b6e65 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockDecryptor.swift @@ -0,0 +1,103 @@ +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal class BlockDecryptor: Cryptor, Updatable { + + internal enum Error: Swift.Error { + case unsupported + } + + @usableFromInline + let blockSize: Int + + @usableFromInline + let padding: Padding + + @usableFromInline + var worker: CipherModeWorker + + @usableFromInline + var accumulated = Array() + + @usableFromInline + init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws { + self.blockSize = blockSize + self.padding = padding + self.worker = worker + } + + + internal func update(withBytes bytes: ArraySlice, isLast: Bool = false) throws -> Array { + self.accumulated += bytes + + // If a worker (eg GCM) can combine ciphertext + tag + // we need to remove tag from the ciphertext. + if !isLast && self.accumulated.count < self.blockSize + self.worker.additionalBufferSize { + return [] + } + + let accumulatedWithoutSuffix: Array + if self.worker.additionalBufferSize > 0 { + // FIXME: how slow is that? + accumulatedWithoutSuffix = Array(self.accumulated.prefix(self.accumulated.count - self.worker.additionalBufferSize)) + } else { + accumulatedWithoutSuffix = self.accumulated + } + + var processedBytesCount = 0 + var plaintext = Array(reserveCapacity: accumulatedWithoutSuffix.count) + // Processing in a block-size manner. It's good for block modes, but bad for stream modes. + for var chunk in accumulatedWithoutSuffix.batched(by: self.blockSize) { + if isLast || (accumulatedWithoutSuffix.count - processedBytesCount) >= blockSize { + let isLastChunk = processedBytesCount + chunk.count == accumulatedWithoutSuffix.count + + if isLast, isLastChunk, var finalizingWorker = worker as? FinalizingDecryptModeWorker { + chunk = try finalizingWorker.willDecryptLast(bytes: chunk + accumulated.suffix(worker.additionalBufferSize)) // tag size + } + + if !chunk.isEmpty { + plaintext += worker.decrypt(block: chunk) + } + + if isLast, isLastChunk, var finalizingWorker = worker as? FinalizingDecryptModeWorker { + plaintext = Array(try finalizingWorker.didDecryptLast(bytes: plaintext.slice)) + } + + processedBytesCount += chunk.count + } + } + accumulated.removeFirst(processedBytesCount) // super-slow + + if isLast { + if accumulatedWithoutSuffix.isEmpty, var finalizingWorker = worker as? FinalizingDecryptModeWorker { + try finalizingWorker.willDecryptLast(bytes: self.accumulated.suffix(self.worker.additionalBufferSize)) + plaintext = Array(try finalizingWorker.didDecryptLast(bytes: plaintext.slice)) + } + plaintext = self.padding.remove(from: plaintext, blockSize: self.blockSize) + } + + return plaintext + } + + internal func seek(to position: Int) throws { + guard var worker = self.worker as? SeekableModeWorker else { + throw Error.unsupported + } + + try worker.seek(to: position) + self.worker = worker + + accumulated = [] + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockEncryptor.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockEncryptor.swift new file mode 100644 index 0000000..fd42d35 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockEncryptor.swift @@ -0,0 +1,67 @@ +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +@usableFromInline +final class BlockEncryptor: Cryptor, Updatable { + + internal enum Error: Swift.Error { + case unsupported + } + + private let blockSize: Int + private var worker: CipherModeWorker + private let padding: Padding + // Accumulated bytes. Not all processed bytes. + private var accumulated = Array(reserveCapacity: 16) + + private var lastBlockRemainder = 0 + + @usableFromInline + init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws { + self.blockSize = blockSize + self.padding = padding + self.worker = worker + } + + // MARK: Updatable + + internal func update(withBytes bytes: ArraySlice, isLast: Bool) throws -> Array { + self.accumulated += bytes + + if isLast { + self.accumulated = self.padding.add(to: self.accumulated, blockSize: self.blockSize) + } + + var encrypted = Array(reserveCapacity: accumulated.count) + for chunk in self.accumulated.batched(by: self.blockSize) { + if isLast || chunk.count == self.blockSize { + encrypted += self.worker.encrypt(block: chunk) + } + } + + // Stream encrypts all, so it removes all elements + self.accumulated.removeFirst(encrypted.count) + + if var finalizingWorker = worker as? FinalizingEncryptModeWorker, isLast == true { + encrypted = Array(try finalizingWorker.finalize(encrypt: encrypted.slice)) + } + + return encrypted + } + + @usableFromInline + func seek(to: Int) throws { + throw Error.unsupported + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/BlockMode.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/BlockMode.swift new file mode 100644 index 0000000..1ee5437 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/BlockMode.swift @@ -0,0 +1,26 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal typealias CipherOperationOnBlock = (_ block: ArraySlice) -> Array? + +internal protocol BlockMode { + var options: BlockModeOption { get } + //TODO: doesn't have to be public + func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker + + var customBlockSize: Int? { get } +} + +typealias StreamMode = BlockMode diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/BlockModeOptions.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/BlockModeOptions.swift new file mode 100644 index 0000000..8c39cf9 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/BlockModeOptions.swift @@ -0,0 +1,34 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal struct BlockModeOption: OptionSet { + internal let rawValue: Int + + internal init(rawValue: Int) { + self.rawValue = rawValue + } + + @usableFromInline + static let none = BlockModeOption(rawValue: 1 << 0) + + @usableFromInline + static let initializationVectorRequired = BlockModeOption(rawValue: 1 << 1) + + @usableFromInline + static let paddingRequired = BlockModeOption(rawValue: 1 << 2) + + @usableFromInline + static let useEncryptToDecrypt = BlockModeOption(rawValue: 1 << 3) +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/CBC.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/CBC.swift new file mode 100644 index 0000000..f157f32 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/CBC.swift @@ -0,0 +1,76 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// Cipher-block chaining (CBC) +// + +internal struct CBC: BlockMode { + internal enum Error: Swift.Error { + /// Invalid IV + case invalidInitializationVector + } + + internal let options: BlockModeOption = [.initializationVectorRequired, .paddingRequired] + + private let iv: Array + + internal let customBlockSize: Int? = nil + + internal init(iv: Array) { + self.iv = iv + } + + internal func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker { + if self.iv.count != blockSize { + throw Error.invalidInitializationVector + } + + return CBCModeWorker(blockSize: blockSize, iv: self.iv.slice, cipherOperation: cipherOperation) + } +} + +struct CBCModeWorker: BlockModeWorker { + let cipherOperation: CipherOperationOnBlock + var blockSize: Int + let additionalBufferSize: Int = 0 + private let iv: ArraySlice + private var prev: ArraySlice? + + + init(blockSize: Int, iv: ArraySlice, cipherOperation: @escaping CipherOperationOnBlock) { + self.blockSize = blockSize + self.iv = iv + self.cipherOperation = cipherOperation + } + + + mutating func encrypt(block plaintext: ArraySlice) -> Array { + guard let ciphertext = cipherOperation(xor(prev ?? iv, plaintext)) else { + return Array(plaintext) + } + self.prev = ciphertext.slice + return ciphertext + } + + + mutating func decrypt(block ciphertext: ArraySlice) -> Array { + guard let plaintext = cipherOperation(ciphertext) else { + return Array(ciphertext) + } + let result: Array = xor(prev ?? self.iv, plaintext) + self.prev = ciphertext + return result + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/CCM.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/CCM.swift new file mode 100644 index 0000000..56bce62 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/CCM.swift @@ -0,0 +1,367 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// CCM mode combines the well known CBC-MAC with the well known counter mode of encryption. +// https://tools.ietf.org/html/rfc3610 +// https://csrc.nist.gov/publications/detail/sp/800-38c/final + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#elseif canImport(ucrt) +import ucrt +#elseif canImport(WASILibc) +import WASILibc +#endif + +/// Counter with Cipher Block Chaining-Message Authentication Code +internal struct CCM: StreamMode { + internal enum Error: Swift.Error { + /// Invalid IV + case invalidInitializationVector + case invalidParameter + case fail + } + + internal let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt] + private let nonce: Array + private let additionalAuthenticatedData: Array? + private let tagLength: Int + private let messageLength: Int // total message length. need to know in advance + internal let customBlockSize: Int? = nil + + // `authenticationTag` nil for encryption, known tag for decryption + /// For encryption, the value is set at the end of the encryption. + /// For decryption, this is a known Tag to validate against. + internal var authenticationTag: Array? + + /// Initialize CCM + /// + /// - Parameters: + /// - iv: Initialization vector. Nonce. Valid length between 7 and 13 bytes. + /// - tagLength: Authentication tag length, in bytes. Value of {4, 6, 8, 10, 12, 14, 16}. + /// - messageLength: Plaintext message length (excluding tag if attached). Length have to be provided in advance. + /// - additionalAuthenticatedData: Additional authenticated data. + internal init(iv: Array, tagLength: Int, messageLength: Int, additionalAuthenticatedData: Array? = nil) { + self.nonce = iv + self.tagLength = tagLength + self.additionalAuthenticatedData = additionalAuthenticatedData + self.messageLength = messageLength // - tagLength + } + + /// Initialize CCM + /// + /// - Parameters: + /// - iv: Initialization vector. Nonce. Valid length between 7 and 13 bytes. + /// - tagLength: Authentication tag length, in bytes. Value of {4, 6, 8, 10, 12, 14, 16}. + /// - messageLength: Plaintext message length (excluding tag if attached). Length have to be provided in advance. + /// - authenticationTag: Authentication Tag value if not concatenated to ciphertext. + /// - additionalAuthenticatedData: Additional authenticated data. + internal init(iv: Array, tagLength: Int, messageLength: Int, authenticationTag: Array, additionalAuthenticatedData: Array? = nil) { + self.init(iv: iv, tagLength: tagLength, messageLength: messageLength, additionalAuthenticatedData: additionalAuthenticatedData) + self.authenticationTag = authenticationTag + } + + internal func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker { + if self.nonce.isEmpty { + throw Error.invalidInitializationVector + } + + return CCMModeWorker(blockSize: blockSize, nonce: self.nonce.slice, messageLength: self.messageLength, additionalAuthenticatedData: self.additionalAuthenticatedData, tagLength: self.tagLength, cipherOperation: cipherOperation) + } +} + +class CCMModeWorker: StreamModeWorker, SeekableModeWorker, CounterModeWorker, FinalizingEncryptModeWorker, FinalizingDecryptModeWorker { + typealias Counter = Int + var counter = 0 + + let cipherOperation: CipherOperationOnBlock + let blockSize: Int + private let tagLength: Int + private let messageLength: Int // total message length. need to know in advance + private let q: UInt8 + + let additionalBufferSize: Int + private var keystreamPosIdx = 0 + private let nonce: Array + private var last_y: ArraySlice = [] + private var keystream: Array = [] + // Known Tag used to validate during decryption + private var expectedTag: Array? + + internal enum Error: Swift.Error { + case invalidParameter + } + + init(blockSize: Int, nonce: ArraySlice, messageLength: Int, additionalAuthenticatedData: [UInt8]?, expectedTag: Array? = nil, tagLength: Int, cipherOperation: @escaping CipherOperationOnBlock) { + self.blockSize = 16 // CCM is defined for 128 block size + self.tagLength = tagLength + self.additionalBufferSize = tagLength + self.messageLength = messageLength + self.expectedTag = expectedTag + self.cipherOperation = cipherOperation + self.nonce = Array(nonce) + self.q = UInt8(15 - nonce.count) // n = 15-q + + let hasAssociatedData = additionalAuthenticatedData != nil && !additionalAuthenticatedData!.isEmpty + self.processControlInformation(nonce: self.nonce, tagLength: tagLength, hasAssociatedData: hasAssociatedData) + + if let aad = additionalAuthenticatedData, hasAssociatedData { + self.process(aad: aad) + } + } + + // For the very first time setup new IV (aka y0) from the block0 + private func processControlInformation(nonce: [UInt8], tagLength: Int, hasAssociatedData: Bool) { + let block0 = try! format(nonce: nonce, Q: UInt32(self.messageLength), q: self.q, t: UInt8(tagLength), hasAssociatedData: hasAssociatedData).slice + let y0 = self.cipherOperation(block0)!.slice + self.last_y = y0 + } + + private func process(aad: [UInt8]) { + let encodedAAD = format(aad: aad) + + for block_i in encodedAAD.batched(by: 16) { + let y_i = self.cipherOperation(xor(block_i, self.last_y))!.slice + self.last_y = y_i + } + } + + private func S(i: Int) throws -> [UInt8] { + let ctr = try format(counter: i, nonce: nonce, q: q) + return self.cipherOperation(ctr.slice)! + } + + + func seek(to position: Int) throws { + self.counter = position + self.keystream = try self.S(i: position) + let offset = position % self.blockSize + self.keystreamPosIdx = offset + } + + func encrypt(block plaintext: ArraySlice) -> Array { + var result = Array(reserveCapacity: plaintext.count) + + var processed = 0 + while processed < plaintext.count { + // Need a full block here to update keystream and do CBC + if self.keystream.isEmpty || self.keystreamPosIdx == self.blockSize { + // y[i], where i is the counter. Can encrypt 1 block at a time + self.counter += 1 + guard let S = try? S(i: counter) else { return Array(plaintext) } + let plaintextP = addPadding(Array(plaintext), blockSize: blockSize) + guard let y = cipherOperation(xor(last_y, plaintextP)) else { return Array(plaintext) } + self.last_y = y.slice + + self.keystream = S + self.keystreamPosIdx = 0 + } + + let xored: Array = xor(plaintext[plaintext.startIndex.advanced(by: processed)...], keystream[keystreamPosIdx...]) + keystreamPosIdx += xored.count + processed += xored.count + result += xored + } + return result + } + + + func finalize(encrypt ciphertext: ArraySlice) throws -> ArraySlice { + // concatenate T at the end + guard let S0 = try? S(i: 0) else { return ciphertext } + + let computedTag = xor(last_y.prefix(self.tagLength), S0) as ArraySlice + return ciphertext + computedTag + } + + // Decryption is stream + // CBC is block + private var accumulatedPlaintext: [UInt8] = [] + + func decrypt(block ciphertext: ArraySlice) -> Array { + var output = Array(reserveCapacity: ciphertext.count) + + do { + var currentCounter = self.counter + var processed = 0 + while processed < ciphertext.count { + // Need a full block here to update keystream and do CBC + // New keystream for a new block + if self.keystream.isEmpty || self.keystreamPosIdx == self.blockSize { + currentCounter += 1 + guard let S = try? S(i: currentCounter) else { return Array(ciphertext) } + self.keystream = S + self.keystreamPosIdx = 0 + } + + let xored: Array = xor(ciphertext[ciphertext.startIndex.advanced(by: processed)...], keystream[keystreamPosIdx...]) // plaintext + keystreamPosIdx += xored.count + processed += xored.count + output += xored + self.counter = currentCounter + } + } + + // Accumulate plaintext for the MAC calculations at the end. + // It would be good to process it together though, here. + self.accumulatedPlaintext += output + + // Shouldn't return plaintext until validate tag. + // With incremental update, can't validate tag until all block are processed. + return output + } + + + func finalize(decrypt plaintext: ArraySlice) throws -> ArraySlice { + // concatenate T at the end + let computedTag = Array(last_y.prefix(self.tagLength)) + guard let expectedTag = self.expectedTag, expectedTag == computedTag else { + throw CCM.Error.fail + } + + return plaintext + } + + @discardableResult + func willDecryptLast(bytes ciphertext: ArraySlice) throws -> ArraySlice { + // get tag of additionalBufferSize size + // `ciphertext` contains at least additionalBufferSize bytes + // overwrite expectedTag property used later for verification + guard let S0 = try? S(i: 0) else { return ciphertext } + self.expectedTag = xor(ciphertext.suffix(self.tagLength), S0) as [UInt8] + return ciphertext[ciphertext.startIndex..) throws -> ArraySlice { + + // Calculate Tag, from the last CBC block, for accumulated plaintext. + var processed = 0 + for block in self.accumulatedPlaintext.batched(by: self.blockSize) { + let blockP = addPadding(Array(block), blockSize: blockSize) + guard let y = cipherOperation(xor(last_y, blockP)) else { return plaintext } + self.last_y = y.slice + processed += block.count + } + self.accumulatedPlaintext.removeFirst(processed) + return plaintext + } +} + +// Q - octet length of P +// q - octet length of Q. Maximum length (in octets) of payload. An element of {2,3,4,5,6,7,8} +// t - octet length of T (MAC length). An element of {4,6,8,10,12,14,16} +private func format(nonce N: [UInt8], Q: UInt32, q: UInt8, t: UInt8, hasAssociatedData: Bool) throws -> [UInt8] { + var flags0: UInt8 = 0 + + if hasAssociatedData { + // 7 bit + flags0 |= (1 << 6) + } + + // 6,5,4 bit is t in 3 bits + flags0 |= (((t - 2) / 2) & 0x07) << 3 + + // 3,2,1 bit is q in 3 bits + flags0 |= ((q - 1) & 0x07) << 0 + + var block0: [UInt8] = Array(repeating: 0, count: 16) + block0[0] = flags0 + + // N in 1...(15-q) octets, n = 15-q + // n is an element of {7,8,9,10,11,12,13} + let n = 15 - Int(q) + guard (n + Int(q)) == 15 else { + // n+q == 15 + throw CCMModeWorker.Error.invalidParameter + } + block0[1...n] = N[0...(n - 1)] + + // Q in (16-q)...15 octets + block0[(16 - Int(q))...15] = Q.bytes(totalBytes: Int(q)).slice + + return block0 +} + +/// Formatting of the Counter Blocks. Ctr[i] +/// The counter generation function. +/// Q - octet length of P +/// q - octet length of Q. Maximum length (in octets) of payload. An element of {2,3,4,5,6,7,8} +private func format(counter i: Int, nonce N: [UInt8], q: UInt8) throws -> [UInt8] { + var flags0: UInt8 = 0 + + // bit 8,7 is Reserved + // bit 4,5,6 shall be set to 0 + // 3,2,1 bit is q in 3 bits + flags0 |= ((q - 1) & 0x07) << 0 + + var block = Array(repeating: 0, count: 16) // block[0] + block[0] = flags0 + + // N in 1...(15-q) octets, n = 15-q + // n is an element of {7,8,9,10,11,12,13} + let n = 15 - Int(q) + guard (n + Int(q)) == 15 else { + // n+q == 15 + throw CCMModeWorker.Error.invalidParameter + } + block[1...n] = N[0...(n - 1)] + + // [i]8q in (16-q)...15 octets + block[(16 - Int(q))...15] = i.bytes(totalBytes: Int(q)).slice + + return block +} + +/// Resulting can be partitioned into 16-octet blocks +private func format(aad: [UInt8]) -> [UInt8] { + let a = aad.count + + switch Double(a) { + case 0..<65280: // 2^16-2^8 + // [a]16 + return addPadding(a.bytes(totalBytes: 2) + aad, blockSize: 16) + case 65280..<4_294_967_296: // 2^32 + // [a]32 + return addPadding([0xFF, 0xFE] + a.bytes(totalBytes: 4) + aad, blockSize: 16) + case 4_294_967_296.., blockSize: Int) -> Array { + if bytes.isEmpty { + return Array(repeating: 0, count: blockSize) + } + + let remainder = bytes.count % blockSize + if remainder == 0 { + return bytes + } + + let paddingCount = blockSize - remainder + if paddingCount > 0 { + return bytes + Array(repeating: 0, count: paddingCount) + } + return bytes +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/CFB.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/CFB.swift new file mode 100644 index 0000000..f1d2db6 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/CFB.swift @@ -0,0 +1,102 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// Cipher feedback (CFB) +// + +internal struct CFB: BlockMode { + internal enum Error: Swift.Error { + /// Invalid IV + case invalidInitializationVector + } + + internal enum SegmentSize: Int { + case cfb8 = 1 // Encrypt byte per byte + case cfb128 = 16 // Encrypt 16 bytes per 16 bytes (default) + } + + internal let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt] + private let iv: Array + private let segmentSize: SegmentSize + internal let customBlockSize: Int? + + internal init(iv: Array, segmentSize: SegmentSize = .cfb128) { + self.iv = iv + self.segmentSize = segmentSize + self.customBlockSize = segmentSize.rawValue + } + + internal func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker { + if !(self.iv.count == blockSize || (segmentSize == .cfb8 && self.iv.count == AES.blockSize)) { + throw Error.invalidInitializationVector + } + + return CFBModeWorker(blockSize: blockSize, iv: self.iv.slice, segmentSize: segmentSize, cipherOperation: cipherOperation) + } +} + +struct CFBModeWorker: BlockModeWorker { + let cipherOperation: CipherOperationOnBlock + let blockSize: Int + let additionalBufferSize: Int = 0 + private let iv: ArraySlice + private let segmentSize: CFB.SegmentSize + private var prev: ArraySlice? + + init(blockSize: Int, iv: ArraySlice, segmentSize: CFB.SegmentSize, cipherOperation: @escaping CipherOperationOnBlock) { + self.blockSize = blockSize + self.iv = iv + self.segmentSize = segmentSize + self.cipherOperation = cipherOperation + } + + + mutating func encrypt(block plaintext: ArraySlice) -> Array { + switch segmentSize { + case .cfb128: + guard let ciphertext = cipherOperation(prev ?? iv) else { + return Array(plaintext) + } + self.prev = xor(plaintext, ciphertext.slice) + return Array(self.prev ?? []) + case .cfb8: + guard let ciphertext = cipherOperation(prev ?? iv) else { + return Array(plaintext) + } + let result = [Array(plaintext)[0] ^ Array(ciphertext)[0]] + self.prev = Array((prev ?? iv).dropFirst()) + [result[0]] + return result + } + } + + + mutating func decrypt(block ciphertext: ArraySlice) -> Array { + switch segmentSize { + case .cfb128: + guard let plaintext = cipherOperation(prev ?? iv) else { + return Array(ciphertext) + } + let result: Array = xor(plaintext, ciphertext) + prev = ciphertext + return result + case .cfb8: + guard let plaintext = cipherOperation(prev ?? iv) else { + return Array(ciphertext) + } + self.prev = Array((prev ?? iv).dropFirst()) + [Array(ciphertext)[0]] + return [Array(ciphertext)[0] ^ Array(plaintext)[0]] + } + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/CTR.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/CTR.swift new file mode 100644 index 0000000..8313c2c --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/CTR.swift @@ -0,0 +1,138 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// Counter (CTR) + +internal struct CTR: StreamMode { + internal enum Error: Swift.Error { + /// Invalid IV + case invalidInitializationVector + } + + internal let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt] + private let iv: Array + private let counter: Int + internal let customBlockSize: Int? = nil + + internal init(iv: Array, counter: Int = 0) { + self.iv = iv + self.counter = counter + } + + internal func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker { + if self.iv.count != blockSize { + throw Error.invalidInitializationVector + } + + return CTRModeWorker(blockSize: blockSize, iv: self.iv.slice, counter: self.counter, cipherOperation: cipherOperation) + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct CTRModeWorker: StreamModeWorker, SeekableModeWorker, CounterModeWorker { + typealias Counter = CTRCounter + + final class CTRCounter { + private let constPrefix: Array + private var value: UInt64 + //TODO: make it an updatable value, computing is too slow + var bytes: Array { + self.constPrefix + self.value.bytes() + } + + + init(_ initialValue: Array) { + let halfIndex = initialValue.startIndex.advanced(by: initialValue.count / 2) + self.constPrefix = Array(initialValue[initialValue.startIndex.., startAt index: Int) { + self.init(buildCounterValue(nonce, counter: UInt64(index))) + } + + static func += (lhs: CTRCounter, rhs: Int) { + lhs.value += UInt64(rhs) + } + } + + let cipherOperation: CipherOperationOnBlock + let additionalBufferSize: Int = 0 + let iv: Array + var counter: CTRCounter + + private let blockSize: Int + + // The same keystream is used for the block length plaintext + // As new data is added, keystream suffix is used to xor operation. + private var keystream: Array + private var keystreamPosIdx = 0 + + init(blockSize: Int, iv: ArraySlice, counter: Int, cipherOperation: @escaping CipherOperationOnBlock) { + self.cipherOperation = cipherOperation + self.blockSize = blockSize + self.iv = Array(iv) + + // the first keystream is calculated from the nonce = initial value of counter + self.counter = CTRCounter(nonce: Array(iv), startAt: counter) + self.keystream = Array(cipherOperation(self.counter.bytes.slice)!) + } + + + mutating func seek(to position: Int) throws { + let offset = position % self.blockSize + self.counter = CTRCounter(nonce: self.iv, startAt: position / self.blockSize) + self.keystream = Array(self.cipherOperation(self.counter.bytes.slice)!) + self.keystreamPosIdx = offset + } + + // plaintext is at most blockSize long + + mutating func encrypt(block plaintext: ArraySlice) -> Array { + var result = Array(reserveCapacity: plaintext.count) + + var processed = 0 + while processed < plaintext.count { + // Update keystream + if self.keystreamPosIdx == self.blockSize { + self.counter += 1 + self.keystream = Array(self.cipherOperation(self.counter.bytes.slice)!) + self.keystreamPosIdx = 0 + } + + let xored: Array = xor(plaintext[plaintext.startIndex.advanced(by: processed)...], keystream[keystreamPosIdx...]) + keystreamPosIdx += xored.count + processed += xored.count + result += xored + } + + return result + } + + mutating func decrypt(block ciphertext: ArraySlice) -> Array { + self.encrypt(block: ciphertext) + } +} + +private func buildCounterValue(_ iv: Array, counter: UInt64) -> Array { + let noncePartLen = iv.count / 2 + let noncePrefix = iv[iv.startIndex.. +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal protocol CipherModeWorker { + var cipherOperation: CipherOperationOnBlock { get } + + // Additional space needed when incrementally process data + // eg. for GCM combined mode + var additionalBufferSize: Int { get } + + + mutating func encrypt(block plaintext: ArraySlice) -> Array + + + mutating func decrypt(block ciphertext: ArraySlice) -> Array +} + +/// Block workers use `BlockEncryptor` +internal protocol BlockModeWorker: CipherModeWorker { + var blockSize: Int { get } +} + +internal protocol CounterModeWorker: CipherModeWorker { + associatedtype Counter + var counter: Counter { get set } +} + +internal protocol SeekableModeWorker: CipherModeWorker { + mutating func seek(to position: Int) throws +} + +/// Stream workers use `StreamEncryptor` +internal protocol StreamModeWorker: CipherModeWorker { +} + +internal protocol FinalizingEncryptModeWorker: CipherModeWorker { + // Any final calculations, eg. calculate tag + // Called after the last block is encrypted + mutating func finalize(encrypt ciphertext: ArraySlice) throws -> ArraySlice +} + +internal protocol FinalizingDecryptModeWorker: CipherModeWorker { + // Called before decryption, hence input is ciphertext. + // ciphertext is either a last block, or a tag (for stream workers) + @discardableResult + mutating func willDecryptLast(bytes ciphertext: ArraySlice) throws -> ArraySlice + // Called after decryption, hence input is ciphertext + mutating func didDecryptLast(bytes plaintext: ArraySlice) throws -> ArraySlice + // Any final calculations, eg. calculate tag + // Called after the last block is encrypted + mutating func finalize(decrypt plaintext: ArraySlice) throws -> ArraySlice +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/ECB.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/ECB.swift new file mode 100644 index 0000000..f18be0d --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/ECB.swift @@ -0,0 +1,53 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// Electronic codebook (ECB) +// + +internal struct ECB: BlockMode { + internal let options: BlockModeOption = .paddingRequired + internal let customBlockSize: Int? = nil + + internal init() { + } + + internal func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker { + ECBModeWorker(blockSize: blockSize, cipherOperation: cipherOperation) + } +} + +struct ECBModeWorker: BlockModeWorker { + typealias Element = Array + let cipherOperation: CipherOperationOnBlock + let blockSize: Int + let additionalBufferSize: Int = 0 + + init(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock) { + self.blockSize = blockSize + self.cipherOperation = cipherOperation + } + + + mutating func encrypt(block plaintext: ArraySlice) -> Array { + guard let ciphertext = cipherOperation(plaintext) else { + return Array(plaintext) + } + return ciphertext + } + + mutating func decrypt(block ciphertext: ArraySlice) -> Array { + self.encrypt(block: ciphertext) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/GCM.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/GCM.swift new file mode 100644 index 0000000..af2bbf2 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/GCM.swift @@ -0,0 +1,374 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// Galois/Counter Mode (GCM) +// https://csrc.nist.gov/publications/detail/sp/800-38d/final +// ref: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.694.695&rep=rep1&type=pdf +// + +internal final class GCM: BlockMode { + internal enum Mode { + /// In combined mode, the authentication tag is directly appended to the encrypted message. This is usually what you want. + case combined + /// Some applications may need to store the authentication tag and the encrypted message at different locations. + case detached + } + + internal let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt] + + internal enum Error: Swift.Error { + /// Invalid IV + case invalidInitializationVector + /// Special symbol FAIL that indicates that the inputs are not authentic. + case fail + } + + private let iv: Array + private let additionalAuthenticatedData: Array? + private let mode: Mode + internal let customBlockSize: Int? = nil + + /// Length of authentication tag, in bytes. + /// For encryption, the value is given as init parameter. + /// For decryption, the length of given authentication tag is used. + private let tagLength: Int + + // `authenticationTag` nil for encryption, known tag for decryption + /// For encryption, the value is set at the end of the encryption. + /// For decryption, this is a known Tag to validate against. + internal var authenticationTag: Array? + + // encrypt + /// Possible tag lengths: 4,8,12,13,14,15,16 + internal init(iv: Array, additionalAuthenticatedData: Array? = nil, tagLength: Int = 16, mode: Mode = .detached) { + self.iv = iv + self.additionalAuthenticatedData = additionalAuthenticatedData + self.mode = mode + self.tagLength = tagLength + } + + // decrypt + internal convenience init(iv: Array, authenticationTag: Array, additionalAuthenticatedData: Array? = nil, mode: Mode = .detached) { + self.init(iv: iv, additionalAuthenticatedData: additionalAuthenticatedData, tagLength: authenticationTag.count, mode: mode) + self.authenticationTag = authenticationTag + } + + internal func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker { + if self.iv.isEmpty { + throw Error.invalidInitializationVector + } + + let worker = GCMModeWorker(iv: iv.slice, aad: self.additionalAuthenticatedData?.slice, expectedTag: self.authenticationTag, tagLength: self.tagLength, mode: self.mode, cipherOperation: cipherOperation) + worker.didCalculateTag = { [weak self] tag in + self?.authenticationTag = tag + } + return worker + } +} + +// MARK: - Worker + +final class GCMModeWorker: BlockModeWorker, FinalizingEncryptModeWorker, FinalizingDecryptModeWorker { + let cipherOperation: CipherOperationOnBlock + + // Callback called when authenticationTag is ready + var didCalculateTag: ((Array) -> Void)? + + private let tagLength: Int + // GCM nonce is 96-bits by default. It's the most effective length for the IV + private static let nonceSize = 12 + + // GCM is designed for 128-bit ciphers like AES (but not really for Blowfish). 64-bit mode is not implemented. + let blockSize = 16 // 128 bit + let additionalBufferSize: Int + private let iv: ArraySlice + private let mode: GCM.Mode + private var counter: UInt128 + private let eky0: UInt128 // move to GF? + private let h: UInt128 + + // Additional authenticated data + private let aad: ArraySlice? + // Known Tag used to validate during decryption + private var expectedTag: Array? + + // Note: need new worker to reset instance + // Use empty aad if not specified. AAD is optional. + private lazy var gf: GF = { + if let aad = aad { + return GF(aad: Array(aad), h: h, blockSize: blockSize) + } + return GF(aad: [UInt8](), h: h, blockSize: blockSize) + }() + + init(iv: ArraySlice, aad: ArraySlice? = nil, expectedTag: Array? = nil, tagLength: Int, mode: GCM.Mode, cipherOperation: @escaping CipherOperationOnBlock) { + self.cipherOperation = cipherOperation + self.iv = iv + self.mode = mode + self.aad = aad + self.expectedTag = expectedTag + self.tagLength = tagLength + self.h = UInt128(cipherOperation(Array(repeating: 0, count: self.blockSize).slice)!) // empty block + + if mode == .combined { + self.additionalBufferSize = tagLength + } else { + self.additionalBufferSize = 0 + } + + // Assume nonce is 12 bytes long, otherwise initial counter would be calulated from GHASH + // counter = GF.ghash(aad: [UInt8](), ciphertext: nonce) + if iv.count == GCMModeWorker.nonceSize { + self.counter = makeCounter(nonce: Array(self.iv)) + } else { + self.counter = GF.ghash(h: self.h, aad: [UInt8](), ciphertext: Array(iv), blockSize: self.blockSize) + } + + // Set constants + self.eky0 = UInt128(cipherOperation(self.counter.bytes.slice)!) + } + + func encrypt(block plaintext: ArraySlice) -> Array { + self.counter = incrementCounter(self.counter) + + guard let ekyN = cipherOperation(counter.bytes.slice) else { + return Array(plaintext) + } + + // plaintext block ^ ek1 + let ciphertext = xor(plaintext, ekyN) as Array + + // update ghash incrementally + gf.ghashUpdate(block: ciphertext) + + return Array(ciphertext) + } + + + func finalize(encrypt ciphertext: ArraySlice) throws -> ArraySlice { + // Calculate MAC tag. + let ghash = self.gf.ghashFinish() + let tag = Array((ghash ^ self.eky0).bytes.prefix(self.tagLength)) + + // Notify handler + self.didCalculateTag?(tag) + + switch self.mode { + case .combined: + return (ciphertext + tag).slice + case .detached: + return ciphertext + } + } + + + func decrypt(block ciphertext: ArraySlice) -> Array { + self.counter = incrementCounter(self.counter) + + // update ghash incrementally + self.gf.ghashUpdate(block: Array(ciphertext)) + + guard let ekN = cipherOperation(counter.bytes.slice) else { + return Array(ciphertext) + } + + // ciphertext block ^ ek1 + let plaintext = xor(ciphertext, ekN) as Array + return plaintext + } + + // The authenticated decryption operation has five inputs: K, IV , C, A, and T. It has only a single + // output, either the plaintext value P or a special symbol FAIL that indicates that the inputs are not + // authentic. + @discardableResult + func willDecryptLast(bytes ciphertext: ArraySlice) throws -> ArraySlice { + // Validate tag + switch self.mode { + case .combined: + // overwrite expectedTag property used later for verification + self.expectedTag = Array(ciphertext.suffix(self.tagLength)) + return ciphertext[ciphertext.startIndex..) throws -> ArraySlice { + // Calculate MAC tag. + let ghash = self.gf.ghashFinish() + let computedTag = Array((ghash ^ self.eky0).bytes.prefix(self.tagLength)) + + // Validate tag + guard let expectedTag = self.expectedTag, computedTag == expectedTag else { + throw GCM.Error.fail + } + + return plaintext + } + + func finalize(decrypt plaintext: ArraySlice) throws -> ArraySlice { + // do nothing + plaintext + } +} + +// MARK: - Local utils + +private func makeCounter(nonce: Array) -> UInt128 { + UInt128(nonce + [0, 0, 0, 1]) +} + +// Successive counter values are generated using the function incr(), which treats the rightmost 32 +// bits of its argument as a nonnegative integer with the least significant bit on the right +private func incrementCounter(_ counter: UInt128) -> UInt128 { + let b = counter.i.b + 1 + let a = (b == 0 ? counter.i.a + 1 : counter.i.a) + return UInt128((a, b)) +} + +// If data is not a multiple of block size bytes long then the remainder is zero padded +// Note: It's similar to ZeroPadding, but it's not the same. +private func addPadding(_ bytes: Array, blockSize: Int) -> Array { + if bytes.isEmpty { + return Array(repeating: 0, count: blockSize) + } + + let remainder = bytes.count % blockSize + if remainder == 0 { + return bytes + } + + let paddingCount = blockSize - remainder + if paddingCount > 0 { + return bytes + Array(repeating: 0, count: paddingCount) + } + return bytes +} + +// MARK: - GF + +/// The Field GF(2^128) +private final class GF { + static let r = UInt128(a: 0xE100000000000000, b: 0) + + let blockSize: Int + let h: UInt128 + + // AAD won't change + let aadLength: Int + + // Updated for every consumed block + var ciphertextLength: Int + + // Start with 0 + var x: UInt128 + + init(aad: [UInt8], h: UInt128, blockSize: Int) { + self.blockSize = blockSize + self.aadLength = aad.count + self.ciphertextLength = 0 + self.h = h + self.x = 0 + + // Calculate for AAD at the begining + self.x = GF.calculateX(aad: aad, x: self.x, h: h, blockSize: blockSize) + } + + @discardableResult + func ghashUpdate(block ciphertextBlock: Array) -> UInt128 { + self.ciphertextLength += ciphertextBlock.count + self.x = GF.calculateX(block: addPadding(ciphertextBlock, blockSize: self.blockSize), x: self.x, h: self.h, blockSize: self.blockSize) + return self.x + } + + func ghashFinish() -> UInt128 { + // len(A) || len(C) + let len = UInt128(a: UInt64(aadLength * 8), b: UInt64(ciphertextLength * 8)) + x = GF.multiply(self.x ^ len, self.h) + return self.x + } + + // GHASH. One-time calculation + static func ghash(x startx: UInt128 = 0, h: UInt128, aad: Array, ciphertext: Array, blockSize: Int) -> UInt128 { + var x = self.calculateX(aad: aad, x: startx, h: h, blockSize: blockSize) + x = self.calculateX(ciphertext: ciphertext, x: x, h: h, blockSize: blockSize) + + // len(aad) || len(ciphertext) + let len = UInt128(a: UInt64(aad.count * 8), b: UInt64(ciphertext.count * 8)) + x = self.multiply(x ^ len, h) + + return x + } + + // Calculate Ciphertext part, for all blocks + // Not used with incremental calculation. + private static func calculateX(ciphertext: [UInt8], x startx: UInt128, h: UInt128, blockSize: Int) -> UInt128 { + let pciphertext = addPadding(ciphertext, blockSize: blockSize) + let blocksCount = pciphertext.count / blockSize + + var x = startx + for i in 0.., x: UInt128, h: UInt128, blockSize: Int) -> UInt128 { + let k = x ^ UInt128(ciphertextBlock) + return self.multiply(k, h) + } + + // Calculate AAD part, for all blocks + private static func calculateX(aad: [UInt8], x startx: UInt128, h: UInt128, blockSize: Int) -> UInt128 { + let paad = addPadding(aad, blockSize: blockSize) + let blocksCount = paad.count / blockSize + + var x = startx + for i in 0.. UInt128 { + var z: UInt128 = 0 + var v = x + var k = UInt128(a: 1 << 63, b: 0) + + for _ in 0..<128 { + if y & k == k { + z = z ^ v + } + + if v & 1 != 1 { + v = v >> 1 + } else { + v = (v >> 1) ^ self.r + } + + k = k >> 1 + } + + return z + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/OCB.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/OCB.swift new file mode 100644 index 0000000..42abe0b --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/OCB.swift @@ -0,0 +1,398 @@ +// +// CryptoSwift +// +// Copyright (C) Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// The OCB Authenticated-Encryption Algorithm +// https://tools.ietf.org/html/rfc7253 +// + +internal final class OCB: BlockMode { + + internal enum Mode { + /// In combined mode, the authentication tag is directly appended to the encrypted message. This is usually what you want. + case combined + /// Some applications may need to store the authentication tag and the encrypted message at different locations. + case detached + } + + internal let options: BlockModeOption = [.initializationVectorRequired] + + internal enum Error: Swift.Error { + case invalidNonce + case fail + } + + private let N: Array + private let additionalAuthenticatedData: Array? + private let mode: Mode + internal let customBlockSize: Int? = nil + + /// Length of authentication tag, in bytes. + /// For encryption, the value is given as init parameter. + /// For decryption, the length of given authentication tag is used. + private let tagLength: Int + + // `authenticationTag` nil for encryption, known tag for decryption + /// For encryption, the value is set at the end of the encryption. + /// For decryption, this is a known Tag to validate against. + internal var authenticationTag: Array? + + // encrypt + internal init(nonce N: Array, additionalAuthenticatedData: Array? = nil, tagLength: Int = 16, mode: Mode = .detached) { + self.N = N + self.additionalAuthenticatedData = additionalAuthenticatedData + self.mode = mode + self.tagLength = tagLength + } + + // decrypt + + internal convenience init(nonce N: Array, authenticationTag: Array, additionalAuthenticatedData: Array? = nil, mode: Mode = .detached) { + self.init(nonce: N, additionalAuthenticatedData: additionalAuthenticatedData, tagLength: authenticationTag.count, mode: mode) + self.authenticationTag = authenticationTag + } + + internal func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker { + if self.N.isEmpty || self.N.count > 15 { + throw Error.invalidNonce + } + + let worker = OCBModeWorker(N: N.slice, aad: self.additionalAuthenticatedData?.slice, expectedTag: self.authenticationTag, tagLength: self.tagLength, mode: self.mode, cipherOperation: cipherOperation, encryptionOperation: encryptionOperation) + worker.didCalculateTag = { [weak self] tag in + self?.authenticationTag = tag + } + return worker + } +} + +// MARK: - Worker + +final class OCBModeWorker: BlockModeWorker, FinalizingEncryptModeWorker, FinalizingDecryptModeWorker { + + let cipherOperation: CipherOperationOnBlock + var hashOperation: CipherOperationOnBlock! + + // Callback called when authenticationTag is ready + var didCalculateTag: ((Array) -> Void)? + + private let tagLength: Int + + let blockSize = 16 // 128 bit + var additionalBufferSize: Int + private let mode: OCB.Mode + + // Additional authenticated data + private let aad: ArraySlice? + // Known Tag used to validate during decryption + private var expectedTag: Array? + + /* + * KEY-DEPENDENT + */ + // NOTE: elements are lazily calculated + private var l = [Array]() + private var lAsterisk: Array + private var lDollar: Array + + /* + * PER-ENCRYPTION/DECRYPTION + */ + private var mainBlockCount: UInt64 + private var offsetMain: Array + private var checksum: Array + + init(N: ArraySlice, aad: ArraySlice? = nil, expectedTag: Array? = nil, tagLength: Int, mode: OCB.Mode, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) { + + self.cipherOperation = cipherOperation + self.hashOperation = encryptionOperation + self.mode = mode + self.aad = aad + self.expectedTag = expectedTag + self.tagLength = tagLength + + if mode == .combined { + self.additionalBufferSize = tagLength + } else { + self.additionalBufferSize = 0 + } + + /* + * KEY-DEPENDENT INITIALIZATION + */ + + let zeros = Array(repeating: 0, count: self.blockSize) + self.lAsterisk = self.hashOperation(zeros.slice)! /// L_* = ENCIPHER(K, zeros(128)) + self.lDollar = double(self.lAsterisk) /// L_$ = double(L_*) + self.l.append(double(self.lDollar)) /// L_0 = double(L_$) + + /* + * NONCE-DEPENDENT AND PER-ENCRYPTION/DECRYPTION INITIALIZATION + */ + + /// Nonce = num2str(TAGLEN mod 128,7) || zeros(120-bitlen(N)) || 1 || N + var nonce = Array(repeating: 0, count: blockSize) + nonce[(nonce.count - N.count)...] = N + nonce[0] = UInt8(tagLength) << 4 + nonce[blockSize - 1 - N.count] |= 1 + + /// bottom = str2num(Nonce[123..128]) + let bottom = nonce[15] & 0x3F + + /// Ktop = ENCIPHER(K, Nonce[1..122] || zeros(6)) + nonce[15] &= 0xC0 + let Ktop = self.hashOperation(nonce.slice)! + + /// Stretch = Ktop || (Ktop[1..64] xor Ktop[9..72]) + let Stretch = Ktop + xor(Ktop[0..<8], Ktop[1..<9]) + + /// Offset_0 = Stretch[1+bottom..128+bottom] + var offsetMAIN_0 = Array(repeating: 0, count: blockSize) + let bits = bottom % 8 + let bytes = Int(bottom / 8) + if bits == 0 { + offsetMAIN_0[0..> (8 - bits))) + } + } + + self.mainBlockCount = 0 + self.offsetMain = Array(offsetMAIN_0.slice) + self.checksum = Array(repeating: 0, count: self.blockSize) /// Checksum_0 = zeros(128) + } + + /// L_i = double(L_{i-1}) for every integer i > 0 + func getLSub(_ n: Int) -> Array { + while n >= self.l.count { + self.l.append(double(self.l.last!)) + } + return self.l[n] + } + + func computeTag() -> Array { + + let sum = self.hashAAD() + + /// Tag = ENCIPHER(K, Checksum_* xor Offset_* xor L_$) xor HASH(K,A) + return xor(self.hashOperation(xor(xor(self.checksum, self.offsetMain).slice, self.lDollar))!, sum) + } + + func hashAAD() -> Array { + var sum = Array(repeating: 0, count: blockSize) + + guard let aad = self.aad else { + return sum + } + + var offset = Array(repeating: 0, count: blockSize) + var blockCount: UInt64 = 1 + for aadBlock in aad.batched(by: self.blockSize) { + + if aadBlock.count == self.blockSize { + + /// Offset_i = Offset_{i-1} xor L_{ntz(i)} + offset = xor(offset, self.getLSub(ntz(blockCount))) + + /// Sum_i = Sum_{i-1} xor ENCIPHER(K, A_i xor Offset_i) + sum = xor(sum, self.hashOperation(xor(aadBlock, offset))!) + } else { + if !aadBlock.isEmpty { + + /// Offset_* = Offset_m xor L_* + offset = xor(offset, self.lAsterisk) + + /// CipherInput = (A_* || 1 || zeros(127-bitlen(A_*))) xor Offset_* + let cipherInput: Array = xor(extend(aadBlock, size: blockSize), offset) + + /// Sum = Sum_m xor ENCIPHER(K, CipherInput) + sum = xor(sum, self.hashOperation(cipherInput.slice)!) + } + } + blockCount += 1 + } + + return sum + } + + func encrypt(block plaintext: ArraySlice) -> Array { + + if plaintext.count == self.blockSize { + return self.processBlock(block: plaintext, forEncryption: true) + } else { + return self.processFinalBlock(block: plaintext, forEncryption: true) + } + } + + func finalize(encrypt ciphertext: ArraySlice) throws -> ArraySlice { + + let tag = self.computeTag() + + self.didCalculateTag?(tag) + + switch self.mode { + case .combined: + return ciphertext + tag + case .detached: + return ciphertext + } + } + + func decrypt(block ciphertext: ArraySlice) -> Array { + + if ciphertext.count == self.blockSize { + return self.processBlock(block: ciphertext, forEncryption: false) + } else { + return self.processFinalBlock(block: ciphertext, forEncryption: false) + } + } + + func finalize(decrypt plaintext: ArraySlice) throws -> ArraySlice { + // do nothing + plaintext + } + + private func processBlock(block: ArraySlice, forEncryption: Bool) -> Array { + + /* + * OCB-ENCRYPT/OCB-DECRYPT: Process any whole blocks + */ + + self.mainBlockCount += 1 + + /// Offset_i = Offset_{i-1} xor L_{ntz(i)} + self.offsetMain = xor(self.offsetMain, self.getLSub(ntz(self.mainBlockCount))) + + /// C_i = Offset_i xor ENCIPHER(K, P_i xor Offset_i) + /// P_i = Offset_i xor DECIPHER(K, C_i xor Offset_i) + var mainBlock = Array(block) + mainBlock = xor(mainBlock, offsetMain) + mainBlock = self.cipherOperation(mainBlock.slice)! + mainBlock = xor(mainBlock, self.offsetMain) + + /// Checksum_i = Checksum_{i-1} xor P_i + if forEncryption { + self.checksum = xor(self.checksum, block) + } else { + self.checksum = xor(self.checksum, mainBlock) + } + + return mainBlock + } + + private func processFinalBlock(block: ArraySlice, forEncryption: Bool) -> Array { + + let out: Array + + if block.isEmpty { + /// C_* = + /// P_* = + out = [] + + } else { + + /// Offset_* = Offset_m xor L_* + self.offsetMain = xor(self.offsetMain, self.lAsterisk) + + /// Pad = ENCIPHER(K, Offset_*) + let Pad = self.hashOperation(self.offsetMain.slice)! + + /// C_* = P_* xor Pad[1..bitlen(P_*)] + /// P_* = C_* xor Pad[1..bitlen(C_*)] + out = xor(block, Pad[0..) throws -> ArraySlice { + // Validate tag + switch self.mode { + case .combined: + // overwrite expectedTag property used later for verification + self.expectedTag = Array(ciphertext.suffix(self.tagLength)) + return ciphertext[ciphertext.startIndex..) throws -> ArraySlice { + // Calculate MAC tag. + let computedTag = self.computeTag() + + // Validate tag + guard let expectedTag = self.expectedTag, computedTag == expectedTag else { + throw OCB.Error.fail + } + + return plaintext + } +} + +// MARK: - Local utils + +private func ntz(_ x: UInt64) -> Int { + if x == 0 { + return 64 + } + + var xv = x + var n = 0 + while (xv & 1) == 0 { + n += 1 + xv = xv >> 1 + } + return n +} + +private func double(_ block: Array) -> Array { + var ( carry, result) = shiftLeft(block) + + /* + * NOTE: This construction is an attempt at a constant-time implementation. + */ + result[15] ^= (0x87 >> ((1 - carry) << 3)) + + return result +} + +private func shiftLeft(_ block: Array) -> (UInt8, Array) { + var output = Array(repeating: 0, count: block.count) + + var bit: UInt8 = 0 + + for i in 0..> 7) & 1 + } + return (bit, output) +} + +private func extend(_ block: ArraySlice, size: Int) -> Array { + var output = Array(repeating: 0, count: size) + output[0.. +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// Output Feedback (OFB) +// + +internal struct OFB: BlockMode { + internal enum Error: Swift.Error { + /// Invalid IV + case invalidInitializationVector + } + + internal let options: BlockModeOption = [.initializationVectorRequired, .useEncryptToDecrypt] + private let iv: Array + internal let customBlockSize: Int? = nil + + internal init(iv: Array) { + self.iv = iv + } + + internal func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker { + if self.iv.count != blockSize { + throw Error.invalidInitializationVector + } + + return OFBModeWorker(blockSize: blockSize, iv: self.iv.slice, cipherOperation: cipherOperation) + } +} + +struct OFBModeWorker: BlockModeWorker { + let cipherOperation: CipherOperationOnBlock + let blockSize: Int + let additionalBufferSize: Int = 0 + private let iv: ArraySlice + private var prev: ArraySlice? + + init(blockSize: Int, iv: ArraySlice, cipherOperation: @escaping CipherOperationOnBlock) { + self.blockSize = blockSize + self.iv = iv + self.cipherOperation = cipherOperation + } + + + mutating func encrypt(block plaintext: ArraySlice) -> Array { + guard let ciphertext = cipherOperation(prev ?? iv) else { + return Array(plaintext) + } + self.prev = ciphertext.slice + return xor(plaintext, ciphertext) + } + + + mutating func decrypt(block ciphertext: ArraySlice) -> Array { + guard let decrypted = cipherOperation(prev ?? iv) else { + return Array(ciphertext) + } + let plaintext: Array = xor(decrypted, ciphertext) + prev = decrypted.slice + return plaintext + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/PCBC.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/PCBC.swift new file mode 100644 index 0000000..8cd9558 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/BlockMode/PCBC.swift @@ -0,0 +1,74 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// Propagating Cipher Block Chaining (PCBC) +// + +internal struct PCBC: BlockMode { + internal enum Error: Swift.Error { + /// Invalid IV + case invalidInitializationVector + } + + internal let options: BlockModeOption = [.initializationVectorRequired, .paddingRequired] + private let iv: Array + internal let customBlockSize: Int? = nil + + internal init(iv: Array) { + self.iv = iv + } + + internal func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker { + if self.iv.count != blockSize { + throw Error.invalidInitializationVector + } + + return PCBCModeWorker(blockSize: blockSize, iv: self.iv.slice, cipherOperation: cipherOperation) + } +} + +struct PCBCModeWorker: BlockModeWorker { + let cipherOperation: CipherOperationOnBlock + var blockSize: Int + let additionalBufferSize: Int = 0 + private let iv: ArraySlice + private var prev: ArraySlice? + + + init(blockSize: Int, iv: ArraySlice, cipherOperation: @escaping CipherOperationOnBlock) { + self.blockSize = blockSize + self.iv = iv + self.cipherOperation = cipherOperation + } + + + mutating func encrypt(block plaintext: ArraySlice) -> Array { + guard let ciphertext = cipherOperation(xor(prev ?? iv, plaintext)) else { + return Array(plaintext) + } + self.prev = xor(plaintext, ciphertext.slice) + return ciphertext + } + + + mutating func decrypt(block ciphertext: ArraySlice) -> Array { + guard let plaintext = cipherOperation(ciphertext) else { + return Array(ciphertext) + } + let result: Array = xor(prev ?? self.iv, plaintext) + self.prev = xor(result, ciphertext) + return result + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Blowfish.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Blowfish.swift new file mode 100644 index 0000000..2f2adfe --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Blowfish.swift @@ -0,0 +1,545 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// https://en.wikipedia.org/wiki/Blowfish_(cipher) +// Based on Paul Kocher implementation +// + +internal final class Blowfish { + internal enum Error: Swift.Error { + /// Data padding is required + case dataPaddingRequired + /// Invalid key or IV + case invalidKeyOrInitializationVector + /// Invalid IV + case invalidInitializationVector + /// Invalid block mode + case invalidBlockMode + } + + internal static let blockSize: Int = 8 // 64 bit + internal let keySize: Int + + private let blockMode: BlockMode + private let padding: Padding + private var decryptWorker: CipherModeWorker! + private var encryptWorker: CipherModeWorker! + + private let N = 16 // rounds + private var P: Array + private var S: Array> + private let origP: Array = [ + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, + 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, + 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, + 0xb5470917, 0x9216d5d9, 0x8979fb1b + ] + + private let origS: Array> = [ + [ + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a + ], + [ + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 + ], + [ + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 + ], + [ + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 + ] + ] + + internal init(key: Array, blockMode: BlockMode = CBC(iv: Array(repeating: 0, count: Blowfish.blockSize)), padding: Padding) throws { + precondition(key.count >= 5 && key.count <= 56) + + self.blockMode = blockMode + self.padding = padding + self.keySize = key.count + + self.S = self.origS + self.P = self.origP + + self.expandKey(key: key) + try self.setupBlockModeWorkers() + } + + private func setupBlockModeWorkers() throws { + let decryptBlock = { [weak self] (block: ArraySlice) -> Array? in + self?.decrypt(block: block) + } + + let encryptBlock = { [weak self] (block: ArraySlice) -> Array? in + self?.encrypt(block: block) + } + + self.encryptWorker = try self.blockMode.worker(blockSize: Blowfish.blockSize, cipherOperation: encryptBlock, encryptionOperation: encryptBlock) + + if self.blockMode.options.contains(.useEncryptToDecrypt) { + self.decryptWorker = try self.blockMode.worker(blockSize: Blowfish.blockSize, cipherOperation: encryptBlock, encryptionOperation: encryptBlock) + } else { + self.decryptWorker = try self.blockMode.worker(blockSize: Blowfish.blockSize, cipherOperation: decryptBlock, encryptionOperation: encryptBlock) + } + } + + private func reset() { + self.S = self.origS + self.P = self.origP + // todo expand key + } + + private func expandKey(key: Array) { + var j = 0 + for i in 0..<(self.N + 2) { + var data: UInt32 = 0x0 + for _ in 0..<4 { + data = (data << 8) | UInt32(key[j]) + j += 1 + if j >= key.count { + j = 0 + } + } + self.P[i] ^= data + } + + var datal: UInt32 = 0 + var datar: UInt32 = 0 + + for i in stride(from: 0, to: self.N + 2, by: 2) { + self.encryptBlowfishBlock(l: &datal, r: &datar) + self.P[i] = datal + self.P[i + 1] = datar + } + + for i in 0..<4 { + for j in stride(from: 0, to: 256, by: 2) { + self.encryptBlowfishBlock(l: &datal, r: &datar) + self.S[i][j] = datal + self.S[i][j + 1] = datar + } + } + } + + private func encrypt(block: ArraySlice) -> Array? { + var result = Array() + + var l = UInt32(bytes: block[block.startIndex..> 24) & 0xff), + UInt8((l >> 16) & 0xff) + ] + result += [ + UInt8((l >> 8) & 0xff), + UInt8((l >> 0) & 0xff) + ] + result += [ + UInt8((r >> 24) & 0xff), + UInt8((r >> 16) & 0xff) + ] + result += [ + UInt8((r >> 8) & 0xff), + UInt8((r >> 0) & 0xff) + ] + + return result + } + + private func decrypt(block: ArraySlice) -> Array? { + var result = Array() + + var l = UInt32(bytes: block[block.startIndex..> 24) & 0xff), + UInt8((l >> 16) & 0xff) + ] + result += [ + UInt8((l >> 8) & 0xff), + UInt8((l >> 0) & 0xff) + ] + result += [ + UInt8((r >> 24) & 0xff), + UInt8((r >> 16) & 0xff) + ] + result += [ + UInt8((r >> 8) & 0xff), + UInt8((r >> 0) & 0xff) + ] + return result + } + + /// Encrypts the 8-byte padded buffer + /// + /// - Parameters: + /// - l: left half + /// - r: right half + private func encryptBlowfishBlock(l: inout UInt32, r: inout UInt32) { + var Xl = l + var Xr = r + + for i in 0.. UInt32 { + let f1 = self.S[0][Int(x >> 24) & 0xff] + let f2 = self.S[1][Int(x >> 16) & 0xff] + let f3 = self.S[2][Int(x >> 8) & 0xff] + let f4 = self.S[3][Int(x & 0xff)] + return ((f1 &+ f2) ^ f3) &+ f4 + } +} + +extension Blowfish: Cipher { + /// Encrypt the 8-byte padded buffer, block by block. Note that for amounts of data larger than a block, it is not safe to just call encrypt() on successive blocks. + /// + /// - Parameter bytes: Plaintext data + /// - Returns: Encrypted data + internal func encrypt(_ bytes: C) throws -> Array where C.Element == UInt8, C.Index == Int { + let bytes = self.padding.add(to: Array(bytes), blockSize: Blowfish.blockSize) // FIXME: Array(bytes) copies + + var out = Array() + out.reserveCapacity(bytes.count) + + for chunk in bytes.batched(by: Blowfish.blockSize) { + out += self.encryptWorker.encrypt(block: chunk) + } + + if self.blockMode.options.contains(.paddingRequired) && (out.count % Blowfish.blockSize != 0) { + throw Error.dataPaddingRequired + } + + return out + } + + /// Decrypt the 8-byte padded buffer + /// + /// - Parameter bytes: Ciphertext data + /// - Returns: Plaintext data + internal func decrypt(_ bytes: C) throws -> Array where C.Element == UInt8, C.Index == Int { + if self.blockMode.options.contains(.paddingRequired) && (bytes.count % Blowfish.blockSize != 0) { + throw Error.dataPaddingRequired + } + + var out = Array() + out.reserveCapacity(bytes.count) + + for chunk in Array(bytes).batched(by: Blowfish.blockSize) { + out += self.decryptWorker.decrypt(block: chunk) // FIXME: copying here is innefective + } + + out = self.padding.remove(from: out, blockSize: Blowfish.blockSize) + + return out + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CBCMAC.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CBCMAC.swift new file mode 100644 index 0000000..f20b0c6 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CBCMAC.swift @@ -0,0 +1,30 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal final class CBCMAC: CMAC { + internal override func authenticate(_ bytes: Array) throws -> Array { + var inBytes = bytes + bitPadding(to: &inBytes, blockSize: CMAC.BlockSize) + let blocks = inBytes.batched(by: CMAC.BlockSize) + + var lastBlockEncryptionResult: [UInt8] = CBCMAC.Zero + try blocks.forEach { block in + let aes = try AES(key: Array(key), blockMode: CBC(iv: lastBlockEncryptionResult), padding: .noPadding) + lastBlockEncryptionResult = try aes.encrypt(block) + } + + return lastBlockEncryptionResult + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CMAC.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CMAC.swift new file mode 100644 index 0000000..2a9a7f8 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CMAC.swift @@ -0,0 +1,106 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal class CMAC: Authenticator { + internal enum Error: Swift.Error { + case wrongKeyLength + } + + internal let key: SecureBytes + + internal static let BlockSize: Int = 16 + internal static let Zero: Array = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + private static let Rb: Array = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87] + + internal init(key: Array) throws { + self.key = SecureBytes(bytes: key) + } + + // MARK: Authenticator + + // AES-CMAC + internal func authenticate(_ bytes: Array) throws -> Array { + let cipher = try AES(key: Array(key), blockMode: CBC(iv: CMAC.Zero), padding: .noPadding) + return try self.authenticate(bytes, cipher: cipher) + } + + // CMAC using a Cipher + internal func authenticate(_ bytes: Array, cipher: Cipher) throws -> Array { + let l = try cipher.encrypt(CMAC.Zero) + var subKey1 = self.leftShiftOneBit(l) + if (l[0] & 0x80) != 0 { + subKey1 = xor(CMAC.Rb, subKey1) + } + var subKey2 = self.leftShiftOneBit(subKey1) + if (subKey1[0] & 0x80) != 0 { + subKey2 = xor(CMAC.Rb, subKey2) + } + + let lastBlockComplete: Bool + let blockCount = (bytes.count + CMAC.BlockSize - 1) / CMAC.BlockSize + if blockCount == 0 { + lastBlockComplete = false + } else { + lastBlockComplete = bytes.count % CMAC.BlockSize == 0 + } + var paddedBytes = bytes + if !lastBlockComplete { + bitPadding(to: &paddedBytes, blockSize: CMAC.BlockSize) + } + + var blocks = Array(paddedBytes.batched(by: CMAC.BlockSize)) + var lastBlock = blocks.popLast()! + if lastBlockComplete { + lastBlock = xor(lastBlock, subKey1) + } else { + lastBlock = xor(lastBlock, subKey2) + } + + var x = Array(repeating: 0x00, count: CMAC.BlockSize) + var y = Array(repeating: 0x00, count: CMAC.BlockSize) + for block in blocks { + y = xor(block, x) + x = try cipher.encrypt(y) + } + // the difference between CMAC and CBC-MAC is that CMAC xors the final block with a secret value + y = self.process(lastBlock: lastBlock, with: x) + return try cipher.encrypt(y) + } + + func process(lastBlock: ArraySlice, with x: [UInt8]) -> [UInt8] { + xor(lastBlock, x) + } + + // MARK: Helper methods + + /** + Performs left shift by one bit to the bit string aquired after concatenating al bytes in the byte array + - parameters: + - bytes: byte array + - returns: bit shifted bit string split again in array of bytes + */ + private func leftShiftOneBit(_ bytes: Array) -> Array { + var shifted = Array(repeating: 0x00, count: bytes.count) + let last = bytes.count - 1 + for index in 0..= 0) + var carry = word + var i = shift + while carry > 0 { + let (d, c) = self[i].addingReportingOverflow(carry) + self[i] = d + carry = (c ? 1 : 0) + i += 1 + } + } + + /// Add the digit `d` to this integer and return the result. + /// `d` is shifted `shift` words to the left before being added. + /// + /// - Complexity: O(max(count, shift)) + internal func addingWord(_ word: Word, shiftedBy shift: Int = 0) -> CS.BigUInt { + var r = self + r.addWord(word, shiftedBy: shift) + return r + } + + /// Add `b` to this integer in place. + /// `b` is shifted `shift` words to the left before being added. + /// + /// - Complexity: O(max(count, b.count + shift)) + internal mutating func add(_ b: CS.BigUInt, shiftedBy shift: Int = 0) { + precondition(shift >= 0) + var carry = false + var bi = 0 + let bc = b.count + while bi < bc || carry { + let ai = shift + bi + let (d, c) = self[ai].addingReportingOverflow(b[bi]) + if carry { + let (d2, c2) = d.addingReportingOverflow(1) + self[ai] = d2 + carry = c || c2 + } + else { + self[ai] = d + carry = c + } + bi += 1 + } + } + + /// Add `b` to this integer and return the result. + /// `b` is shifted `shift` words to the left before being added. + /// + /// - Complexity: O(max(count, b.count + shift)) + internal func adding(_ b: CS.BigUInt, shiftedBy shift: Int = 0) -> CS.BigUInt { + var r = self + r.add(b, shiftedBy: shift) + return r + } + + /// Increment this integer by one. If `shift` is non-zero, it selects + /// the word that is to be incremented. + /// + /// - Complexity: O(count + shift) + internal mutating func increment(shiftedBy shift: Int = 0) { + self.addWord(1, shiftedBy: shift) + } + + /// Add `a` and `b` together and return the result. + /// + /// - Complexity: O(max(a.count, b.count)) + internal static func +(a: CS.BigUInt, b: CS.BigUInt) -> CS.BigUInt { + return a.adding(b) + } + + /// Add `a` and `b` together, and store the sum in `a`. + /// + /// - Complexity: O(max(a.count, b.count)) + internal static func +=(a: inout CS.BigUInt, b: CS.BigUInt) { + a.add(b, shiftedBy: 0) + } +} + +extension CS.BigInt { + /// Add `a` to `b` and return the result. + internal static func +(a: CS.BigInt, b: CS.BigInt) -> CS.BigInt { + switch (a.sign, b.sign) { + case (.plus, .plus): + return CS.BigInt(sign: .plus, magnitude: a.magnitude + b.magnitude) + case (.minus, .minus): + return CS.BigInt(sign: .minus, magnitude: a.magnitude + b.magnitude) + case (.plus, .minus): + if a.magnitude >= b.magnitude { + return CS.BigInt(sign: .plus, magnitude: a.magnitude - b.magnitude) + } + else { + return CS.BigInt(sign: .minus, magnitude: b.magnitude - a.magnitude) + } + case (.minus, .plus): + if b.magnitude >= a.magnitude { + return CS.BigInt(sign: .plus, magnitude: b.magnitude - a.magnitude) + } + else { + return CS.BigInt(sign: .minus, magnitude: a.magnitude - b.magnitude) + } + } + } + + /// Add `b` to `a` in place. + internal static func +=(a: inout CS.BigInt, b: CS.BigInt) { + a = a + b + } +} + diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/BigInt.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/BigInt.swift new file mode 100644 index 0000000..8002b98 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/BigInt.swift @@ -0,0 +1,78 @@ +// +// CS.BigInt.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2015-12-27. +// Copyright © 2016-2017 Károly Lőrentey. +// + +//MARK: CS.BigInt + +extension CS { + + /// An arbitary precision signed integer type, also known as a "big integer". + /// + /// Operations on big integers never overflow, but they might take a long time to execute. + /// The amount of memory (and address space) available is the only constraint to the magnitude of these numbers. + /// + /// This particular big integer type uses base-2^64 digits to represent integers. + /// + /// `BigInt` is essentially a tiny wrapper that extends `BigUInt` with a sign bit and provides signed integer + /// operations. Both the underlying absolute value and the negative/positive flag are available as read-write + /// properties. + /// + /// Not all algorithms of `BigUInt` are available for `BigInt` values; for example, there is no square root or + /// primality test for signed integers. When you need to call one of these, just extract the absolute value: + /// + /// ```Swift + /// CS.BigInt(255).abs.isPrime() // Returns false + /// ``` + /// + internal struct BigInt: SignedInteger { + internal enum Sign { + case plus + case minus + } + + internal typealias Magnitude = BigUInt + + /// The type representing a digit in `BigInt`'s underlying number system. + internal typealias Word = BigUInt.Word + + internal static var isSigned: Bool { + return true + } + + /// The absolute value of this integer. + internal var magnitude: BigUInt + + /// True iff the value of this integer is negative. + internal var sign: Sign + + /// Initializes a new big integer with the provided absolute number and sign flag. + internal init(sign: Sign, magnitude: BigUInt) { + self.sign = (magnitude.isZero ? .plus : sign) + self.magnitude = magnitude + } + + /// Return true iff this integer is zero. + /// + /// - Complexity: O(1) + internal var isZero: Bool { + return magnitude.isZero + } + + /// Returns `-1` if this value is negative and `1` if it’s positive; otherwise, `0`. + /// + /// - Returns: The sign of this number, expressed as an integer of the same type. + internal func signum() -> CS.BigInt { + switch sign { + case .plus: + return isZero ? 0 : 1 + case .minus: + return -1 + } + } + } + +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/BigUInt.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/BigUInt.swift new file mode 100644 index 0000000..430d82c --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/BigUInt.swift @@ -0,0 +1,389 @@ +// +// BigUInt.swift +// BigInt +// +// Created by Károly Lőrentey on 2015-12-26. +// Copyright © 2016-2017 Károly Lőrentey. +// + +extension CS { + + /// An arbitary precision unsigned integer type, also known as a "big integer". + /// + /// Operations on big integers never overflow, but they may take a long time to execute. + /// The amount of memory (and address space) available is the only constraint to the magnitude of these numbers. + /// + /// This particular big integer type uses base-2^64 digits to represent integers; you can think of it as a wrapper + /// around `Array`. (In fact, `BigUInt` only uses an array if there are more than two digits.) + internal struct BigUInt: UnsignedInteger { + /// The type representing a digit in `BigUInt`'s underlying number system. + internal typealias Word = UInt + + /// The storage variants of a `BigUInt`. + enum Kind { + /// Value consists of the two specified words (low and high). Either or both words may be zero. + case inline(Word, Word) + /// Words are stored in a slice of the storage array. + case slice(from: Int, to: Int) + /// Words are stored in the storage array. + case array + } + + internal fileprivate(set) var kind: Kind // Internal for testing only + internal fileprivate(set) var storage: [Word] // Internal for testing only; stored separately to prevent COW copies + + /// Initializes a new BigUInt with value 0. + internal init() { + self.kind = .inline(0, 0) + self.storage = [] + } + + internal init(word: Word) { + self.kind = .inline(word, 0) + self.storage = [] + } + + internal init(low: Word, high: Word) { + self.kind = .inline(low, high) + self.storage = [] + } + + /// Initializes a new BigUInt with the specified digits. The digits are ordered from least to most significant. + internal init(words: [Word]) { + self.kind = .array + self.storage = words + normalize() + } + + internal init(words: [Word], from startIndex: Int, to endIndex: Int) { + self.kind = .slice(from: startIndex, to: endIndex) + self.storage = words + normalize() + } + } + +} + +extension CS.BigUInt { + internal static var isSigned: Bool { + return false + } + + /// Return true iff this integer is zero. + /// + /// - Complexity: O(1) + var isZero: Bool { + switch kind { + case .inline(0, 0): return true + case .array: return storage.isEmpty + default: + return false + } + } + + /// Returns `1` if this value is, positive; otherwise, `0`. + /// + /// - Returns: The sign of this number, expressed as an integer of the same type. + internal func signum() -> CS.BigUInt { + return isZero ? 0 : 1 + } +} + +extension CS.BigUInt { + mutating func ensureArray() { + switch kind { + case let .inline(w0, w1): + kind = .array + storage = w1 != 0 ? [w0, w1] + : w0 != 0 ? [w0] + : [] + case let .slice(from: start, to: end): + kind = .array + storage = Array(storage[start ..< end]) + case .array: + break + } + } + + var capacity: Int { + guard case .array = kind else { return 0 } + return storage.capacity + } + + mutating func reserveCapacity(_ minimumCapacity: Int) { + switch kind { + case let .inline(w0, w1): + kind = .array + storage.reserveCapacity(minimumCapacity) + if w1 != 0 { + storage.append(w0) + storage.append(w1) + } + else if w0 != 0 { + storage.append(w0) + } + case let .slice(from: start, to: end): + kind = .array + var words: [Word] = [] + words.reserveCapacity(Swift.max(end - start, minimumCapacity)) + words.append(contentsOf: storage[start ..< end]) + storage = words + case .array: + storage.reserveCapacity(minimumCapacity) + } + } + + /// Gets rid of leading zero digits in the digit array and converts slices into inline digits when possible. + internal mutating func normalize() { + switch kind { + case .slice(from: let start, to: var end): + assert(start >= 0 && end <= storage.count && start <= end) + while start < end, storage[end - 1] == 0 { + end -= 1 + } + switch end - start { + case 0: + kind = .inline(0, 0) + storage = [] + case 1: + kind = .inline(storage[start], 0) + storage = [] + case 2: + kind = .inline(storage[start], storage[start + 1]) + storage = [] + case storage.count: + assert(start == 0) + kind = .array + default: + kind = .slice(from: start, to: end) + } + case .array where storage.last == 0: + while storage.last == 0 { + storage.removeLast() + } + default: + break + } + } + + /// Set this integer to 0 without releasing allocated storage capacity (if any). + mutating func clear() { + self.load(0) + } + + /// Set this integer to `value` by copying its digits without releasing allocated storage capacity (if any). + mutating func load(_ value: CS.BigUInt) { + switch kind { + case .inline, .slice: + self = value + case .array: + self.storage.removeAll(keepingCapacity: true) + self.storage.append(contentsOf: value.words) + } + } +} + +extension CS.BigUInt { + //MARK: Collection-like members + + /// The number of digits in this integer, excluding leading zero digits. + var count: Int { + switch kind { + case let .inline(w0, w1): + return w1 != 0 ? 2 + : w0 != 0 ? 1 + : 0 + case let .slice(from: start, to: end): + return end - start + case .array: + return storage.count + } + } + + /// Get or set a digit at a given index. + /// + /// - Note: Unlike a normal collection, it is OK for the index to be greater than or equal to `endIndex`. + /// The subscripting getter returns zero for indexes beyond the most significant digit. + /// Setting these extended digits automatically appends new elements to the underlying digit array. + /// - Requires: index >= 0 + /// - Complexity: The getter is O(1). The setter is O(1) if the conditions below are true; otherwise it's O(count). + /// - The integer's storage is not shared with another integer + /// - The integer wasn't created as a slice of another integer + /// - `index < count` + subscript(_ index: Int) -> Word { + get { + precondition(index >= 0) + switch (kind, index) { + case (.inline(let w0, _), 0): return w0 + case (.inline(_, let w1), 1): return w1 + case (.slice(from: let start, to: let end), _) where index < end - start: + return storage[start + index] + case (.array, _) where index < storage.count: + return storage[index] + default: + return 0 + } + } + set(word) { + precondition(index >= 0) + switch (kind, index) { + case let (.inline(_, w1), 0): + kind = .inline(word, w1) + case let (.inline(w0, _), 1): + kind = .inline(w0, word) + case let (.slice(from: start, to: end), _) where index < end - start: + replace(at: index, with: word) + case (.array, _) where index < storage.count: + replace(at: index, with: word) + default: + extend(at: index, with: word) + } + } + } + + private mutating func replace(at index: Int, with word: Word) { + ensureArray() + precondition(index < storage.count) + storage[index] = word + if word == 0, index == storage.count - 1 { + normalize() + } + } + + private mutating func extend(at index: Int, with word: Word) { + guard word != 0 else { return } + reserveCapacity(index + 1) + precondition(index >= storage.count) + storage.append(contentsOf: repeatElement(0, count: index - storage.count)) + storage.append(word) + } + + /// Returns an integer built from the digits of this integer in the given range. + internal func extract(_ bounds: Range) -> CS.BigUInt { + switch kind { + case let .inline(w0, w1): + let bounds = bounds.clamped(to: 0 ..< 2) + if bounds == 0 ..< 2 { + return CS.BigUInt(low: w0, high: w1) + } + else if bounds == 0 ..< 1 { + return CS.BigUInt(word: w0) + } + else if bounds == 1 ..< 2 { + return CS.BigUInt(word: w1) + } + else { + return CS.BigUInt() + } + case let .slice(from: start, to: end): + let s = Swift.min(end, start + Swift.max(bounds.lowerBound, 0)) + let e = Swift.max(s, (bounds.upperBound > end - start ? end : start + bounds.upperBound)) + return CS.BigUInt(words: storage, from: s, to: e) + case .array: + let b = bounds.clamped(to: storage.startIndex ..< storage.endIndex) + return CS.BigUInt(words: storage, from: b.lowerBound, to: b.upperBound) + } + } + + internal func extract(_ bounds: Bounds) -> CS.BigUInt where Bounds.Bound == Int { + return self.extract(bounds.relative(to: 0 ..< Int.max)) + } +} + +extension CS.BigUInt { + internal mutating func shiftRight(byWords amount: Int) { + assert(amount >= 0) + guard amount > 0 else { return } + switch kind { + case let .inline(_, w1) where amount == 1: + kind = .inline(w1, 0) + case .inline(_, _): + kind = .inline(0, 0) + case let .slice(from: start, to: end): + let s = start + amount + if s >= end { + kind = .inline(0, 0) + } + else { + kind = .slice(from: s, to: end) + normalize() + } + case .array: + if amount >= storage.count { + storage.removeAll(keepingCapacity: true) + } + else { + storage.removeFirst(amount) + } + } + } + + internal mutating func shiftLeft(byWords amount: Int) { + assert(amount >= 0) + guard amount > 0 else { return } + guard !isZero else { return } + switch kind { + case let .inline(w0, 0) where amount == 1: + kind = .inline(0, w0) + case let .inline(w0, w1): + let c = (w1 == 0 ? 1 : 2) + storage.reserveCapacity(amount + c) + storage.append(contentsOf: repeatElement(0, count: amount)) + storage.append(w0) + if w1 != 0 { + storage.append(w1) + } + kind = .array + case let .slice(from: start, to: end): + var words: [Word] = [] + words.reserveCapacity(amount + count) + words.append(contentsOf: repeatElement(0, count: amount)) + words.append(contentsOf: storage[start ..< end]) + storage = words + kind = .array + case .array: + storage.insert(contentsOf: repeatElement(0, count: amount), at: 0) + } + } +} + +extension CS.BigUInt { + //MARK: Low and High + + /// Split this integer into a high-order and a low-order part. + /// + /// - Requires: count > 1 + /// - Returns: `(low, high)` such that + /// - `self == low.add(high, shiftedBy: middleIndex)` + /// - `high.width <= floor(width / 2)` + /// - `low.width <= ceil(width / 2)` + /// - Complexity: Typically O(1), but O(count) in the worst case, because high-order zero digits need to be removed after the split. + internal var split: (high: CS.BigUInt, low: CS.BigUInt) { + precondition(count > 1) + let mid = middleIndex + return (self.extract(mid...), self.extract(.. 1 + internal var low: CS.BigUInt { + return self.extract(0 ..< middleIndex) + } + + /// The high-order half of this CS.BigUInt. + /// + /// - Returns: `self[middleIndex ..< count]` + /// - Requires: count > 1 + internal var high: CS.BigUInt { + return self.extract(middleIndex ..< count) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/BitwiseOps.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/BitwiseOps.swift new file mode 100644 index 0000000..2064fe0 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/BitwiseOps.swift @@ -0,0 +1,121 @@ +// +// BitwiseOps.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2016-01-03. +// Copyright © 2016-2017 Károly Lőrentey. +// + +//MARK: Bitwise Operations + +extension CS.BigUInt { + /// Return the ones' complement of `a`. + /// + /// - Complexity: O(a.count) + internal static prefix func ~(a: CS.BigUInt) -> CS.BigUInt { + return CS.BigUInt(words: a.words.map { ~$0 }) + } + + /// Calculate the bitwise OR of `a` and `b`, and store the result in `a`. + /// + /// - Complexity: O(max(a.count, b.count)) + internal static func |= (a: inout CS.BigUInt, b: CS.BigUInt) { + a.reserveCapacity(b.count) + for i in 0 ..< b.count { + a[i] |= b[i] + } + } + + /// Calculate the bitwise AND of `a` and `b` and return the result. + /// + /// - Complexity: O(max(a.count, b.count)) + internal static func &= (a: inout CS.BigUInt, b: CS.BigUInt) { + for i in 0 ..< Swift.max(a.count, b.count) { + a[i] &= b[i] + } + } + + /// Calculate the bitwise XOR of `a` and `b` and return the result. + /// + /// - Complexity: O(max(a.count, b.count)) + internal static func ^= (a: inout CS.BigUInt, b: CS.BigUInt) { + a.reserveCapacity(b.count) + for i in 0 ..< b.count { + a[i] ^= b[i] + } + } +} + +extension CS.BigInt { + internal static prefix func ~(x: CS.BigInt) -> CS.BigInt { + switch x.sign { + case .plus: + return CS.BigInt(sign: .minus, magnitude: x.magnitude + 1) + case .minus: + return CS.BigInt(sign: .plus, magnitude: x.magnitude - 1) + } + } + + internal static func &(lhs: inout CS.BigInt, rhs: CS.BigInt) -> CS.BigInt { + let left = lhs.words + let right = rhs.words + // Note we aren't using left.count/right.count here; we account for the sign bit separately later. + let count = Swift.max(lhs.magnitude.count, rhs.magnitude.count) + var words: [UInt] = [] + words.reserveCapacity(count) + for i in 0 ..< count { + words.append(left[i] & right[i]) + } + if lhs.sign == .minus && rhs.sign == .minus { + words.twosComplement() + return CS.BigInt(sign: .minus, magnitude: CS.BigUInt(words: words)) + } + return CS.BigInt(sign: .plus, magnitude: CS.BigUInt(words: words)) + } + + internal static func |(lhs: inout CS.BigInt, rhs: CS.BigInt) -> CS.BigInt { + let left = lhs.words + let right = rhs.words + // Note we aren't using left.count/right.count here; we account for the sign bit separately later. + let count = Swift.max(lhs.magnitude.count, rhs.magnitude.count) + var words: [UInt] = [] + words.reserveCapacity(count) + for i in 0 ..< count { + words.append(left[i] | right[i]) + } + if lhs.sign == .minus || rhs.sign == .minus { + words.twosComplement() + return CS.BigInt(sign: .minus, magnitude: CS.BigUInt(words: words)) + } + return CS.BigInt(sign: .plus, magnitude: CS.BigUInt(words: words)) + } + + internal static func ^(lhs: inout CS.BigInt, rhs: CS.BigInt) -> CS.BigInt { + let left = lhs.words + let right = rhs.words + // Note we aren't using left.count/right.count here; we account for the sign bit separately later. + let count = Swift.max(lhs.magnitude.count, rhs.magnitude.count) + var words: [UInt] = [] + words.reserveCapacity(count) + for i in 0 ..< count { + words.append(left[i] ^ right[i]) + } + if (lhs.sign == .minus) != (rhs.sign == .minus) { + words.twosComplement() + return CS.BigInt(sign: .minus, magnitude: CS.BigUInt(words: words)) + } + return CS.BigInt(sign: .plus, magnitude: CS.BigUInt(words: words)) + } + + internal static func &=(lhs: inout CS.BigInt, rhs: CS.BigInt) { + lhs = lhs & rhs + } + + internal static func |=(lhs: inout CS.BigInt, rhs: CS.BigInt) { + lhs = lhs | rhs + } + + internal static func ^=(lhs: inout CS.BigInt, rhs: CS.BigInt) { + lhs = lhs ^ rhs + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/CS.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/CS.swift new file mode 100644 index 0000000..d4e0351 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/CS.swift @@ -0,0 +1,24 @@ +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// To avoid name conflict with BigInt library, I choose to rename +// BigInt -> BigInteger +// BigUInt -> BigUInteger + +internal typealias BigInteger = CS.BigInt +internal typealias BigUInteger = CS.BigUInt + +internal enum CS { + // namespace +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Codable.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Codable.swift new file mode 100644 index 0000000..541b6dd --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Codable.swift @@ -0,0 +1,157 @@ +// +// Codable.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2017-8-11. +// Copyright © 2016-2017 Károly Lőrentey. +// + +extension CS { + + // Little-endian to big-endian + struct Units: RandomAccessCollection + where Words.Element: FixedWidthInteger, Words.Index == Int { + typealias Word = Words.Element + let words: Words + init(of type: Unit.Type, _ words: Words) { + precondition(Word.bitWidth % Unit.bitWidth == 0 || Unit.bitWidth % Word.bitWidth == 0) + self.words = words + } + var count: Int { return (words.count * Word.bitWidth + Unit.bitWidth - 1) / Unit.bitWidth } + var startIndex: Int { return 0 } + var endIndex: Int { return count } + subscript(_ index: Int) -> Unit { + let index = count - 1 - index + if Unit.bitWidth == Word.bitWidth { + return Unit(words[index]) + } + else if Unit.bitWidth > Word.bitWidth { + let c = Unit.bitWidth / Word.bitWidth + var unit: Unit = 0 + var j = 0 + for i in (c * index) ..< Swift.min(c * (index + 1), words.endIndex) { + unit |= Unit(words[i]) << j + j += Word.bitWidth + } + return unit + } + // Unit.bitWidth < Word.bitWidth + let c = Word.bitWidth / Unit.bitWidth + let i = index / c + let j = index % c + return Unit(truncatingIfNeeded: words[i] >> (j * Unit.bitWidth)) + } + } +} + +extension Array where Element: FixedWidthInteger { + // Big-endian to little-endian + init(count: Int?, generator: () throws -> Unit?) rethrows { + typealias Word = Element + precondition(Word.bitWidth % Unit.bitWidth == 0 || Unit.bitWidth % Word.bitWidth == 0) + self = [] + if Unit.bitWidth == Word.bitWidth { + if let count = count { + self.reserveCapacity(count) + } + while let unit = try generator() { + self.append(Word(unit)) + } + } + else if Unit.bitWidth > Word.bitWidth { + let wordsPerUnit = Unit.bitWidth / Word.bitWidth + if let count = count { + self.reserveCapacity(count * wordsPerUnit) + } + while let unit = try generator() { + var shift = Unit.bitWidth - Word.bitWidth + while shift >= 0 { + self.append(Word(truncatingIfNeeded: unit >> shift)) + shift -= Word.bitWidth + } + } + } + else { + let unitsPerWord = Word.bitWidth / Unit.bitWidth + if let count = count { + self.reserveCapacity((count + unitsPerWord - 1) / unitsPerWord) + } + var word: Word = 0 + var c = 0 + while let unit = try generator() { + word <<= Unit.bitWidth + word |= Word(unit) + c += Unit.bitWidth + if c == Word.bitWidth { + self.append(word) + word = 0 + c = 0 + } + } + if c > 0 { + self.append(word << c) + var shifted: Word = 0 + for i in self.indices { + let word = self[i] + self[i] = shifted | (word >> c) + shifted = word << (Word.bitWidth - c) + } + } + } + self.reverse() + } +} + +extension CS.BigInt: Codable { + internal init(from decoder: Decoder) throws { + var container = try decoder.unkeyedContainer() + + // Decode sign + let sign: CS.BigInt.Sign + switch try container.decode(String.self) { + case "+": + sign = .plus + case "-": + sign = .minus + default: + throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, + debugDescription: "Invalid big integer sign")) + } + + // Decode magnitude + let words = try [UInt](count: container.count?.advanced(by: -1)) { () -> UInt64? in + guard !container.isAtEnd else { return nil } + return try container.decode(UInt64.self) + } + let magnitude = CS.BigUInt(words: words) + + self.init(sign: sign, magnitude: magnitude) + } + + internal func encode(to encoder: Encoder) throws { + var container = encoder.unkeyedContainer() + try container.encode(sign == .plus ? "+" : "-") + let units = CS.Units(of: UInt64.self, self.magnitude.words) + if units.isEmpty { + try container.encode(0 as UInt64) + } + else { + try container.encode(contentsOf: units) + } + } +} + +extension CS.BigUInt: Codable { + internal init(from decoder: Decoder) throws { + let value = try CS.BigInt(from: decoder) + guard value.sign == .plus else { + throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, + debugDescription: "BigUInt cannot hold a negative value")) + } + self = value.magnitude + } + + internal func encode(to encoder: Encoder) throws { + try CS.BigInt(sign: .plus, magnitude: self).encode(to: encoder) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Comparable.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Comparable.swift new file mode 100644 index 0000000..648503a --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Comparable.swift @@ -0,0 +1,63 @@ +// +// Comparable.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2016-01-03. +// Copyright © 2016-2017 Károly Lőrentey. +// + +import Foundation + +extension CS.BigUInt: Comparable { + //MARK: Comparison + + /// Compare `a` to `b` and return an `NSComparisonResult` indicating their order. + /// + /// - Complexity: O(count) + internal static func compare(_ a: CS.BigUInt, _ b: CS.BigUInt) -> ComparisonResult { + if a.count != b.count { return a.count > b.count ? .orderedDescending : .orderedAscending } + for i in (0 ..< a.count).reversed() { + let ad = a[i] + let bd = b[i] + if ad != bd { return ad > bd ? .orderedDescending : .orderedAscending } + } + return .orderedSame + } + + /// Return true iff `a` is equal to `b`. + /// + /// - Complexity: O(count) + internal static func ==(a: CS.BigUInt, b: CS.BigUInt) -> Bool { + return CS.BigUInt.compare(a, b) == .orderedSame + } + + /// Return true iff `a` is less than `b`. + /// + /// - Complexity: O(count) + internal static func <(a: CS.BigUInt, b: CS.BigUInt) -> Bool { + return CS.BigUInt.compare(a, b) == .orderedAscending + } +} + +extension CS.BigInt { + /// Return true iff `a` is equal to `b`. + internal static func ==(a: CS.BigInt, b: CS.BigInt) -> Bool { + return a.sign == b.sign && a.magnitude == b.magnitude + } + + /// Return true iff `a` is less than `b`. + internal static func <(a: CS.BigInt, b: CS.BigInt) -> Bool { + switch (a.sign, b.sign) { + case (.plus, .plus): + return a.magnitude < b.magnitude + case (.plus, .minus): + return false + case (.minus, .plus): + return true + case (.minus, .minus): + return a.magnitude > b.magnitude + } + } +} + + diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/DataConversion.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/DataConversion.swift new file mode 100644 index 0000000..f6e3c6f --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/DataConversion.swift @@ -0,0 +1,179 @@ +// +// DataConversion.swift +// BigInt +// +// Created by Károly Lőrentey on 2016-01-04. +// Copyright © 2016-2017 Károly Lőrentey. +// + +import Foundation + +extension CS.BigUInt { + //MARK: NSData Conversion + + /// Initialize a BigInt from bytes accessed from an UnsafeRawBufferPointer + internal init(_ buffer: UnsafeRawBufferPointer) { + // This assumes Word is binary. + precondition(Word.bitWidth % 8 == 0) + + self.init() + + let length = buffer.count + guard length > 0 else { return } + let bytesPerDigit = Word.bitWidth / 8 + var index = length / bytesPerDigit + var c = bytesPerDigit - length % bytesPerDigit + if c == bytesPerDigit { + c = 0 + index -= 1 + } + + var word: Word = 0 + for byte in buffer { + word <<= 8 + word += Word(byte) + c += 1 + if c == bytesPerDigit { + self[index] = word + index -= 1 + c = 0 + word = 0 + } + } + assert(c == 0 && word == 0 && index == -1) + } + + + /// Initializes an integer from the bits stored inside a piece of `Data`. + /// The data is assumed to be in network (big-endian) byte order. + internal init(_ data: Data) { + // This assumes Word is binary. + precondition(Word.bitWidth % 8 == 0) + + self.init() + + let length = data.count + guard length > 0 else { return } + let bytesPerDigit = Word.bitWidth / 8 + var index = length / bytesPerDigit + var c = bytesPerDigit - length % bytesPerDigit + if c == bytesPerDigit { + c = 0 + index -= 1 + } + let word: Word = data.withUnsafeBytes { buffPtr in + var word: Word = 0 + let p = buffPtr.bindMemory(to: UInt8.self) + for byte in p { + word <<= 8 + word += Word(byte) + c += 1 + if c == bytesPerDigit { + self[index] = word + index -= 1 + c = 0 + word = 0 + } + } + return word + } + assert(c == 0 && word == 0 && index == -1) + } + + /// Return a `Data` value that contains the base-256 representation of this integer, in network (big-endian) byte order. + internal func serialize() -> Data { + // This assumes Digit is binary. + precondition(Word.bitWidth % 8 == 0) + + let byteCount = (self.bitWidth + 7) / 8 + + guard byteCount > 0 else { return Data() } + + var data = Data(count: byteCount) + data.withUnsafeMutableBytes { buffPtr in + let p = buffPtr.bindMemory(to: UInt8.self) + var i = byteCount - 1 + for var word in self.words { + for _ in 0 ..< Word.bitWidth / 8 { + p[i] = UInt8(word & 0xFF) + word >>= 8 + if i == 0 { + assert(word == 0) + break + } + i -= 1 + } + } + } + return data + } +} + +extension CS.BigInt { + + /// Initialize a BigInt from bytes accessed from an UnsafeRawBufferPointer, + /// where the first byte indicates sign (0 for positive, 1 for negative) + internal init(_ buffer: UnsafeRawBufferPointer) { + // This assumes Word is binary. + precondition(Word.bitWidth % 8 == 0) + + self.init() + + let length = buffer.count + + // Serialized data for a BigInt should contain at least 2 bytes: one representing + // the sign, and another for the non-zero magnitude. Zero is represented by an + // empty Data struct, and negative zero is not supported. + guard length > 1, let firstByte = buffer.first else { return } + + // The first byte gives the sign + // This byte is compared to a bitmask to allow additional functionality to be added + // to this byte in the future. + self.sign = firstByte & 0b1 == 0 ? .plus : .minus + + self.magnitude = CS.BigUInt(UnsafeRawBufferPointer(rebasing: buffer.dropFirst(1))) + } + + /// Initializes an integer from the bits stored inside a piece of `Data`. + /// The data is assumed to be in network (big-endian) byte order with a first + /// byte to represent the sign (0 for positive, 1 for negative) + internal init(_ data: Data) { + // This assumes Word is binary. + // This is the same assumption made when initializing CS.BigUInt from Data + precondition(Word.bitWidth % 8 == 0) + + self.init() + + // Serialized data for a BigInt should contain at least 2 bytes: one representing + // the sign, and another for the non-zero magnitude. Zero is represented by an + // empty Data struct, and negative zero is not supported. + guard data.count > 1, let firstByte = data.first else { return } + + // The first byte gives the sign + // This byte is compared to a bitmask to allow additional functionality to be added + // to this byte in the future. + self.sign = firstByte & 0b1 == 0 ? .plus : .minus + + // The remaining bytes are read and stored as the magnitude + self.magnitude = CS.BigUInt(data.dropFirst(1)) + } + + /// Return a `Data` value that contains the base-256 representation of this integer, in network (big-endian) byte order and a prepended byte to indicate the sign (0 for positive, 1 for negative) + internal func serialize() -> Data { + // Create a data object for the magnitude portion of the BigInt + let magnitudeData = self.magnitude.serialize() + + // Similar to CS.BigUInt, a value of 0 should return an initialized, empty Data struct + guard magnitudeData.count > 0 else { return magnitudeData } + + // Create a new Data struct for the signed BigInt value + var data = Data(capacity: magnitudeData.count + 1) + + // The first byte should be 0 for a positive value, or 1 for a negative value + // i.e., the sign bit is the LSB + data.append(self.sign == .plus ? 0 : 1) + + data.append(magnitudeData) + return data + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Division.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Division.swift new file mode 100644 index 0000000..a3cb216 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Division.swift @@ -0,0 +1,375 @@ +// +// Division.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2016-01-03. +// Copyright © 2016-2017 Károly Lőrentey. +// + +//MARK: Full-width multiplication and division + +// TODO: Return to `where Magnitude == Self` when SR-13491 is resolved +extension FixedWidthInteger { + private var halfShift: Self { + return Self(Self.bitWidth / 2) + + } + private var high: Self { + return self &>> halfShift + } + + private var low: Self { + let mask: Self = 1 &<< halfShift - 1 + return self & mask + } + + private var upshifted: Self { + return self &<< halfShift + } + + private var split: (high: Self, low: Self) { + return (self.high, self.low) + } + + private init(_ value: (high: Self, low: Self)) { + self = value.high.upshifted + value.low + } + + /// Divide the double-width integer `dividend` by `self` and return the quotient and remainder. + /// + /// - Requires: `dividend.high < self`, so that the result will fit in a single digit. + /// - Complexity: O(1) with 2 divisions, 6 multiplications and ~12 or so additions/subtractions. + internal func fastDividingFullWidth(_ dividend: (high: Self, low: Self.Magnitude)) -> (quotient: Self, remainder: Self) { + // Division is complicated; doing it with single-digit operations is maddeningly complicated. + // This is a Swift adaptation for "divlu2" in Hacker's Delight, + // which is in turn a C adaptation of Knuth's Algorithm D (TAOCP vol 2, 4.3.1). + precondition(dividend.high < self) + + // This replaces the implementation in stdlib, which is much slower. + // FIXME: Speed up stdlib. It should use full-width idiv on Intel processors, and + // fall back to a reasonably fast algorithm elsewhere. + + // The trick here is that we're actually implementing a 4/2 long division using half-words, + // with the long division loop unrolled into two 3/2 half-word divisions. + // Luckily, 3/2 half-word division can be approximated by a single full-word division operation + // that, when the divisor is normalized, differs from the correct result by at most 2. + + /// Find the half-word quotient in `u / vn`, which must be normalized. + /// `u` contains three half-words in the two halves of `u.high` and the lower half of + /// `u.low`. (The weird distribution makes for a slightly better fit with the input.) + /// `vn` contains the normalized divisor, consisting of two half-words. + /// + /// - Requires: u.high < vn && u.low.high == 0 && vn.leadingZeroBitCount == 0 + func quotient(dividing u: (high: Self, low: Self), by vn: Self) -> Self { + let (vn1, vn0) = vn.split + // Get approximate quotient. + let (q, r) = u.high.quotientAndRemainder(dividingBy: vn1) + let p = q * vn0 + // q is often already correct, but sometimes the approximation overshoots by at most 2. + // The code that follows checks for this while being careful to only perform single-digit operations. + if q.high == 0 && p <= r.upshifted + u.low { return q } + let r2 = r + vn1 + if r2.high != 0 { return q - 1 } + if (q - 1).high == 0 && p - vn0 <= r2.upshifted + u.low { return q - 1 } + //assert((r + 2 * vn1).high != 0 || p - 2 * vn0 <= (r + 2 * vn1).upshifted + u.low) + return q - 2 + } + /// Divide 3 half-digits by 2 half-digits to get a half-digit quotient and a full-digit remainder. + /// + /// - Requires: u.high < v && u.low.high == 0 && vn.width = width(Digit) + func quotientAndRemainder(dividing u: (high: Self, low: Self), by v: Self) -> (quotient: Self, remainder: Self) { + let q = quotient(dividing: u, by: v) + // Note that `uh.low` masks off a couple of bits, and `q * v` and the + // subtraction are likely to overflow. Despite this, the end result (remainder) will + // still be correct and it will fit inside a single (full) Digit. + let r = Self(u) &- q &* v + assert(r < v) + return (q, r) + } + + // Normalize the dividend and the divisor (self) such that the divisor has no leading zeroes. + let z = Self(self.leadingZeroBitCount) + let w = Self(Self.bitWidth) - z + let vn = self << z + + let un32 = (z == 0 ? dividend.high : (dividend.high &<< z) | ((dividend.low as! Self) &>> w)) // No bits are lost + let un10 = dividend.low &<< z + let (un1, un0) = un10.split + + // Divide `(un32,un10)` by `vn`, splitting the full 4/2 division into two 3/2 ones. + let (q1, un21) = quotientAndRemainder(dividing: (un32, (un1 as! Self)), by: vn) + let (q0, rn) = quotientAndRemainder(dividing: (un21, (un0 as! Self)), by: vn) + + // Undo normalization of the remainder and combine the two halves of the quotient. + let mod = rn >> z + let div = Self((q1, q0)) + return (div, mod) + } + + /// Return the quotient of the 3/2-word division `x/y` as a single word. + /// + /// - Requires: (x.0, x.1) <= y && y.0.high != 0 + /// - Returns: The exact value when it fits in a single word, otherwise `Self`. + static func approximateQuotient(dividing x: (Self, Self, Self), by y: (Self, Self)) -> Self { + // Start with q = (x.0, x.1) / y.0, (or Word.max on overflow) + var q: Self + var r: Self + if x.0 == y.0 { + q = Self.max + let (s, o) = x.0.addingReportingOverflow(x.1) + if o { return q } + r = s + } + else { + (q, r) = y.0.fastDividingFullWidth((x.0, (x.1 as! Magnitude))) + } + // Now refine q by considering x.2 and y.1. + // Note that since y is normalized, q * y - x is between 0 and 2. + let (ph, pl) = q.multipliedFullWidth(by: y.1) + if ph < r || (ph == r && pl <= x.2) { return q } + + let (r1, ro) = r.addingReportingOverflow(y.0) + if ro { return q - 1 } + + let (pl1, so) = pl.subtractingReportingOverflow((y.1 as! Magnitude)) + let ph1 = (so ? ph - 1 : ph) + + if ph1 < r1 || (ph1 == r1 && pl1 <= x.2) { return q - 1 } + return q - 2 + } +} + +extension CS.BigUInt { + //MARK: Division + + /// Divide this integer by the word `y`, leaving the quotient in its place and returning the remainder. + /// + /// - Requires: y > 0 + /// - Complexity: O(count) + internal mutating func divide(byWord y: Word) -> Word { + precondition(y > 0) + if y == 1 { return 0 } + + var remainder: Word = 0 + for i in (0 ..< count).reversed() { + let u = self[i] + (self[i], remainder) = y.fastDividingFullWidth((remainder, u)) + } + return remainder + } + + /// Divide this integer by the word `y` and return the resulting quotient and remainder. + /// + /// - Requires: y > 0 + /// - Returns: (quotient, remainder) where quotient = floor(x/y), remainder = x - quotient * y + /// - Complexity: O(x.count) + internal func quotientAndRemainder(dividingByWord y: Word) -> (quotient: CS.BigUInt, remainder: Word) { + var div = self + let mod = div.divide(byWord: y) + return (div, mod) + } + + /// Divide `x` by `y`, putting the quotient in `x` and the remainder in `y`. + /// Reusing integers like this reduces the number of allocations during the calculation. + static func divide(_ x: inout CS.BigUInt, by y: inout CS.BigUInt) { + // This is a Swift adaptation of "divmnu" from Hacker's Delight, which is in + // turn a C adaptation of Knuth's Algorithm D (TAOCP vol 2, 4.3.1). + + precondition(!y.isZero) + + // First, let's take care of the easy cases. + if x < y { + (x, y) = (0, x) + return + } + if y.count == 1 { + // The single-word case reduces to a simpler loop. + y = CS.BigUInt(x.divide(byWord: y[0])) + return + } + + // In the hard cases, we will perform the long division algorithm we learned in school. + // It works by successively calculating the single-word quotient of the top y.count + 1 + // words of x divided by y, replacing the top of x with the remainder, and repeating + // the process one word lower. + // + // The tricky part is that the algorithm needs to be able to do n+1/n word divisions, + // but we only have a primitive for dividing two words by a single + // word. (Remember that this step is also tricky when we do it on paper!) + // + // The solution is that the long division can be approximated by a single full division + // using just the most significant words. We can then use multiplications and + // subtractions to refine the approximation until we get the correct quotient word. + // + // We could do this by doing a simple 2/1 full division, but Knuth goes one step further, + // and implements a 3/2 division. This results in an exact approximation in the + // vast majority of cases, eliminating an extra subtraction over big integers. + // + // The function `approximateQuotient` above implements Knuth's 3/2 division algorithm. + // It requires that the divisor's most significant word is larger than + // Word.max / 2. This ensures that the approximation has tiny error bounds, + // which is what makes this entire approach viable. + // To satisfy this requirement, we will normalize the division by multiplying + // both the divisor and the dividend by the same (small) factor. + let z = y.leadingZeroBitCount + y <<= z + x <<= z // We'll calculate the remainder in the normalized dividend. + var quotient = CS.BigUInt() + assert(y.leadingZeroBitCount == 0) + + // We're ready to start the long division! + let dc = y.count + let d1 = y[dc - 1] + let d0 = y[dc - 2] + var product: CS.BigUInt = 0 + for j in (dc ... x.count).reversed() { + // Approximate dividing the top dc+1 words of `remainder` using the topmost 3/2 words. + let r2 = x[j] + let r1 = x[j - 1] + let r0 = x[j - 2] + let q = Word.approximateQuotient(dividing: (r2, r1, r0), by: (d1, d0)) + + // Multiply the entire divisor with `q` and subtract the result from remainder. + // Normalization ensures the 3/2 quotient will either be exact for the full division, or + // it may overshoot by at most 1, in which case the product will be greater + // than the remainder. + product.load(y) + product.multiply(byWord: q) + if product <= x.extract(j - dc ..< j + 1) { + x.subtract(product, shiftedBy: j - dc) + quotient[j - dc] = q + } + else { + // This case is extremely rare -- it has a probability of 1/2^(Word.bitWidth - 1). + x.add(y, shiftedBy: j - dc) + x.subtract(product, shiftedBy: j - dc) + quotient[j - dc] = q - 1 + } + } + // The remainder's normalization needs to be undone, but otherwise we're done. + x >>= z + y = x + x = quotient + } + + /// Divide `x` by `y`, putting the remainder in `x`. + mutating func formRemainder(dividingBy y: CS.BigUInt, normalizedBy shift: Int) { + precondition(!y.isZero) + assert(y.leadingZeroBitCount == 0) + if y.count == 1 { + let remainder = self.divide(byWord: y[0] >> shift) + self.load(CS.BigUInt(remainder)) + return + } + self <<= shift + if self >= y { + let dc = y.count + let d1 = y[dc - 1] + let d0 = y[dc - 2] + var product: CS.BigUInt = 0 + for j in (dc ... self.count).reversed() { + let r2 = self[j] + let r1 = self[j - 1] + let r0 = self[j - 2] + let q = Word.approximateQuotient(dividing: (r2, r1, r0), by: (d1, d0)) + product.load(y) + product.multiply(byWord: q) + if product <= self.extract(j - dc ..< j + 1) { + self.subtract(product, shiftedBy: j - dc) + } + else { + self.add(y, shiftedBy: j - dc) + self.subtract(product, shiftedBy: j - dc) + } + } + } + self >>= shift + } + + + /// Divide this integer by `y` and return the resulting quotient and remainder. + /// + /// - Requires: `y > 0` + /// - Returns: `(quotient, remainder)` where `quotient = floor(self/y)`, `remainder = self - quotient * y` + /// - Complexity: O(count^2) + internal func quotientAndRemainder(dividingBy y: CS.BigUInt) -> (quotient: CS.BigUInt, remainder: CS.BigUInt) { + var x = self + var y = y + CS.BigUInt.divide(&x, by: &y) + return (x, y) + } + + /// Divide `x` by `y` and return the quotient. + /// + /// - Note: Use `divided(by:)` if you also need the remainder. + internal static func /(x: CS.BigUInt, y: CS.BigUInt) -> CS.BigUInt { + return x.quotientAndRemainder(dividingBy: y).quotient + } + + /// Divide `x` by `y` and return the remainder. + /// + /// - Note: Use `divided(by:)` if you also need the remainder. + internal static func %(x: CS.BigUInt, y: CS.BigUInt) -> CS.BigUInt { + var x = x + let shift = y.leadingZeroBitCount + x.formRemainder(dividingBy: y << shift, normalizedBy: shift) + return x + } + + /// Divide `x` by `y` and store the quotient in `x`. + /// + /// - Note: Use `divided(by:)` if you also need the remainder. + internal static func /=(x: inout CS.BigUInt, y: CS.BigUInt) { + var y = y + CS.BigUInt.divide(&x, by: &y) + } + + /// Divide `x` by `y` and store the remainder in `x`. + /// + /// - Note: Use `divided(by:)` if you also need the remainder. + internal static func %=(x: inout CS.BigUInt, y: CS.BigUInt) { + let shift = y.leadingZeroBitCount + x.formRemainder(dividingBy: y << shift, normalizedBy: shift) + } +} + +extension CS.BigInt { + /// Divide this integer by `y` and return the resulting quotient and remainder. + /// + /// - Requires: `y > 0` + /// - Returns: `(quotient, remainder)` where `quotient = floor(self/y)`, `remainder = self - quotient * y` + /// - Complexity: O(count^2) + internal func quotientAndRemainder(dividingBy y: CS.BigInt) -> (quotient: CS.BigInt, remainder: CS.BigInt) { + var a = self.magnitude + var b = y.magnitude + CS.BigUInt.divide(&a, by: &b) + return ( CS.BigInt(sign: self.sign == y.sign ? .plus : .minus, magnitude: a), + CS.BigInt(sign: self.sign, magnitude: b)) + } + + /// Divide `a` by `b` and return the quotient. Traps if `b` is zero. + internal static func /(a: CS.BigInt, b: CS.BigInt) -> CS.BigInt { + return CS.BigInt(sign: a.sign == b.sign ? .plus : .minus, magnitude: a.magnitude / b.magnitude) + } + + /// Divide `a` by `b` and return the remainder. The result has the same sign as `a`. + internal static func %(a: CS.BigInt, b: CS.BigInt) -> CS.BigInt { + return CS.BigInt(sign: a.sign, magnitude: a.magnitude % b.magnitude) + } + + /// Return the result of `a` mod `b`. The result is always a nonnegative integer that is less than the absolute value of `b`. + internal func modulus(_ mod: CS.BigInt) -> CS.BigInt { + let remainder = self.magnitude % mod.magnitude + return CS.BigInt( + self.sign == .minus && !remainder.isZero + ? mod.magnitude - remainder + : remainder) + } +} + +extension CS.BigInt { + /// Divide `a` by `b` storing the quotient in `a`. + internal static func /=(a: inout CS.BigInt, b: CS.BigInt) { a = a / b } + /// Divide `a` by `b` storing the remainder in `a`. + internal static func %=(a: inout CS.BigInt, b: CS.BigInt) { a = a % b } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Exponentiation.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Exponentiation.swift new file mode 100644 index 0000000..be4505d --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Exponentiation.swift @@ -0,0 +1,119 @@ +// +// Exponentiation.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2016-01-03. +// Copyright © 2016-2017 Károly Lőrentey. +// + +extension CS.BigUInt { + //MARK: Exponentiation + + /// Returns this integer raised to the power `exponent`. + /// + /// This function calculates the result by [successively squaring the base while halving the exponent][expsqr]. + /// + /// [expsqr]: https://en.wikipedia.org/wiki/Exponentiation_by_squaring + /// + /// - Note: This function can be unreasonably expensive for large exponents, which is why `exponent` is + /// a simple integer value. If you want to calculate big exponents, you'll probably need to use + /// the modulo arithmetic variant. + /// - Returns: 1 if `exponent == 0`, otherwise `self` raised to `exponent`. (This implies that `0.power(0) == 1`.) + /// - SeeAlso: `BigUInt.power(_:, modulus:)` + /// - Complexity: O((exponent * self.count)^log2(3)) or somesuch. The result may require a large amount of memory, too. + internal func power(_ exponent: Int) -> CS.BigUInt { + if exponent == 0 { return 1 } + if exponent == 1 { return self } + if exponent < 0 { + precondition(!self.isZero) + return self == 1 ? 1 : 0 + } + if self <= 1 { return self } + var result = CS.BigUInt(1) + var b = self + var e = exponent + while e > 0 { + if e & 1 == 1 { + result *= b + } + e >>= 1 + b *= b + } + return result + } + + /// Returns the remainder of this integer raised to the power `exponent` in modulo arithmetic under `modulus`. + /// + /// Uses the [right-to-left binary method][rtlb]. + /// + /// [rtlb]: https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method + /// + /// - Complexity: O(exponent.count * modulus.count^log2(3)) or somesuch + internal func power(_ exponent: CS.BigUInt, modulus: CS.BigUInt) -> CS.BigUInt { + precondition(!modulus.isZero) + if modulus == (1 as CS.BigUInt) { return 0 } + let shift = modulus.leadingZeroBitCount + let normalizedModulus = modulus << shift + var result = CS.BigUInt(1) + var b = self + b.formRemainder(dividingBy: normalizedModulus, normalizedBy: shift) + for var e in exponent.words { + for _ in 0 ..< Word.bitWidth { + if e & 1 == 1 { + result *= b + result.formRemainder(dividingBy: normalizedModulus, normalizedBy: shift) + } + e >>= 1 + b *= b + b.formRemainder(dividingBy: normalizedModulus, normalizedBy: shift) + } + } + return result + } +} + +extension CS.BigInt { + /// Returns this integer raised to the power `exponent`. + /// + /// This function calculates the result by [successively squaring the base while halving the exponent][expsqr]. + /// + /// [expsqr]: https://en.wikipedia.org/wiki/Exponentiation_by_squaring + /// + /// - Note: This function can be unreasonably expensive for large exponents, which is why `exponent` is + /// a simple integer value. If you want to calculate big exponents, you'll probably need to use + /// the modulo arithmetic variant. + /// - Returns: 1 if `exponent == 0`, otherwise `self` raised to `exponent`. (This implies that `0.power(0) == 1`.) + /// - SeeAlso: `BigUInt.power(_:, modulus:)` + /// - Complexity: O((exponent * self.count)^log2(3)) or somesuch. The result may require a large amount of memory, too. + internal func power(_ exponent: Int) -> CS.BigInt { + return CS.BigInt(sign: self.sign == .minus && exponent & 1 != 0 ? .minus : .plus, + magnitude: self.magnitude.power(exponent)) + } + + /// Returns the remainder of this integer raised to the power `exponent` in modulo arithmetic under `modulus`. + /// + /// Uses the [right-to-left binary method][rtlb]. + /// + /// [rtlb]: https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method + /// + /// - Complexity: O(exponent.count * modulus.count^log2(3)) or somesuch + internal func power(_ exponent: CS.BigInt, modulus: CS.BigInt) -> CS.BigInt { + precondition(!modulus.isZero) + if modulus.magnitude == 1 { return 0 } + if exponent.isZero { return 1 } + if exponent == 1 { return self.modulus(modulus) } + if exponent < 0 { + precondition(!self.isZero) + guard magnitude == 1 else { return 0 } + guard sign == .minus else { return 1 } + guard exponent.magnitude[0] & 1 != 0 else { return 1 } + return CS.BigInt(modulus.magnitude - 1) + } + let power = self.magnitude.power(exponent.magnitude, + modulus: modulus.magnitude) + if self.sign == .plus || exponent.magnitude[0] & 1 == 0 || power.isZero { + return CS.BigInt(power) + } + return CS.BigInt(modulus.magnitude - power) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/FloatingPointConversion.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/FloatingPointConversion.swift new file mode 100644 index 0000000..848f81a --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/FloatingPointConversion.swift @@ -0,0 +1,73 @@ +// +// FloatingPointConversion.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2017-08-11. +// Copyright © 2016-2017 Károly Lőrentey. +// + +extension CS.BigUInt { + internal init?(exactly source: T) { + guard source.isFinite else { return nil } + guard !source.isZero else { self = 0; return } + guard source.sign == .plus else { return nil } + let value = source.rounded(.towardZero) + guard value == source else { return nil } + assert(value.floatingPointClass == .positiveNormal) + assert(value.exponent >= 0) + let significand = value.significandBitPattern + self = (CS.BigUInt(1) << value.exponent) + CS.BigUInt(significand) >> (T.significandBitCount - Int(value.exponent)) + } + + internal init(_ source: T) { + self.init(exactly: source.rounded(.towardZero))! + } +} + +extension CS.BigInt { + internal init?(exactly source: T) { + switch source.sign{ + case .plus: + guard let magnitude = CS.BigUInt(exactly: source) else { return nil } + self = CS.BigInt(sign: .plus, magnitude: magnitude) + case .minus: + guard let magnitude = CS.BigUInt(exactly: -source) else { return nil } + self = CS.BigInt(sign: .minus, magnitude: magnitude) + } + } + + internal init(_ source: T) { + self.init(exactly: source.rounded(.towardZero))! + } +} + +extension BinaryFloatingPoint where RawExponent: FixedWidthInteger, RawSignificand: FixedWidthInteger { + internal init(_ value: CS.BigInt) { + guard !value.isZero else { self = 0; return } + let v = value.magnitude + let bitWidth = v.bitWidth + var exponent = bitWidth - 1 + let shift = bitWidth - Self.significandBitCount - 1 + var significand = value.magnitude >> (shift - 1) + if significand[0] & 3 == 3 { // Handle rounding + significand >>= 1 + significand += 1 + if significand.trailingZeroBitCount >= Self.significandBitCount { + exponent += 1 + } + } + else { + significand >>= 1 + } + let bias = 1 << (Self.exponentBitCount - 1) - 1 + guard exponent <= bias else { self = Self.infinity; return } + significand &= 1 << Self.significandBitCount - 1 + self = Self.init(sign: value.sign == .plus ? .plus : .minus, + exponentBitPattern: RawExponent(bias + exponent), + significandBitPattern: RawSignificand(significand)) + } + + internal init(_ value: CS.BigUInt) { + self.init(CS.BigInt(sign: .plus, magnitude: value)) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/GCD.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/GCD.swift new file mode 100644 index 0000000..4df8250 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/GCD.swift @@ -0,0 +1,80 @@ +// +// GCD.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2016-01-03. +// Copyright © 2016-2017 Károly Lőrentey. +// + +extension CS.BigUInt { + //MARK: Greatest Common Divisor + + /// Returns the greatest common divisor of `self` and `b`. + /// + /// - Complexity: O(count^2) where count = max(self.count, b.count) + internal func greatestCommonDivisor(with b: CS.BigUInt) -> CS.BigUInt { + // This is Stein's algorithm: https://en.wikipedia.org/wiki/Binary_GCD_algorithm + if self.isZero { return b } + if b.isZero { return self } + + let az = self.trailingZeroBitCount + let bz = b.trailingZeroBitCount + let twos = Swift.min(az, bz) + + var (x, y) = (self >> az, b >> bz) + if x < y { swap(&x, &y) } + + while !x.isZero { + x >>= x.trailingZeroBitCount + if x < y { swap(&x, &y) } + x -= y + } + return y << twos + } + + /// Returns the [multiplicative inverse of this integer in modulo `modulus` arithmetic][inverse], + /// or `nil` if there is no such number. + /// + /// [inverse]: https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers + /// + /// - Returns: If `gcd(self, modulus) == 1`, the value returned is an integer `a < modulus` such that `(a * self) % modulus == 1`. If `self` and `modulus` aren't coprime, the return value is `nil`. + /// - Requires: modulus > 1 + /// - Complexity: O(count^3) + internal func inverse(_ modulus: CS.BigUInt) -> CS.BigUInt? { + precondition(modulus > 1) + var t1 = CS.BigInt(0) + var t2 = CS.BigInt(1) + var r1 = modulus + var r2 = self + while !r2.isZero { + let quotient = r1 / r2 + (t1, t2) = (t2, t1 - CS.BigInt(quotient) * t2) + (r1, r2) = (r2, r1 - quotient * r2) + } + if r1 > 1 { return nil } + if t1.sign == .minus { return modulus - t1.magnitude } + return t1.magnitude + } +} + +extension CS.BigInt { + /// Returns the greatest common divisor of `a` and `b`. + /// + /// - Complexity: O(count^2) where count = max(a.count, b.count) + internal func greatestCommonDivisor(with b: CS.BigInt) -> CS.BigInt { + return CS.BigInt(self.magnitude.greatestCommonDivisor(with: b.magnitude)) + } + + /// Returns the [multiplicative inverse of this integer in modulo `modulus` arithmetic][inverse], + /// or `nil` if there is no such number. + /// + /// [inverse]: https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers + /// + /// - Returns: If `gcd(self, modulus) == 1`, the value returned is an integer `a < modulus` such that `(a * self) % modulus == 1`. If `self` and `modulus` aren't coprime, the return value is `nil`. + /// - Requires: modulus.magnitude > 1 + /// - Complexity: O(count^3) + internal func inverse(_ modulus: CS.BigInt) -> CS.BigInt? { + guard let inv = self.magnitude.inverse(modulus.magnitude) else { return nil } + return CS.BigInt(self.sign == .plus || inv.isZero ? inv : modulus.magnitude - inv) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Hashable.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Hashable.swift new file mode 100644 index 0000000..03871ba --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Hashable.swift @@ -0,0 +1,26 @@ +// +// Hashable.swift +// BigInt +// +// Created by Károly Lőrentey on 2016-01-03. +// Copyright © 2016-2017 Károly Lőrentey. +// + +extension CS.BigUInt: Hashable { + //MARK: Hashing + + /// Append this `BigUInt` to the specified hasher. + internal func hash(into hasher: inout Hasher) { + for word in self.words { + hasher.combine(word) + } + } +} + +extension CS.BigInt: Hashable { + /// Append this `BigInt` to the specified hasher. + internal func hash(into hasher: inout Hasher) { + hasher.combine(sign) + hasher.combine(magnitude) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/IntegerConversion.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/IntegerConversion.swift new file mode 100644 index 0000000..5fece8a --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/IntegerConversion.swift @@ -0,0 +1,89 @@ +// +// IntegerConversion.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2017-08-11. +// Copyright © 2016-2017 Károly Lőrentey. +// + +extension CS.BigUInt { + internal init?(exactly source: T) { + guard source >= (0 as T) else { return nil } + if source.bitWidth <= 2 * Word.bitWidth { + var it = source.words.makeIterator() + self.init(low: it.next() ?? 0, high: it.next() ?? 0) + precondition(it.next() == nil, "Length of BinaryInteger.words is greater than its bitWidth") + } + else { + self.init(words: source.words) + } + } + + internal init(_ source: T) { + precondition(source >= (0 as T), "BigUInt cannot represent negative values") + self.init(exactly: source)! + } + + internal init(truncatingIfNeeded source: T) { + self.init(words: source.words) + } + + internal init(clamping source: T) { + if source <= (0 as T) { + self.init() + } + else { + self.init(words: source.words) + } + } +} + +extension CS.BigInt { + internal init() { + self.init(sign: .plus, magnitude: 0) + } + + /// Initializes a new signed big integer with the same value as the specified unsigned big integer. + internal init(_ integer: CS.BigUInt) { + self.magnitude = integer + self.sign = .plus + } + + internal init(_ source: T) where T : BinaryInteger { + if source >= (0 as T) { + self.init(sign: .plus, magnitude: CS.BigUInt(source)) + } + else { + var words = Array(source.words) + words.twosComplement() + self.init(sign: .minus, magnitude: CS.BigUInt(words: words)) + } + } + + internal init?(exactly source: T) where T : BinaryInteger { + self.init(source) + } + + internal init(clamping source: T) where T : BinaryInteger { + self.init(source) + } + + internal init(truncatingIfNeeded source: T) where T : BinaryInteger { + self.init(source) + } +} + +extension CS.BigUInt: ExpressibleByIntegerLiteral { + /// Initialize a new big integer from an integer literal. + internal init(integerLiteral value: UInt64) { + self.init(value) + } +} + +extension CS.BigInt: ExpressibleByIntegerLiteral { + /// Initialize a new big integer from an integer literal. + internal init(integerLiteral value: Int64) { + self.init(value) + } +} + diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Multiplication.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Multiplication.swift new file mode 100644 index 0000000..47d83f0 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Multiplication.swift @@ -0,0 +1,165 @@ +// +// Multiplication.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2016-01-03. +// Copyright © 2016-2017 Károly Lőrentey. +// + +extension CS.BigUInt { + + //MARK: Multiplication + + /// Multiply this big integer by a single word, and store the result in place of the original big integer. + /// + /// - Complexity: O(count) + internal mutating func multiply(byWord y: Word) { + guard y != 0 else { self = 0; return } + guard y != 1 else { return } + var carry: Word = 0 + let c = self.count + for i in 0 ..< c { + let (h, l) = self[i].multipliedFullWidth(by: y) + let (low, o) = l.addingReportingOverflow(carry) + self[i] = low + carry = (o ? h + 1 : h) + } + self[c] = carry + } + + /// Multiply this big integer by a single Word, and return the result. + /// + /// - Complexity: O(count) + internal func multiplied(byWord y: Word) -> CS.BigUInt { + var r = self + r.multiply(byWord: y) + return r + } + + /// Multiply `x` by `y`, and add the result to this integer, optionally shifted `shift` words to the left. + /// + /// - Note: This is the fused multiply/shift/add operation; it is more efficient than doing the components + /// individually. (The fused operation doesn't need to allocate space for temporary big integers.) + /// - Returns: `self` is set to `self + (x * y) << (shift * 2^Word.bitWidth)` + /// - Complexity: O(count) + internal mutating func multiplyAndAdd(_ x: CS.BigUInt, _ y: Word, shiftedBy shift: Int = 0) { + precondition(shift >= 0) + guard y != 0 && x.count > 0 else { return } + guard y != 1 else { self.add(x, shiftedBy: shift); return } + var mulCarry: Word = 0 + var addCarry = false + let xc = x.count + var xi = 0 + while xi < xc || addCarry || mulCarry > 0 { + let (h, l) = x[xi].multipliedFullWidth(by: y) + let (low, o) = l.addingReportingOverflow(mulCarry) + mulCarry = (o ? h + 1 : h) + + let ai = shift + xi + let (sum1, so1) = self[ai].addingReportingOverflow(low) + if addCarry { + let (sum2, so2) = sum1.addingReportingOverflow(1) + self[ai] = sum2 + addCarry = so1 || so2 + } + else { + self[ai] = sum1 + addCarry = so1 + } + xi += 1 + } + } + + /// Multiply this integer by `y` and return the result. + /// + /// - Note: This uses the naive O(n^2) multiplication algorithm unless both arguments have more than + /// `BigUInt.directMultiplicationLimit` words. + /// - Complexity: O(n^log2(3)) + internal func multiplied(by y: CS.BigUInt) -> CS.BigUInt { + // This method is mostly defined for symmetry with the rest of the arithmetic operations. + return self * y + } + + /// Multiplication switches to an asymptotically better recursive algorithm when arguments have more words than this limit. + internal static var directMultiplicationLimit: Int = 1024 + + /// Multiply `a` by `b` and return the result. + /// + /// - Note: This uses the naive O(n^2) multiplication algorithm unless both arguments have more than + /// `BigUInt.directMultiplicationLimit` words. + /// - Complexity: O(n^log2(3)) + internal static func *(x: CS.BigUInt, y: CS.BigUInt) -> CS.BigUInt { + let xc = x.count + let yc = y.count + if xc == 0 { return CS.BigUInt() } + if yc == 0 { return CS.BigUInt() } + if yc == 1 { return x.multiplied(byWord: y[0]) } + if xc == 1 { return y.multiplied(byWord: x[0]) } + + if Swift.min(xc, yc) <= CS.BigUInt.directMultiplicationLimit { + // Long multiplication. + let left = (xc < yc ? y : x) + let right = (xc < yc ? x : y) + var result = CS.BigUInt() + for i in (0 ..< right.count).reversed() { + result.multiplyAndAdd(left, right[i], shiftedBy: i) + } + return result + } + + if yc < xc { + let (xh, xl) = x.split + var r = xl * y + r.add(xh * y, shiftedBy: x.middleIndex) + return r + } + else if xc < yc { + let (yh, yl) = y.split + var r = yl * x + r.add(yh * x, shiftedBy: y.middleIndex) + return r + } + + let shift = x.middleIndex + + // Karatsuba multiplication: + // x * y = * = (ignoring carry) + let (a, b) = x.split + let (c, d) = y.split + + let high = a * c + let low = b * d + let xp = a >= b + let yp = c >= d + let xm = (xp ? a - b : b - a) + let ym = (yp ? c - d : d - c) + let m = xm * ym + + var r = low + r.add(high, shiftedBy: 2 * shift) + r.add(low, shiftedBy: shift) + r.add(high, shiftedBy: shift) + if xp == yp { + r.subtract(m, shiftedBy: shift) + } + else { + r.add(m, shiftedBy: shift) + } + return r + } + + /// Multiply `a` by `b` and store the result in `a`. + internal static func *=(a: inout CS.BigUInt, b: CS.BigUInt) { + a = a * b + } +} + +extension CS.BigInt { + /// Multiply `a` with `b` and return the result. + internal static func *(a: CS.BigInt, b: CS.BigInt) -> CS.BigInt { + return CS.BigInt(sign: a.sign == b.sign ? .plus : .minus, magnitude: a.magnitude * b.magnitude) + } + + /// Multiply `a` with `b` in place. + internal static func *=(a: inout CS.BigInt, b: CS.BigInt) { a = a * b } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/PrimeTest.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/PrimeTest.swift new file mode 100644 index 0000000..793a2ed --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/PrimeTest.swift @@ -0,0 +1,153 @@ +// +// PrimeTest.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2016-01-04. +// Copyright © 2016-2017 Károly Lőrentey. +// + +/// The first several [prime numbers][primes]. +/// +/// [primes]: https://oeis.org/A000040 +let primes: [CS.BigUInt.Word] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41] + +/// The ith element in this sequence is the smallest composite number that passes the strong probable prime test +/// for all of the first (i+1) primes. +/// +/// This is sequence [A014233](http://oeis.org/A014233) on the [Online Encyclopaedia of Integer Sequences](http://oeis.org). +let pseudoPrimes: [CS.BigUInt] = [ + /* 2 */ 2_047, + /* 3 */ 1_373_653, + /* 5 */ 25_326_001, + /* 7 */ 3_215_031_751, + /* 11 */ 2_152_302_898_747, + /* 13 */ 3_474_749_660_383, + /* 17 */ 341_550_071_728_321, + /* 19 */ 341_550_071_728_321, + /* 23 */ 3_825_123_056_546_413_051, + /* 29 */ 3_825_123_056_546_413_051, + /* 31 */ 3_825_123_056_546_413_051, + /* 37 */ "318665857834031151167461", + /* 41 */ "3317044064679887385961981", +] + +extension CS.BigUInt { + //MARK: Primality Testing + + /// Returns true iff this integer passes the [strong probable prime test][sppt] for the specified base. + /// + /// [sppt]: https://en.wikipedia.org/wiki/Probable_prime + internal func isStrongProbablePrime(_ base: CS.BigUInt) -> Bool { + precondition(base > (1 as CS.BigUInt)) + precondition(self > (0 as CS.BigUInt)) + let dec = self - 1 + + let r = dec.trailingZeroBitCount + let d = dec >> r + + var test = base.power(d, modulus: self) + if test == 1 || test == dec { return true } + + if r > 0 { + let shift = self.leadingZeroBitCount + let normalized = self << shift + for _ in 1 ..< r { + test *= test + test.formRemainder(dividingBy: normalized, normalizedBy: shift) + if test == 1 { + return false + } + if test == dec { return true } + } + } + return false + } + + /// Returns true if this integer is probably prime. Returns false if this integer is definitely not prime. + /// + /// This function performs a probabilistic [Miller-Rabin Primality Test][mrpt], consisting of `rounds` iterations, + /// each calculating the strong probable prime test for a random base. The number of rounds is 10 by default, + /// but you may specify your own choice. + /// + /// To speed things up, the function checks if `self` is divisible by the first few prime numbers before + /// diving into (slower) Miller-Rabin testing. + /// + /// Also, when `self` is less than 82 bits wide, `isPrime` does a deterministic test that is guaranteed to + /// return a correct result. + /// + /// [mrpt]: https://en.wikipedia.org/wiki/Miller–Rabin_primality_test + internal func isPrime(rounds: Int = 10) -> Bool { + if count <= 1 && self[0] < 2 { return false } + if count == 1 && self[0] < 4 { return true } + + // Even numbers above 2 aren't prime. + if self[0] & 1 == 0 { return false } + + // Quickly check for small primes. + for i in 1 ..< primes.count { + let p = primes[i] + if self.count == 1 && self[0] == p { + return true + } + if self.quotientAndRemainder(dividingByWord: p).remainder == 0 { + return false + } + } + + /// Give an exact answer when we can. + if self < pseudoPrimes.last! { + for i in 0 ..< pseudoPrimes.count { + guard isStrongProbablePrime(CS.BigUInt(primes[i])) else { + break + } + if self < pseudoPrimes[i] { + // `self` is below the lowest pseudoprime corresponding to the prime bases we tested. It's a prime! + return true + } + } + return false + } + + /// Otherwise do as many rounds of random SPPT as required. + for _ in 0 ..< rounds { + let random = CS.BigUInt.randomInteger(lessThan: self - 2) + 2 + guard isStrongProbablePrime(random) else { + return false + } + } + + // Well, it smells primey to me. + return true + } +} + +extension CS.BigInt { + //MARK: Primality Testing + + /// Returns true iff this integer passes the [strong probable prime test][sppt] for the specified base. + /// + /// [sppt]: https://en.wikipedia.org/wiki/Probable_prime + internal func isStrongProbablePrime(_ base: CS.BigInt) -> Bool { + precondition(base.sign == .plus) + if self.sign == .minus { return false } + return self.magnitude.isStrongProbablePrime(base.magnitude) + } + + /// Returns true if this integer is probably prime. Returns false if this integer is definitely not prime. + /// + /// This function performs a probabilistic [Miller-Rabin Primality Test][mrpt], consisting of `rounds` iterations, + /// each calculating the strong probable prime test for a random base. The number of rounds is 10 by default, + /// but you may specify your own choice. + /// + /// To speed things up, the function checks if `self` is divisible by the first few prime numbers before + /// diving into (slower) Miller-Rabin testing. + /// + /// Also, when `self` is less than 82 bits wide, `isPrime` does a deterministic test that is guaranteed to + /// return a correct result. + /// + /// [mrpt]: https://en.wikipedia.org/wiki/Miller–Rabin_primality_test + internal func isPrime(rounds: Int = 10) -> Bool { + if self.sign == .minus { return false } + return self.magnitude.isPrime(rounds: rounds) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Random.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Random.swift new file mode 100644 index 0000000..c18900a --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Random.swift @@ -0,0 +1,101 @@ +// +// Random.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2016-01-04. +// Copyright © 2016-2017 Károly Lőrentey. +// + +extension CS.BigUInt { + /// Create a big unsigned integer consisting of `width` uniformly distributed random bits. + /// + /// - Parameter width: The maximum number of one bits in the result. + /// - Parameter generator: The source of randomness. + /// - Returns: A big unsigned integer less than `1 << width`. + internal static func randomInteger(withMaximumWidth width: Int, using generator: inout RNG) -> CS.BigUInt { + var result = CS.BigUInt.zero + var bitsLeft = width + var i = 0 + let wordsNeeded = (width + Word.bitWidth - 1) / Word.bitWidth + if wordsNeeded > 2 { + result.reserveCapacity(wordsNeeded) + } + while bitsLeft >= Word.bitWidth { + result[i] = generator.next() + i += 1 + bitsLeft -= Word.bitWidth + } + if bitsLeft > 0 { + let mask: Word = (1 << bitsLeft) - 1 + result[i] = (generator.next() as Word) & mask + } + return result + } + + /// Create a big unsigned integer consisting of `width` uniformly distributed random bits. + /// + /// - Note: I use a `SystemRandomGeneratorGenerator` as the source of randomness. + /// + /// - Parameter width: The maximum number of one bits in the result. + /// - Returns: A big unsigned integer less than `1 << width`. + internal static func randomInteger(withMaximumWidth width: Int) -> CS.BigUInt { + var rng = SystemRandomNumberGenerator() + return randomInteger(withMaximumWidth: width, using: &rng) + } + + /// Create a big unsigned integer consisting of `width-1` uniformly distributed random bits followed by a one bit. + /// + /// - Note: If `width` is zero, the result is zero. + /// + /// - Parameter width: The number of bits required to represent the answer. + /// - Parameter generator: The source of randomness. + /// - Returns: A random big unsigned integer whose width is `width`. + internal static func randomInteger(withExactWidth width: Int, using generator: inout RNG) -> CS.BigUInt { + // width == 0 -> return 0 because there is no room for a one bit. + // width == 1 -> return 1 because there is no room for any random bits. + guard width > 1 else { return CS.BigUInt(width) } + var result = randomInteger(withMaximumWidth: width - 1, using: &generator) + result[(width - 1) / Word.bitWidth] |= 1 << Word((width - 1) % Word.bitWidth) + return result + } + + /// Create a big unsigned integer consisting of `width-1` uniformly distributed random bits followed by a one bit. + /// + /// - Note: If `width` is zero, the result is zero. + /// - Note: I use a `SystemRandomGeneratorGenerator` as the source of randomness. + /// + /// - Returns: A random big unsigned integer whose width is `width`. + internal static func randomInteger(withExactWidth width: Int) -> CS.BigUInt { + var rng = SystemRandomNumberGenerator() + return randomInteger(withExactWidth: width, using: &rng) + } + + /// Create a uniformly distributed random unsigned integer that's less than the specified limit. + /// + /// - Precondition: `limit > 0`. + /// + /// - Parameter limit: The upper bound on the result. + /// - Parameter generator: The source of randomness. + /// - Returns: A random big unsigned integer that is less than `limit`. + internal static func randomInteger(lessThan limit: CS.BigUInt, using generator: inout RNG) -> CS.BigUInt { + precondition(limit > 0, "\(#function): 0 is not a valid limit") + let width = limit.bitWidth + var random = randomInteger(withMaximumWidth: width, using: &generator) + while random >= limit { + random = randomInteger(withMaximumWidth: width, using: &generator) + } + return random + } + + /// Create a uniformly distributed random unsigned integer that's less than the specified limit. + /// + /// - Precondition: `limit > 0`. + /// - Note: I use a `SystemRandomGeneratorGenerator` as the source of randomness. + /// + /// - Parameter limit: The upper bound on the result. + /// - Returns: A random big unsigned integer that is less than `limit`. + internal static func randomInteger(lessThan limit: CS.BigUInt) -> CS.BigUInt { + var rng = SystemRandomNumberGenerator() + return randomInteger(lessThan: limit, using: &rng) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Shifts.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Shifts.swift new file mode 100644 index 0000000..fd5279d --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Shifts.swift @@ -0,0 +1,211 @@ +// +// Shifts.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2016-01-03. +// Copyright © 2016-2017 Károly Lőrentey. +// + +extension CS.BigUInt { + + //MARK: Shift Operators + + internal func shiftedLeft(by amount: Word) -> CS.BigUInt { + guard amount > 0 else { return self } + + let ext = Int(amount / Word(Word.bitWidth)) // External shift amount (new words) + let up = Word(amount % Word(Word.bitWidth)) // Internal shift amount (subword shift) + let down = Word(Word.bitWidth) - up + + var result = CS.BigUInt() + if up > 0 { + var i = 0 + var lowbits: Word = 0 + while i < self.count || lowbits > 0 { + let word = self[i] + result[i + ext] = word << up | lowbits + lowbits = word >> down + i += 1 + } + } + else { + for i in 0 ..< self.count { + result[i + ext] = self[i] + } + } + return result + } + + internal mutating func shiftLeft(by amount: Word) { + guard amount > 0 else { return } + + let ext = Int(amount / Word(Word.bitWidth)) // External shift amount (new words) + let up = Word(amount % Word(Word.bitWidth)) // Internal shift amount (subword shift) + let down = Word(Word.bitWidth) - up + + if up > 0 { + var i = 0 + var lowbits: Word = 0 + while i < self.count || lowbits > 0 { + let word = self[i] + self[i] = word << up | lowbits + lowbits = word >> down + i += 1 + } + } + if ext > 0 && self.count > 0 { + self.shiftLeft(byWords: ext) + } + } + + internal func shiftedRight(by amount: Word) -> CS.BigUInt { + guard amount > 0 else { return self } + guard amount < self.bitWidth else { return 0 } + + let ext = Int(amount / Word(Word.bitWidth)) // External shift amount (new words) + let down = Word(amount % Word(Word.bitWidth)) // Internal shift amount (subword shift) + let up = Word(Word.bitWidth) - down + + var result = CS.BigUInt() + if down > 0 { + var highbits: Word = 0 + for i in (ext ..< self.count).reversed() { + let word = self[i] + result[i - ext] = highbits | word >> down + highbits = word << up + } + } + else { + for i in (ext ..< self.count).reversed() { + result[i - ext] = self[i] + } + } + return result + } + + internal mutating func shiftRight(by amount: Word) { + guard amount > 0 else { return } + guard amount < self.bitWidth else { self.clear(); return } + + let ext = Int(amount / Word(Word.bitWidth)) // External shift amount (new words) + let down = Word(amount % Word(Word.bitWidth)) // Internal shift amount (subword shift) + let up = Word(Word.bitWidth) - down + + if ext > 0 { + self.shiftRight(byWords: ext) + } + if down > 0 { + var i = self.count - 1 + var highbits: Word = 0 + while i >= 0 { + let word = self[i] + self[i] = highbits | word >> down + highbits = word << up + i -= 1 + } + } + } + + internal static func >>=(lhs: inout CS.BigUInt, rhs: Other) { + if rhs < (0 as Other) { + lhs <<= (0 - rhs) + } + else if rhs >= lhs.bitWidth { + lhs.clear() + } + else { + lhs.shiftRight(by: UInt(rhs)) + } + } + + internal static func <<=(lhs: inout CS.BigUInt, rhs: Other) { + if rhs < (0 as Other) { + lhs >>= (0 - rhs) + return + } + lhs.shiftLeft(by: Word(exactly: rhs)!) + } + + internal static func >>(lhs: CS.BigUInt, rhs: Other) -> CS.BigUInt { + if rhs < (0 as Other) { + return lhs << (0 - rhs) + } + if rhs > Word.max { + return 0 + } + return lhs.shiftedRight(by: UInt(rhs)) + } + + internal static func <<(lhs: CS.BigUInt, rhs: Other) -> CS.BigUInt { + if rhs < (0 as Other) { + return lhs >> (0 - rhs) + } + return lhs.shiftedLeft(by: Word(exactly: rhs)!) + } +} + +extension CS.BigInt { + func shiftedLeft(by amount: Word) -> CS.BigInt { + return CS.BigInt(sign: self.sign, magnitude: self.magnitude.shiftedLeft(by: amount)) + } + + mutating func shiftLeft(by amount: Word) { + self.magnitude.shiftLeft(by: amount) + } + + func shiftedRight(by amount: Word) -> CS.BigInt { + let m = self.magnitude.shiftedRight(by: amount) + return CS.BigInt(sign: self.sign, magnitude: self.sign == .minus && m.isZero ? 1 : m) + } + + mutating func shiftRight(by amount: Word) { + magnitude.shiftRight(by: amount) + if sign == .minus, magnitude.isZero { + magnitude.load(1) + } + } + + internal static func &<<(left: CS.BigInt, right: CS.BigInt) -> CS.BigInt { + return left.shiftedLeft(by: right.words[0]) + } + + internal static func &<<=(left: inout CS.BigInt, right: CS.BigInt) { + left.shiftLeft(by: right.words[0]) + } + + internal static func &>>(left: CS.BigInt, right: CS.BigInt) -> CS.BigInt { + return left.shiftedRight(by: right.words[0]) + } + + internal static func &>>=(left: inout CS.BigInt, right: CS.BigInt) { + left.shiftRight(by: right.words[0]) + } + + internal static func <<(lhs: CS.BigInt, rhs: Other) -> CS.BigInt { + guard rhs >= (0 as Other) else { return lhs >> (0 - rhs) } + return lhs.shiftedLeft(by: Word(rhs)) + } + + internal static func <<=(lhs: inout CS.BigInt, rhs: Other) { + if rhs < (0 as Other) { + lhs >>= (0 - rhs) + } + else { + lhs.shiftLeft(by: Word(rhs)) + } + } + + internal static func >>(lhs: CS.BigInt, rhs: Other) -> CS.BigInt { + guard rhs >= (0 as Other) else { return lhs << (0 - rhs) } + return lhs.shiftedRight(by: Word(rhs)) + } + + internal static func >>=(lhs: inout CS.BigInt, rhs: Other) { + if rhs < (0 as Other) { + lhs <<= (0 - rhs) + } + else { + lhs.shiftRight(by: Word(rhs)) + } + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/SquareRoot.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/SquareRoot.swift new file mode 100644 index 0000000..68cff6c --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/SquareRoot.swift @@ -0,0 +1,41 @@ +// +// SquareRoot.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2016-01-03. +// Copyright © 2016-2017 Károly Lőrentey. +// + +//MARK: Square Root + +extension CS.BigUInt { + /// Returns the integer square root of a big integer; i.e., the largest integer whose square isn't greater than `value`. + /// + /// - Returns: floor(sqrt(self)) + internal func squareRoot() -> CS.BigUInt { + // This implementation uses Newton's method. + guard !self.isZero else { return CS.BigUInt() } + var x = CS.BigUInt(1) << ((self.bitWidth + 1) / 2) + var y: CS.BigUInt = 0 + while true { + y.load(self) + y /= x + y += x + y >>= 1 + if x == y || x == y - 1 { break } + x = y + } + return x + } +} + +extension CS.BigInt { + /// Returns the integer square root of a big integer; i.e., the largest integer whose square isn't greater than `value`. + /// + /// - Requires: self >= 0 + /// - Returns: floor(sqrt(self)) + internal func squareRoot() -> CS.BigInt { + precondition(self.sign == .plus) + return CS.BigInt(sign: .plus, magnitude: self.magnitude.squareRoot()) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Strideable.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Strideable.swift new file mode 100644 index 0000000..9a554d4 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Strideable.swift @@ -0,0 +1,38 @@ +// +// Strideable.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2017-08-11. +// Copyright © 2016-2017 Károly Lőrentey. +// + +extension CS.BigUInt: Strideable { + /// A type that can represent the distance between two values ofa `BigUInt`. + internal typealias Stride = CS.BigInt + + /// Adds `n` to `self` and returns the result. Traps if the result would be less than zero. + internal func advanced(by n: CS.BigInt) -> CS.BigUInt { + return n.sign == .minus ? self - n.magnitude : self + n.magnitude + } + + /// Returns the (potentially negative) difference between `self` and `other` as a `BigInt`. Never traps. + internal func distance(to other: CS.BigUInt) -> CS.BigInt { + return CS.BigInt(other) - CS.BigInt(self) + } +} + +extension CS.BigInt: Strideable { + internal typealias Stride = CS.BigInt + + /// Returns `self + n`. + internal func advanced(by n: Stride) -> CS.BigInt { + return self + n + } + + /// Returns `other - self`. + internal func distance(to other: CS.BigInt) -> Stride { + return other - self + } +} + + diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/StringConversion.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/StringConversion.swift new file mode 100644 index 0000000..1e75b31 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/StringConversion.swift @@ -0,0 +1,240 @@ +// +// StringConversion.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2016-01-03. +// Copyright © 2016-2017 Károly Lőrentey. +// + +extension CS.BigUInt { + + //MARK: String Conversion + + /// Calculates the number of numerals in a given radix that fit inside a single `Word`. + /// + /// - Returns: (chars, power) where `chars` is highest that satisfy `radix^chars <= 2^Word.bitWidth`. `power` is zero + /// if radix is a power of two; otherwise `power == radix^chars`. + fileprivate static func charsPerWord(forRadix radix: Int) -> (chars: Int, power: Word) { + var power: Word = 1 + var overflow = false + var count = 0 + while !overflow { + let (high,low) = power.multipliedFullWidth(by: Word(radix)) + if high > 0 { + overflow = true + } + + if !overflow || (high == 1 && low == 0) { + count += 1 + power = low + } + } + return (count, power) + } + + /// Initialize a big integer from an ASCII representation in a given radix. Numerals above `9` are represented by + /// letters from the English alphabet. + /// + /// - Requires: `radix > 1 && radix < 36` + /// - Parameter `text`: A string consisting of characters corresponding to numerals in the given radix. (0-9, a-z, A-Z) + /// - Parameter `radix`: The base of the number system to use, or 10 if unspecified. + /// - Returns: The integer represented by `text`, or nil if `text` contains a character that does not represent a numeral in `radix`. + internal init?(_ text: S, radix: Int = 10) { + precondition(radix > 1 && radix < 36) + guard !text.isEmpty else { return nil } + let (charsPerWord, power) = CS.BigUInt.charsPerWord(forRadix: radix) + + var words: [Word] = [] + var end = text.endIndex + var start = end + var count = 0 + while start != text.startIndex { + start = text.index(before: start) + count += 1 + if count == charsPerWord { + guard let d = Word.init(text[start ..< end], radix: radix) else { return nil } + words.append(d) + end = start + count = 0 + } + } + if start != end { + guard let d = Word.init(text[start ..< end], radix: radix) else { return nil } + words.append(d) + } + + if power == 0 { + self.init(words: words) + } + else { + self.init() + for d in words.reversed() { + self.multiply(byWord: power) + self.addWord(d) + } + } + } +} + +extension CS.BigInt { + /// Initialize a big integer from an ASCII representation in a given radix. Numerals above `9` are represented by + /// letters from the English alphabet. + /// + /// - Requires: `radix > 1 && radix < 36` + /// - Parameter `text`: A string optionally starting with "-" or "+" followed by characters corresponding to numerals in the given radix. (0-9, a-z, A-Z) + /// - Parameter `radix`: The base of the number system to use, or 10 if unspecified. + /// - Returns: The integer represented by `text`, or nil if `text` contains a character that does not represent a numeral in `radix`. + internal init?(_ text: S, radix: Int = 10) { + var magnitude: CS.BigUInt? + var sign: Sign = .plus + if text.first == "-" { + sign = .minus + let text = text.dropFirst() + magnitude = CS.BigUInt(text, radix: radix) + } + else if text.first == "+" { + let text = text.dropFirst() + magnitude = CS.BigUInt(text, radix: radix) + } + else { + magnitude = CS.BigUInt(text, radix: radix) + } + guard let m = magnitude else { return nil } + self.magnitude = m + self.sign = m.isZero ? .plus : sign + } +} + +extension String { + /// Initialize a new string with the base-10 representation of an unsigned big integer. + /// + /// - Complexity: O(v.count^2) + internal init(_ v: CS.BigUInt) { self.init(v, radix: 10, uppercase: false) } + + /// Initialize a new string representing an unsigned big integer in the given radix (base). + /// + /// Numerals greater than 9 are represented as letters from the English alphabet, + /// starting with `a` if `uppercase` is false or `A` otherwise. + /// + /// - Requires: radix > 1 && radix <= 36 + /// - Complexity: O(count) when radix is a power of two; otherwise O(count^2). + internal init(_ v: CS.BigUInt, radix: Int, uppercase: Bool = false) { + precondition(radix > 1) + let (charsPerWord, power) = CS.BigUInt.charsPerWord(forRadix: radix) + + guard !v.isZero else { self = "0"; return } + + var parts: [String] + if power == 0 { + parts = v.words.map { String($0, radix: radix, uppercase: uppercase) } + } + else { + parts = [] + var rest = v + while !rest.isZero { + let mod = rest.divide(byWord: power) + parts.append(String(mod, radix: radix, uppercase: uppercase)) + } + } + assert(!parts.isEmpty) + + self = "" + var first = true + for part in parts.reversed() { + let zeroes = charsPerWord - part.count + assert(zeroes >= 0) + if !first && zeroes > 0 { + // Insert leading zeroes for mid-Words + self += String(repeating: "0", count: zeroes) + } + first = false + self += part + } + } + + /// Initialize a new string representing a signed big integer in the given radix (base). + /// + /// Numerals greater than 9 are represented as letters from the English alphabet, + /// starting with `a` if `uppercase` is false or `A` otherwise. + /// + /// - Requires: radix > 1 && radix <= 36 + /// - Complexity: O(count) when radix is a power of two; otherwise O(count^2). + internal init(_ value: CS.BigInt, radix: Int = 10, uppercase: Bool = false) { + self = String(value.magnitude, radix: radix, uppercase: uppercase) + if value.sign == .minus { + self = "-" + self + } + } +} + +extension CS.BigUInt: ExpressibleByStringLiteral { + /// Initialize a new big integer from a Unicode scalar. + /// The scalar must represent a decimal digit. + internal init(unicodeScalarLiteral value: UnicodeScalar) { + self = CS.BigUInt(String(value), radix: 10)! + } + + /// Initialize a new big integer from an extended grapheme cluster. + /// The cluster must consist of a decimal digit. + internal init(extendedGraphemeClusterLiteral value: String) { + self = CS.BigUInt(value, radix: 10)! + } + + /// Initialize a new big integer from a decimal number represented by a string literal of arbitrary length. + /// The string must contain only decimal digits. + internal init(stringLiteral value: StringLiteralType) { + self = CS.BigUInt(value, radix: 10)! + } +} + +extension CS.BigInt: ExpressibleByStringLiteral { + /// Initialize a new big integer from a Unicode scalar. + /// The scalar must represent a decimal digit. + internal init(unicodeScalarLiteral value: UnicodeScalar) { + self = CS.BigInt(String(value), radix: 10)! + } + + /// Initialize a new big integer from an extended grapheme cluster. + /// The cluster must consist of a decimal digit. + internal init(extendedGraphemeClusterLiteral value: String) { + self = CS.BigInt(value, radix: 10)! + } + + /// Initialize a new big integer from a decimal number represented by a string literal of arbitrary length. + /// The string must contain only decimal digits. + internal init(stringLiteral value: StringLiteralType) { + self = CS.BigInt(value, radix: 10)! + } +} + +extension CS.BigUInt: CustomStringConvertible { + /// Return the decimal representation of this integer. + internal var description: String { + return String(self, radix: 10) + } +} + +extension CS.BigInt: CustomStringConvertible { + /// Return the decimal representation of this integer. + internal var description: String { + return String(self, radix: 10) + } +} + +extension CS.BigUInt: CustomPlaygroundDisplayConvertible { + + /// Return the playground quick look representation of this integer. + internal var playgroundDescription: Any { + let text = String(self) + return text + " (\(self.bitWidth) bits)" + } +} + +extension CS.BigInt: CustomPlaygroundDisplayConvertible { + + /// Return the playground quick look representation of this integer. + internal var playgroundDescription: Any { + let text = String(self) + return text + " (\(self.magnitude.bitWidth) bits)" + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Subtraction.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Subtraction.swift new file mode 100644 index 0000000..68e44a1 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/Subtraction.swift @@ -0,0 +1,169 @@ +// +// Subtraction.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2016-01-03. +// Copyright © 2016-2017 Károly Lőrentey. +// + +extension CS.BigUInt { + //MARK: Subtraction + + /// Subtract `word` from this integer in place, returning a flag indicating if the operation + /// caused an arithmetic overflow. `word` is shifted `shift` words to the left before being subtracted. + /// + /// - Note: If the result indicates an overflow, then `self` becomes the two's complement of the absolute difference. + /// - Complexity: O(count) + internal mutating func subtractWordReportingOverflow(_ word: Word, shiftedBy shift: Int = 0) -> Bool { + precondition(shift >= 0) + var carry: Word = word + var i = shift + let count = self.count + while carry > 0 && i < count { + let (d, c) = self[i].subtractingReportingOverflow(carry) + self[i] = d + carry = (c ? 1 : 0) + i += 1 + } + return carry > 0 + } + + /// Subtract `word` from this integer, returning the difference and a flag that is true if the operation + /// caused an arithmetic overflow. `word` is shifted `shift` words to the left before being subtracted. + /// + /// - Note: If `overflow` is true, then the returned value is the two's complement of the absolute difference. + /// - Complexity: O(count) + internal func subtractingWordReportingOverflow(_ word: Word, shiftedBy shift: Int = 0) -> (partialValue: CS.BigUInt, overflow: Bool) { + var result = self + let overflow = result.subtractWordReportingOverflow(word, shiftedBy: shift) + return (result, overflow) + } + + /// Subtract a digit `d` from this integer in place. + /// `d` is shifted `shift` digits to the left before being subtracted. + /// + /// - Requires: self >= d * 2^shift + /// - Complexity: O(count) + internal mutating func subtractWord(_ word: Word, shiftedBy shift: Int = 0) { + let overflow = subtractWordReportingOverflow(word, shiftedBy: shift) + precondition(!overflow) + } + + /// Subtract a digit `d` from this integer and return the result. + /// `d` is shifted `shift` digits to the left before being subtracted. + /// + /// - Requires: self >= d * 2^shift + /// - Complexity: O(count) + internal func subtractingWord(_ word: Word, shiftedBy shift: Int = 0) -> CS.BigUInt { + var result = self + result.subtractWord(word, shiftedBy: shift) + return result + } + + /// Subtract `other` from this integer in place, and return a flag indicating if the operation caused an + /// arithmetic overflow. `other` is shifted `shift` digits to the left before being subtracted. + /// + /// - Note: If the result indicates an overflow, then `self` becomes the twos' complement of the absolute difference. + /// - Complexity: O(count) + internal mutating func subtractReportingOverflow(_ b: CS.BigUInt, shiftedBy shift: Int = 0) -> Bool { + precondition(shift >= 0) + var carry = false + var bi = 0 + let bc = b.count + let count = self.count + while bi < bc || (shift + bi < count && carry) { + let ai = shift + bi + let (d, c) = self[ai].subtractingReportingOverflow(b[bi]) + if carry { + let (d2, c2) = d.subtractingReportingOverflow(1) + self[ai] = d2 + carry = c || c2 + } + else { + self[ai] = d + carry = c + } + bi += 1 + } + return carry + } + + /// Subtract `other` from this integer, returning the difference and a flag indicating arithmetic overflow. + /// `other` is shifted `shift` digits to the left before being subtracted. + /// + /// - Note: If `overflow` is true, then the result value is the twos' complement of the absolute value of the difference. + /// - Complexity: O(count) + internal func subtractingReportingOverflow(_ other: CS.BigUInt, shiftedBy shift: Int) -> (partialValue: CS.BigUInt, overflow: Bool) { + var result = self + let overflow = result.subtractReportingOverflow(other, shiftedBy: shift) + return (result, overflow) + } + + /// Subtracts `other` from `self`, returning the result and a flag indicating arithmetic overflow. + /// + /// - Note: When the operation overflows, then `partialValue` is the twos' complement of the absolute value of the difference. + /// - Complexity: O(count) + internal func subtractingReportingOverflow(_ other: CS.BigUInt) -> (partialValue: CS.BigUInt, overflow: Bool) { + return self.subtractingReportingOverflow(other, shiftedBy: 0) + } + + /// Subtract `other` from this integer in place. + /// `other` is shifted `shift` digits to the left before being subtracted. + /// + /// - Requires: self >= other * 2^shift + /// - Complexity: O(count) + internal mutating func subtract(_ other: CS.BigUInt, shiftedBy shift: Int = 0) { + let overflow = subtractReportingOverflow(other, shiftedBy: shift) + precondition(!overflow) + } + + /// Subtract `b` from this integer, and return the difference. + /// `b` is shifted `shift` digits to the left before being subtracted. + /// + /// - Requires: self >= b * 2^shift + /// - Complexity: O(count) + internal func subtracting(_ other: CS.BigUInt, shiftedBy shift: Int = 0) -> CS.BigUInt { + var result = self + result.subtract(other, shiftedBy: shift) + return result + } + + /// Decrement this integer by one. + /// + /// - Requires: !isZero + /// - Complexity: O(count) + internal mutating func decrement(shiftedBy shift: Int = 0) { + self.subtract(1, shiftedBy: shift) + } + + /// Subtract `b` from `a` and return the result. + /// + /// - Requires: a >= b + /// - Complexity: O(a.count) + internal static func -(a: CS.BigUInt, b: CS.BigUInt) -> CS.BigUInt { + return a.subtracting(b) + } + + /// Subtract `b` from `a` and store the result in `a`. + /// + /// - Requires: a >= b + /// - Complexity: O(a.count) + internal static func -=(a: inout CS.BigUInt, b: CS.BigUInt) { + a.subtract(b) + } +} + +extension CS.BigInt { + internal mutating func negate() { + guard !magnitude.isZero else { return } + self.sign = self.sign == .plus ? .minus : .plus + } + + /// Subtract `b` from `a` and return the result. + internal static func -(a: CS.BigInt, b: CS.BigInt) -> CS.BigInt { + return a + -b + } + + /// Subtract `b` from `a` in place. + internal static func -=(a: inout CS.BigInt, b: CS.BigInt) { a = a - b } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/WordsAndBits.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/WordsAndBits.swift new file mode 100644 index 0000000..dea42b3 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CS_BigInt/WordsAndBits.swift @@ -0,0 +1,202 @@ +// +// WordsAndBits.swift +// CS.BigInt +// +// Created by Károly Lőrentey on 2017-08-11. +// Copyright © 2016-2017 Károly Lőrentey. +// + +extension Array where Element == UInt { + mutating func twosComplement() { + var increment = true + for i in 0 ..< self.count { + if increment { + (self[i], increment) = (~self[i]).addingReportingOverflow(1) + } + else { + self[i] = ~self[i] + } + } + } +} + +extension CS.BigUInt { + internal subscript(bitAt index: Int) -> Bool { + get { + precondition(index >= 0) + let (i, j) = index.quotientAndRemainder(dividingBy: Word.bitWidth) + return self[i] & (1 << j) != 0 + } + set { + precondition(index >= 0) + let (i, j) = index.quotientAndRemainder(dividingBy: Word.bitWidth) + if newValue { + self[i] |= 1 << j + } + else { + self[i] &= ~(1 << j) + } + } + } +} + +extension CS.BigUInt { + /// The minimum number of bits required to represent this integer in binary. + /// + /// - Returns: floor(log2(2 * self + 1)) + /// - Complexity: O(1) + internal var bitWidth: Int { + guard count > 0 else { return 0 } + return count * Word.bitWidth - self[count - 1].leadingZeroBitCount + } + + /// The number of leading zero bits in the binary representation of this integer in base `2^(Word.bitWidth)`. + /// This is useful when you need to normalize a `BigUInt` such that the top bit of its most significant word is 1. + /// + /// - Note: 0 is considered to have zero leading zero bits. + /// - Returns: A value in `0...(Word.bitWidth - 1)`. + /// - SeeAlso: width + /// - Complexity: O(1) + internal var leadingZeroBitCount: Int { + guard count > 0 else { return 0 } + return self[count - 1].leadingZeroBitCount + } + + /// The number of trailing zero bits in the binary representation of this integer. + /// + /// - Note: 0 is considered to have zero trailing zero bits. + /// - Returns: A value in `0...width`. + /// - Complexity: O(count) + internal var trailingZeroBitCount: Int { + guard count > 0 else { return 0 } + let i = self.words.firstIndex { $0 != 0 }! + return i * Word.bitWidth + self[i].trailingZeroBitCount + } +} + +extension CS.BigInt { + internal var bitWidth: Int { + guard !magnitude.isZero else { return 0 } + return magnitude.bitWidth + 1 + } + + internal var trailingZeroBitCount: Int { + // Amazingly, this works fine for negative numbers + return magnitude.trailingZeroBitCount + } +} + +extension CS.BigUInt { + internal struct Words: RandomAccessCollection { + private let value: CS.BigUInt + + fileprivate init(_ value: CS.BigUInt) { self.value = value } + + internal var startIndex: Int { return 0 } + internal var endIndex: Int { return value.count } + + internal subscript(_ index: Int) -> Word { + return value[index] + } + } + + internal var words: Words { return Words(self) } + + internal init(words: Words) where Words.Element == Word { + let uc = words.underestimatedCount + if uc > 2 { + self.init(words: Array(words)) + } + else { + var it = words.makeIterator() + guard let w0 = it.next() else { + self.init() + return + } + guard let w1 = it.next() else { + self.init(word: w0) + return + } + if let w2 = it.next() { + var words: [UInt] = [] + words.reserveCapacity(Swift.max(3, uc)) + words.append(w0) + words.append(w1) + words.append(w2) + while let word = it.next() { + words.append(word) + } + self.init(words: words) + } + else { + self.init(low: w0, high: w1) + } + } + } +} + +extension CS.BigInt { + internal struct Words: RandomAccessCollection { + internal typealias Indices = CountableRange + + private let value: CS.BigInt + private let decrementLimit: Int + + fileprivate init(_ value: CS.BigInt) { + self.value = value + switch value.sign { + case .plus: + self.decrementLimit = 0 + case .minus: + assert(!value.magnitude.isZero) + self.decrementLimit = value.magnitude.words.firstIndex(where: { $0 != 0 })! + } + } + + internal var count: Int { + switch value.sign { + case .plus: + if let high = value.magnitude.words.last, high >> (Word.bitWidth - 1) != 0 { + return value.magnitude.count + 1 + } + return value.magnitude.count + case .minus: + let high = value.magnitude.words.last! + if high >> (Word.bitWidth - 1) != 0 { + return value.magnitude.count + 1 + } + return value.magnitude.count + } + } + + internal var indices: Indices { return 0 ..< count } + internal var startIndex: Int { return 0 } + internal var endIndex: Int { return count } + + internal subscript(_ index: Int) -> UInt { + // Note that indices above `endIndex` are accepted. + if value.sign == .plus { + return value.magnitude[index] + } + if index <= decrementLimit { + return ~(value.magnitude[index] &- 1) + } + return ~value.magnitude[index] + } + } + + internal var words: Words { + return Words(self) + } + + internal init(words: S) where S.Element == Word { + var words = Array(words) + if (words.last ?? 0) >> (Word.bitWidth - 1) == 0 { + self.init(sign: .plus, magnitude: CS.BigUInt(words: words)) + } + else { + words.twosComplement() + self.init(sign: .minus, magnitude: CS.BigUInt(words: words)) + } + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/ChaCha20.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/ChaCha20.swift new file mode 100644 index 0000000..e9f2ccd --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/ChaCha20.swift @@ -0,0 +1,351 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// https://tools.ietf.org/html/rfc7539 +// + +internal final class ChaCha20: BlockCipher { + internal enum Error: Swift.Error { + case invalidKeyOrInitializationVector + case notSupported + } + + internal static let blockSize = 64 // 512 / 8 + internal let keySize: Int + + fileprivate let key: Key + fileprivate var counter: Array + + internal convenience init(key: Array, iv nonce: Array) throws { + try self.init(key: key, iv: nonce, blockCounter: 0) + } + + init(key: Array, iv nonce: Array, blockCounter: UInt32 = 0) throws { + precondition(nonce.count == 12 || nonce.count == 8) + + if key.count != 32 { + throw Error.invalidKeyOrInitializationVector + } + + self.key = Key(bytes: key) + self.keySize = self.key.count + + if nonce.count == 8 { + self.counter = blockCounter.bigEndian.bytes() + [0, 0, 0, 0] + nonce + } else { + self.counter = blockCounter.bigEndian.bytes() + nonce + } + + assert(self.counter.count == 16) + } + + /// https://tools.ietf.org/html/rfc7539#section-2.3. + fileprivate func core(block: inout Array, counter: Array, key: Array) { + precondition(block.count == ChaCha20.blockSize) + precondition(counter.count == 16) + precondition(key.count == 32) + + let j0: UInt32 = 0x61707865 + let j1: UInt32 = 0x3320646e // 0x3620646e sigma/tau + let j2: UInt32 = 0x79622d32 + let j3: UInt32 = 0x6b206574 + let j4: UInt32 = UInt32(bytes: key[0..<4]).bigEndian + let j5: UInt32 = UInt32(bytes: key[4..<8]).bigEndian + let j6: UInt32 = UInt32(bytes: key[8..<12]).bigEndian + let j7: UInt32 = UInt32(bytes: key[12..<16]).bigEndian + let j8: UInt32 = UInt32(bytes: key[16..<20]).bigEndian + let j9: UInt32 = UInt32(bytes: key[20..<24]).bigEndian + let j10: UInt32 = UInt32(bytes: key[24..<28]).bigEndian + let j11: UInt32 = UInt32(bytes: key[28..<32]).bigEndian + let j12: UInt32 = UInt32(bytes: counter[0..<4]).bigEndian + let j13: UInt32 = UInt32(bytes: counter[4..<8]).bigEndian + let j14: UInt32 = UInt32(bytes: counter[8..<12]).bigEndian + let j15: UInt32 = UInt32(bytes: counter[12..<16]).bigEndian + + var (x0, x1, x2, x3, x4, x5, x6, x7) = (j0, j1, j2, j3, j4, j5, j6, j7) + var (x8, x9, x10, x11, x12, x13, x14, x15) = (j8, j9, j10, j11, j12, j13, j14, j15) + + for _ in 0..<10 { // 20 rounds + x0 = x0 &+ x4 + x12 ^= x0 + x12 = (x12 << 16) | (x12 >> 16) + x8 = x8 &+ x12 + x4 ^= x8 + x4 = (x4 << 12) | (x4 >> 20) + x0 = x0 &+ x4 + x12 ^= x0 + x12 = (x12 << 8) | (x12 >> 24) + x8 = x8 &+ x12 + x4 ^= x8 + x4 = (x4 << 7) | (x4 >> 25) + x1 = x1 &+ x5 + x13 ^= x1 + x13 = (x13 << 16) | (x13 >> 16) + x9 = x9 &+ x13 + x5 ^= x9 + x5 = (x5 << 12) | (x5 >> 20) + x1 = x1 &+ x5 + x13 ^= x1 + x13 = (x13 << 8) | (x13 >> 24) + x9 = x9 &+ x13 + x5 ^= x9 + x5 = (x5 << 7) | (x5 >> 25) + x2 = x2 &+ x6 + x14 ^= x2 + x14 = (x14 << 16) | (x14 >> 16) + x10 = x10 &+ x14 + x6 ^= x10 + x6 = (x6 << 12) | (x6 >> 20) + x2 = x2 &+ x6 + x14 ^= x2 + x14 = (x14 << 8) | (x14 >> 24) + x10 = x10 &+ x14 + x6 ^= x10 + x6 = (x6 << 7) | (x6 >> 25) + x3 = x3 &+ x7 + x15 ^= x3 + x15 = (x15 << 16) | (x15 >> 16) + x11 = x11 &+ x15 + x7 ^= x11 + x7 = (x7 << 12) | (x7 >> 20) + x3 = x3 &+ x7 + x15 ^= x3 + x15 = (x15 << 8) | (x15 >> 24) + x11 = x11 &+ x15 + x7 ^= x11 + x7 = (x7 << 7) | (x7 >> 25) + x0 = x0 &+ x5 + x15 ^= x0 + x15 = (x15 << 16) | (x15 >> 16) + x10 = x10 &+ x15 + x5 ^= x10 + x5 = (x5 << 12) | (x5 >> 20) + x0 = x0 &+ x5 + x15 ^= x0 + x15 = (x15 << 8) | (x15 >> 24) + x10 = x10 &+ x15 + x5 ^= x10 + x5 = (x5 << 7) | (x5 >> 25) + x1 = x1 &+ x6 + x12 ^= x1 + x12 = (x12 << 16) | (x12 >> 16) + x11 = x11 &+ x12 + x6 ^= x11 + x6 = (x6 << 12) | (x6 >> 20) + x1 = x1 &+ x6 + x12 ^= x1 + x12 = (x12 << 8) | (x12 >> 24) + x11 = x11 &+ x12 + x6 ^= x11 + x6 = (x6 << 7) | (x6 >> 25) + x2 = x2 &+ x7 + x13 ^= x2 + x13 = (x13 << 16) | (x13 >> 16) + x8 = x8 &+ x13 + x7 ^= x8 + x7 = (x7 << 12) | (x7 >> 20) + x2 = x2 &+ x7 + x13 ^= x2 + x13 = (x13 << 8) | (x13 >> 24) + x8 = x8 &+ x13 + x7 ^= x8 + x7 = (x7 << 7) | (x7 >> 25) + x3 = x3 &+ x4 + x14 ^= x3 + x14 = (x14 << 16) | (x14 >> 16) + x9 = x9 &+ x14 + x4 ^= x9 + x4 = (x4 << 12) | (x4 >> 20) + x3 = x3 &+ x4 + x14 ^= x3 + x14 = (x14 << 8) | (x14 >> 24) + x9 = x9 &+ x14 + x4 ^= x9 + x4 = (x4 << 7) | (x4 >> 25) + } + + x0 = x0 &+ j0 + x1 = x1 &+ j1 + x2 = x2 &+ j2 + x3 = x3 &+ j3 + x4 = x4 &+ j4 + x5 = x5 &+ j5 + x6 = x6 &+ j6 + x7 = x7 &+ j7 + x8 = x8 &+ j8 + x9 = x9 &+ j9 + x10 = x10 &+ j10 + x11 = x11 &+ j11 + x12 = x12 &+ j12 + x13 = x13 &+ j13 + x14 = x14 &+ j14 + x15 = x15 &+ j15 + + block.replaceSubrange(0..<4, with: x0.bigEndian.bytes()) + block.replaceSubrange(4..<8, with: x1.bigEndian.bytes()) + block.replaceSubrange(8..<12, with: x2.bigEndian.bytes()) + block.replaceSubrange(12..<16, with: x3.bigEndian.bytes()) + block.replaceSubrange(16..<20, with: x4.bigEndian.bytes()) + block.replaceSubrange(20..<24, with: x5.bigEndian.bytes()) + block.replaceSubrange(24..<28, with: x6.bigEndian.bytes()) + block.replaceSubrange(28..<32, with: x7.bigEndian.bytes()) + block.replaceSubrange(32..<36, with: x8.bigEndian.bytes()) + block.replaceSubrange(36..<40, with: x9.bigEndian.bytes()) + block.replaceSubrange(40..<44, with: x10.bigEndian.bytes()) + block.replaceSubrange(44..<48, with: x11.bigEndian.bytes()) + block.replaceSubrange(48..<52, with: x12.bigEndian.bytes()) + block.replaceSubrange(52..<56, with: x13.bigEndian.bytes()) + block.replaceSubrange(56..<60, with: x14.bigEndian.bytes()) + block.replaceSubrange(60..<64, with: x15.bigEndian.bytes()) + } + + // XORKeyStream + func process(bytes: ArraySlice, counter: inout Array, key: Array) -> Array { + precondition(counter.count == 16) + precondition(key.count == 32) + + var block = Array(repeating: 0, count: ChaCha20.blockSize) + var bytesSlice = bytes + var out = Array(reserveCapacity: bytesSlice.count) + + while bytesSlice.count >= ChaCha20.blockSize { + self.core(block: &block, counter: counter, key: key) + for (i, x) in block.enumerated() { + out.append(bytesSlice[bytesSlice.startIndex + i] ^ x) + } + var u: UInt32 = 1 + for i in 0..<4 { + u += UInt32(counter[i]) + counter[i] = UInt8(u & 0xff) + u >>= 8 + } + bytesSlice = bytesSlice[bytesSlice.startIndex + ChaCha20.blockSize..) throws -> Array { + self.process(bytes: bytes, counter: &self.counter, key: Array(self.key)) + } + + internal func decrypt(_ bytes: ArraySlice) throws -> Array { + try self.encrypt(bytes) + } +} + +// MARK: Encryptor + +extension ChaCha20 { + internal struct ChaChaEncryptor: Cryptor, Updatable { + private var accumulated = Array() + private let chacha: ChaCha20 + + init(chacha: ChaCha20) { + self.chacha = chacha + } + + internal mutating func update(withBytes bytes: ArraySlice, isLast: Bool = false) throws -> Array { + self.accumulated += bytes + + var encrypted = Array() + encrypted.reserveCapacity(self.accumulated.count) + for chunk in self.accumulated.batched(by: ChaCha20.blockSize) { + if isLast || self.accumulated.count >= ChaCha20.blockSize { + encrypted += try self.chacha.encrypt(chunk) + self.accumulated.removeFirst(chunk.count) // TODO: improve performance + } + } + return encrypted + } + + internal func seek(to: Int) throws { + throw Error.notSupported + } + } +} + +// MARK: Decryptor + +extension ChaCha20 { + internal struct ChaChaDecryptor: Cryptor, Updatable { + private var accumulated = Array() + + private var offset: Int = 0 + private var offsetToRemove: Int = 0 + private let chacha: ChaCha20 + + init(chacha: ChaCha20) { + self.chacha = chacha + } + + internal mutating func update(withBytes bytes: ArraySlice, isLast: Bool = true) throws -> Array { + // prepend "offset" number of bytes at the beginning + if self.offset > 0 { + self.accumulated += Array(repeating: 0, count: self.offset) + bytes + self.offsetToRemove = self.offset + self.offset = 0 + } else { + self.accumulated += bytes + } + + var plaintext = Array() + plaintext.reserveCapacity(self.accumulated.count) + for chunk in self.accumulated.batched(by: ChaCha20.blockSize) { + if isLast || self.accumulated.count >= ChaCha20.blockSize { + plaintext += try self.chacha.decrypt(chunk) + + // remove "offset" from the beginning of first chunk + if self.offsetToRemove > 0 { + plaintext.removeFirst(self.offsetToRemove) // TODO: improve performance + self.offsetToRemove = 0 + } + + self.accumulated.removeFirst(chunk.count) + } + } + + return plaintext + } + + internal func seek(to: Int) throws { + throw Error.notSupported + } + } +} + +// MARK: Cryptors + +extension ChaCha20: Cryptors { + //TODO: Use BlockEncryptor/BlockDecryptor + + internal func makeEncryptor() -> Cryptor & Updatable { + ChaCha20.ChaChaEncryptor(chacha: self) + } + + internal func makeDecryptor() -> Cryptor & Updatable { + ChaCha20.ChaChaDecryptor(chacha: self) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Checksum.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Checksum.swift new file mode 100644 index 0000000..aa5e6ff --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Checksum.swift @@ -0,0 +1,208 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +/// CRC - cyclic redundancy check code. +internal final class Checksum { + + @usableFromInline + static let table32: Array = [ + 0x0000_0000, 0x7707_3096, 0xEE0E_612C, 0x9909_51BA, 0x076D_C419, 0x706A_F48F, 0xE963_A535, 0x9E64_95A3, + 0x0EDB_8832, 0x79DC_B8A4, 0xE0D5_E91E, 0x97D2_D988, 0x09B6_4C2B, 0x7EB1_7CBD, 0xE7B8_2D07, 0x90BF_1D91, + 0x1DB7_1064, 0x6AB0_20F2, 0xF3B9_7148, 0x84BE_41DE, 0x1ADA_D47D, 0x6DDD_E4EB, 0xF4D4_B551, 0x83D3_85C7, + 0x136C_9856, 0x646B_A8C0, 0xFD62_F97A, 0x8A65_C9EC, 0x1401_5C4F, 0x6306_6CD9, 0xFA0F_3D63, 0x8D08_0DF5, + 0x3B6E_20C8, 0x4C69_105E, 0xD560_41E4, 0xA267_7172, 0x3C03_E4D1, 0x4B04_D447, 0xD20D_85FD, 0xA50A_B56B, + 0x35B5_A8FA, 0x42B2_986C, 0xDBBB_C9D6, 0xACBC_F940, 0x32D8_6CE3, 0x45DF_5C75, 0xDCD6_0DCF, 0xABD1_3D59, + 0x26D9_30AC, 0x51DE_003A, 0xC8D7_5180, 0xBFD0_6116, 0x21B4_F4B5, 0x56B3_C423, 0xCFBA_9599, 0xB8BD_A50F, + 0x2802_B89E, 0x5F05_8808, 0xC60C_D9B2, 0xB10B_E924, 0x2F6F_7C87, 0x5868_4C11, 0xC161_1DAB, 0xB666_2D3D, + 0x76DC_4190, 0x01DB_7106, 0x98D2_20BC, 0xEFD5_102A, 0x71B1_8589, 0x06B6_B51F, 0x9FBF_E4A5, 0xE8B8_D433, + 0x7807_C9A2, 0x0F00_F934, 0x9609_A88E, 0xE10E_9818, 0x7F6A_0DBB, 0x086D_3D2D, 0x9164_6C97, 0xE663_5C01, + 0x6B6B_51F4, 0x1C6C_6162, 0x8565_30D8, 0xF262_004E, 0x6C06_95ED, 0x1B01_A57B, 0x8208_F4C1, 0xF50F_C457, + 0x65B0_D9C6, 0x12B7_E950, 0x8BBE_B8EA, 0xFCB9_887C, 0x62DD_1DDF, 0x15DA_2D49, 0x8CD3_7CF3, 0xFBD4_4C65, + 0x4DB2_6158, 0x3AB5_51CE, 0xA3BC_0074, 0xD4BB_30E2, 0x4ADF_A541, 0x3DD8_95D7, 0xA4D1_C46D, 0xD3D6_F4FB, + 0x4369_E96A, 0x346E_D9FC, 0xAD67_8846, 0xDA60_B8D0, 0x4404_2D73, 0x3303_1DE5, 0xAA0A_4C5F, 0xDD0D_7CC9, + 0x5005_713C, 0x2702_41AA, 0xBE0B_1010, 0xC90C_2086, 0x5768_B525, 0x206F_85B3, 0xB966_D409, 0xCE61_E49F, + 0x5EDE_F90E, 0x29D9_C998, 0xB0D0_9822, 0xC7D7_A8B4, 0x59B3_3D17, 0x2EB4_0D81, 0xB7BD_5C3B, 0xC0BA_6CAD, + 0xEDB8_8320, 0x9ABF_B3B6, 0x03B6_E20C, 0x74B1_D29A, 0xEAD5_4739, 0x9DD2_77AF, 0x04DB_2615, 0x73DC_1683, + 0xE363_0B12, 0x9464_3B84, 0x0D6D_6A3E, 0x7A6A_5AA8, 0xE40E_CF0B, 0x9309_FF9D, 0x0A00_AE27, 0x7D07_9EB1, + 0xF00F_9344, 0x8708_A3D2, 0x1E01_F268, 0x6906_C2FE, 0xF762_575D, 0x8065_67CB, 0x196C_3671, 0x6E6B_06E7, + 0xFED4_1B76, 0x89D3_2BE0, 0x10DA_7A5A, 0x67DD_4ACC, 0xF9B9_DF6F, 0x8EBE_EFF9, 0x17B7_BE43, 0x60B0_8ED5, + 0xD6D6_A3E8, 0xA1D1_937E, 0x38D8_C2C4, 0x4FDF_F252, 0xD1BB_67F1, 0xA6BC_5767, 0x3FB5_06DD, 0x48B2_364B, + 0xD80D_2BDA, 0xAF0A_1B4C, 0x3603_4AF6, 0x4104_7A60, 0xDF60_EFC3, 0xA867_DF55, 0x316E_8EEF, 0x4669_BE79, + 0xCB61_B38C, 0xBC66_831A, 0x256F_D2A0, 0x5268_E236, 0xCC0C_7795, 0xBB0B_4703, 0x2202_16B9, 0x5505_262F, + 0xC5BA_3BBE, 0xB2BD_0B28, 0x2BB4_5A92, 0x5CB3_6A04, 0xC2D7_FFA7, 0xB5D0_CF31, 0x2CD9_9E8B, 0x5BDE_AE1D, + 0x9B64_C2B0, 0xEC63_F226, 0x756A_A39C, 0x026D_930A, 0x9C09_06A9, 0xEB0E_363F, 0x7207_6785, 0x0500_5713, + 0x95BF_4A82, 0xE2B8_7A14, 0x7BB1_2BAE, 0x0CB6_1B38, 0x92D2_8E9B, 0xE5D5_BE0D, 0x7CDC_EFB7, 0x0BDB_DF21, + 0x86D3_D2D4, 0xF1D4_E242, 0x68DD_B3F8, 0x1FDA_836E, 0x81BE_16CD, 0xF6B9_265B, 0x6FB0_77E1, 0x18B7_4777, + 0x8808_5AE6, 0xFF0F_6A70, 0x6606_3BCA, 0x1101_0B5C, 0x8F65_9EFF, 0xF862_AE69, 0x616B_FFD3, 0x166C_CF45, + 0xA00A_E278, 0xD70D_D2EE, 0x4E04_8354, 0x3903_B3C2, 0xA767_2661, 0xD060_16F7, 0x4969_474D, 0x3E6E_77DB, + 0xAED1_6A4A, 0xD9D6_5ADC, 0x40DF_0B66, 0x37D8_3BF0, 0xA9BC_AE53, 0xDEBB_9EC5, 0x47B2_CF7F, 0x30B5_FFE9, + 0xBDBD_F21C, 0xCABA_C28A, 0x53B3_9330, 0x24B4_A3A6, 0xBAD0_3605, 0xCDD7_0693, 0x54DE_5729, 0x23D9_67BF, + 0xB366_7A2E, 0xC461_4AB8, 0x5D68_1B02, 0x2A6F_2B94, 0xB40B_BE37, 0xC30C_8EA1, 0x5A05_DF1B, 0x2D02_EF8D + ] + + @usableFromInline + static let table32c: Array = [ + 0x0000_0000, 0xF26B_8303, 0xE13B_70F7, 0x1350_F3F4, 0xC79A_971F, 0x35F1_141C, 0x26A1_E7E8, 0xD4CA_64EB, + 0x8AD9_58CF, 0x78B2_DBCC, 0x6BE2_2838, 0x9989_AB3B, 0x4D43_CFD0, 0xBF28_4CD3, 0xAC78_BF27, 0x5E13_3C24, + 0x105E_C76F, 0xE235_446C, 0xF165_B798, 0x030E_349B, 0xD7C4_5070, 0x25AF_D373, 0x36FF_2087, 0xC494_A384, + 0x9A87_9FA0, 0x68EC_1CA3, 0x7BBC_EF57, 0x89D7_6C54, 0x5D1D_08BF, 0xAF76_8BBC, 0xBC26_7848, 0x4E4D_FB4B, + 0x20BD_8EDE, 0xD2D6_0DDD, 0xC186_FE29, 0x33ED_7D2A, 0xE727_19C1, 0x154C_9AC2, 0x061C_6936, 0xF477_EA35, + 0xAA64_D611, 0x580F_5512, 0x4B5F_A6E6, 0xB934_25E5, 0x6DFE_410E, 0x9F95_C20D, 0x8CC5_31F9, 0x7EAE_B2FA, + 0x30E3_49B1, 0xC288_CAB2, 0xD1D8_3946, 0x23B3_BA45, 0xF779_DEAE, 0x0512_5DAD, 0x1642_AE59, 0xE429_2D5A, + 0xBA3A_117E, 0x4851_927D, 0x5B01_6189, 0xA96A_E28A, 0x7DA0_8661, 0x8FCB_0562, 0x9C9B_F696, 0x6EF0_7595, + 0x417B_1DBC, 0xB310_9EBF, 0xA040_6D4B, 0x522B_EE48, 0x86E1_8AA3, 0x748A_09A0, 0x67DA_FA54, 0x95B1_7957, + 0xCBA2_4573, 0x39C9_C670, 0x2A99_3584, 0xD8F2_B687, 0x0C38_D26C, 0xFE53_516F, 0xED03_A29B, 0x1F68_2198, + 0x5125_DAD3, 0xA34E_59D0, 0xB01E_AA24, 0x4275_2927, 0x96BF_4DCC, 0x64D4_CECF, 0x7784_3D3B, 0x85EF_BE38, + 0xDBFC_821C, 0x2997_011F, 0x3AC7_F2EB, 0xC8AC_71E8, 0x1C66_1503, 0xEE0D_9600, 0xFD5D_65F4, 0x0F36_E6F7, + 0x61C6_9362, 0x93AD_1061, 0x80FD_E395, 0x7296_6096, 0xA65C_047D, 0x5437_877E, 0x4767_748A, 0xB50C_F789, + 0xEB1F_CBAD, 0x1974_48AE, 0x0A24_BB5A, 0xF84F_3859, 0x2C85_5CB2, 0xDEEE_DFB1, 0xCDBE_2C45, 0x3FD5_AF46, + 0x7198_540D, 0x83F3_D70E, 0x90A3_24FA, 0x62C8_A7F9, 0xB602_C312, 0x4469_4011, 0x5739_B3E5, 0xA552_30E6, + 0xFB41_0CC2, 0x092A_8FC1, 0x1A7A_7C35, 0xE811_FF36, 0x3CDB_9BDD, 0xCEB0_18DE, 0xDDE0_EB2A, 0x2F8B_6829, + 0x82F6_3B78, 0x709D_B87B, 0x63CD_4B8F, 0x91A6_C88C, 0x456C_AC67, 0xB707_2F64, 0xA457_DC90, 0x563C_5F93, + 0x082F_63B7, 0xFA44_E0B4, 0xE914_1340, 0x1B7F_9043, 0xCFB5_F4A8, 0x3DDE_77AB, 0x2E8E_845F, 0xDCE5_075C, + 0x92A8_FC17, 0x60C3_7F14, 0x7393_8CE0, 0x81F8_0FE3, 0x5532_6B08, 0xA759_E80B, 0xB409_1BFF, 0x4662_98FC, + 0x1871_A4D8, 0xEA1A_27DB, 0xF94A_D42F, 0x0B21_572C, 0xDFEB_33C7, 0x2D80_B0C4, 0x3ED0_4330, 0xCCBB_C033, + 0xA24B_B5A6, 0x5020_36A5, 0x4370_C551, 0xB11B_4652, 0x65D1_22B9, 0x97BA_A1BA, 0x84EA_524E, 0x7681_D14D, + 0x2892_ED69, 0xDAF9_6E6A, 0xC9A9_9D9E, 0x3BC2_1E9D, 0xEF08_7A76, 0x1D63_F975, 0x0E33_0A81, 0xFC58_8982, + 0xB215_72C9, 0x407E_F1CA, 0x532E_023E, 0xA145_813D, 0x758F_E5D6, 0x87E4_66D5, 0x94B4_9521, 0x66DF_1622, + 0x38CC_2A06, 0xCAA7_A905, 0xD9F7_5AF1, 0x2B9C_D9F2, 0xFF56_BD19, 0x0D3D_3E1A, 0x1E6D_CDEE, 0xEC06_4EED, + 0xC38D_26C4, 0x31E6_A5C7, 0x22B6_5633, 0xD0DD_D530, 0x0417_B1DB, 0xF67C_32D8, 0xE52C_C12C, 0x1747_422F, + 0x4954_7E0B, 0xBB3F_FD08, 0xA86F_0EFC, 0x5A04_8DFF, 0x8ECE_E914, 0x7CA5_6A17, 0x6FF5_99E3, 0x9D9E_1AE0, + 0xD3D3_E1AB, 0x21B8_62A8, 0x32E8_915C, 0xC083_125F, 0x1449_76B4, 0xE622_F5B7, 0xF572_0643, 0x0719_8540, + 0x590A_B964, 0xAB61_3A67, 0xB831_C993, 0x4A5A_4A90, 0x9E90_2E7B, 0x6CFB_AD78, 0x7FAB_5E8C, 0x8DC0_DD8F, + 0xE330_A81A, 0x115B_2B19, 0x020B_D8ED, 0xF060_5BEE, 0x24AA_3F05, 0xD6C1_BC06, 0xC591_4FF2, 0x37FA_CCF1, + 0x69E9_F0D5, 0x9B82_73D6, 0x88D2_8022, 0x7AB9_0321, 0xAE73_67CA, 0x5C18_E4C9, 0x4F48_173D, 0xBD23_943E, + 0xF36E_6F75, 0x0105_EC76, 0x1255_1F82, 0xE03E_9C81, 0x34F4_F86A, 0xC69F_7B69, 0xD5CF_889D, 0x27A4_0B9E, + 0x79B7_37BA, 0x8BDC_B4B9, 0x988C_474D, 0x6AE7_C44E, 0xBE2D_A0A5, 0x4C46_23A6, 0x5F16_D052, 0xAD7D_5351 + ] + + @usableFromInline + static let table16: Array = [ + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 + ] + + @usableFromInline + init() { + // + } + + /// Polynomial: 0xEDB88320 (Reversed) - IEEE + + func crc32(_ message: Array, seed: UInt32? = nil, reflect: Bool = true) -> UInt32 { + var crc: UInt32 = seed != nil ? seed! : 0xFFFF_FFFF + for chunk in message.batched(by: 256) { + for b in chunk { + let idx = Int((crc ^ UInt32(reflect ? b : reversed(b))) & 0xFF) + crc = (crc >> 8) ^ Checksum.table32[idx] + } + } + return (reflect ? crc : reversed(crc)) ^ 0xFFFF_FFFF + } + + /// Polynomial: 0x82F63B78 (Reversed) - Castagnoli + + func crc32c(_ message: Array, seed: UInt32? = nil, reflect: Bool = true) -> UInt32 { + var crc: UInt32 = seed != nil ? seed! : 0xFFFF_FFFF + for chunk in message.batched(by: 256) { + for b in chunk { + let idx = Int((crc ^ UInt32(reflect ? b : reversed(b))) & 0xFF) + crc = (crc >> 8) ^ Checksum.table32c[idx] + } + } + return (reflect ? crc : reversed(crc)) ^ 0xFFFF_FFFF + } + + /// Polynomial: 0xA001 (Reversed) - IBM + + func crc16(_ message: Array, seed: UInt16? = nil) -> UInt16 { + var crc: UInt16 = seed != nil ? seed! : 0x0000 + for chunk in message.batched(by: 256) { + for b in chunk { + crc = (crc >> 8) ^ Checksum.table16[Int((crc ^ UInt16(b)) & 0xFF)] + } + } + return crc + } +} + +// MARK: internal interface + +internal extension Checksum { + /// Calculate CRC32. + /// + /// - parameter message: Message + /// - parameter seed: Seed value (Optional) + /// - parameter reflect: is reflect (default true) + /// + /// - returns: Calculated code + + static func crc32(_ message: Array, seed: UInt32? = nil, reflect: Bool = true) -> UInt32 { + Checksum().crc32(message, seed: seed, reflect: reflect) + } + + /// Calculate CRC32C + /// + /// - parameter message: Message + /// - parameter seed: Seed value (Optional) + /// - parameter reflect: is reflect (default true) + /// + /// - returns: Calculated code + + static func crc32c(_ message: Array, seed: UInt32? = nil, reflect: Bool = true) -> UInt32 { + Checksum().crc32c(message, seed: seed, reflect: reflect) + } + + /// Calculate CRC16 + /// + /// - parameter message: Message + /// - parameter seed: Seed value (Optional) + /// + /// - returns: Calculated code + + static func crc16(_ message: Array, seed: UInt16? = nil) -> UInt16 { + Checksum().crc16(message, seed: seed) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Cipher.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Cipher.swift new file mode 100644 index 0000000..e7dca34 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Cipher.swift @@ -0,0 +1,47 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal enum CipherError: Error { + case encrypt + case decrypt +} + +internal protocol Cipher: AnyObject { + var keySize: Int { get } + + /// Encrypt given bytes at once + /// + /// - parameter bytes: Plaintext data + /// - returns: Encrypted data + func encrypt(_ bytes: ArraySlice) throws -> Array + func encrypt(_ bytes: Array) throws -> Array + + /// Decrypt given bytes at once + /// + /// - parameter bytes: Ciphertext data + /// - returns: Plaintext data + func decrypt(_ bytes: ArraySlice) throws -> Array + func decrypt(_ bytes: Array) throws -> Array +} + +extension Cipher { + internal func encrypt(_ bytes: Array) throws -> Array { + try self.encrypt(bytes.slice) + } + + internal func decrypt(_ bytes: Array) throws -> Array { + try self.decrypt(bytes.slice) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Collection+Extension.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Collection+Extension.swift new file mode 100644 index 0000000..76d0e70 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Collection+Extension.swift @@ -0,0 +1,61 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// +extension Collection where Self.Element == UInt8, Self.Index == Int { + // Big endian order + + func toUInt32Array() -> Array { + guard !isEmpty else { + return [] + } + + let c = strideCount(from: startIndex, to: endIndex, by: 4) + return Array(unsafeUninitializedCapacity: c) { buf, count in + var counter = 0 + for idx in stride(from: startIndex, to: endIndex, by: 4) { + let val = UInt32(bytes: self, fromIndex: idx).bigEndian + buf[counter] = val + counter += 1 + } + count = counter + assert(counter == c) + } + } + + // Big endian order + + func toUInt64Array() -> Array { + guard !isEmpty else { + return [] + } + + let c = strideCount(from: startIndex, to: endIndex, by: 8) + return Array(unsafeUninitializedCapacity: c) { buf, count in + var counter = 0 + for idx in stride(from: startIndex, to: endIndex, by: 8) { + let val = UInt64(bytes: self, fromIndex: idx).bigEndian + buf[counter] = val + counter += 1 + } + count = counter + assert(counter == c) + } + } +} + +@usableFromInline +func strideCount(from: Int, to: Int, by: Int) -> Int { + let count = to - from + return count / by + (count % by > 0 ? 1 : 0) +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/CompactMap.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/CompactMap.swift new file mode 100644 index 0000000..39a28e5 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/CompactMap.swift @@ -0,0 +1,25 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +#if swift(>=4.1) +// TODO: remove this file when Xcode 9.2 is no longer used +#else + extension Sequence { + + internal func compactMap(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult] { + try flatMap(transform) + } + } +#endif diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Cryptor.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Cryptor.swift new file mode 100644 index 0000000..45566e0 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Cryptor.swift @@ -0,0 +1,22 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +/// Cryptor (Encryptor or Decryptor) +internal protocol Cryptor { + /// Seek to position in file, if block mode allows random access. + /// + /// - parameter to: new value of counter + mutating func seek(to: Int) throws +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Cryptors.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Cryptors.swift new file mode 100644 index 0000000..4b4cc39 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Cryptors.swift @@ -0,0 +1,44 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#elseif canImport(ucrt) +import ucrt +#endif + +/// Worker cryptor/decryptor of `Updatable` types +internal protocol Cryptors: AnyObject { + + /// Cryptor suitable for encryption + func makeEncryptor() throws -> Cryptor & Updatable + + /// Cryptor suitable for decryption + func makeDecryptor() throws -> Cryptor & Updatable + + /// Generate array of random bytes. Helper function. + static func randomIV(_ blockSize: Int) -> Array +} + +extension Cryptors { + /// Generate array of random values. + /// Convenience helper that uses `Swift.RandomNumberGenerator`. + /// - Parameter count: Length of array + internal static func randomIV(_ count: Int) -> Array { + (0.. +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +@available(*, renamed: "Digest") +internal typealias Hash = Digest + +/// Hash functions to calculate Digest. +internal struct Digest { + /// Calculate MD5 Digest + /// - parameter bytes: input message + /// - returns: Digest bytes + internal static func md5(_ bytes: Array) -> Array { + MD5().calculate(for: bytes) + } + + /// Calculate SHA1 Digest + /// - parameter bytes: input message + /// - returns: Digest bytes + internal static func sha1(_ bytes: Array) -> Array { + SHA1().calculate(for: bytes) + } + + /// Calculate SHA2-224 Digest + /// - parameter bytes: input message + /// - returns: Digest bytes + internal static func sha224(_ bytes: Array) -> Array { + self.sha2(bytes, variant: .sha224) + } + + /// Calculate SHA2-256 Digest + /// - parameter bytes: input message + /// - returns: Digest bytes + internal static func sha256(_ bytes: Array) -> Array { + self.sha2(bytes, variant: .sha256) + } + + /// Calculate SHA2-384 Digest + /// - parameter bytes: input message + /// - returns: Digest bytes + internal static func sha384(_ bytes: Array) -> Array { + self.sha2(bytes, variant: .sha384) + } + + /// Calculate SHA2-512 Digest + /// - parameter bytes: input message + /// - returns: Digest bytes + internal static func sha512(_ bytes: Array) -> Array { + self.sha2(bytes, variant: .sha512) + } + + /// Calculate SHA2 Digest + /// - parameter bytes: input message + /// - parameter variant: SHA-2 variant + /// - returns: Digest bytes + internal static func sha2(_ bytes: Array, variant: SHA2.Variant) -> Array { + SHA2(variant: variant).calculate(for: bytes) + } + + /// Calculate SHA3 Digest + /// - parameter bytes: input message + /// - parameter variant: SHA-3 variant + /// - returns: Digest bytes + internal static func sha3(_ bytes: Array, variant: SHA3.Variant) -> Array { + SHA3(variant: variant).calculate(for: bytes) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/DigestType.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/DigestType.swift new file mode 100644 index 0000000..9a46a01 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/DigestType.swift @@ -0,0 +1,18 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal protocol DigestType { + func calculate(for bytes: Array) -> Array +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/AES+Foundation.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/AES+Foundation.swift new file mode 100644 index 0000000..f33542b --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/AES+Foundation.swift @@ -0,0 +1,32 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +extension AES { + /// Initialize with CBC block mode. + /// + /// - Parameters: + /// - key: Key as a String. + /// - iv: IV as a String. + /// - padding: Padding + /// - Throws: Error + /// + /// The input is a String, that is treat as sequence of bytes made directly out of String. + /// If input is Base64 encoded data (which is a String technically) it is not decoded automatically for you. + internal convenience init(key: String, iv: String, padding: Padding = .pkcs7) throws { + try self.init(key: key.bytes, blockMode: CBC(iv: iv.bytes), padding: padding) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/Array+Foundation.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/Array+Foundation.swift new file mode 100644 index 0000000..1b66dce --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/Array+Foundation.swift @@ -0,0 +1,32 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +internal extension Array where Element == UInt8 { + func toBase64(options: Data.Base64EncodingOptions = []) -> String { + Data(self).base64EncodedString(options: options) + } + + init(base64: String, options: Data.Base64DecodingOptions = .ignoreUnknownCharacters) { + self.init() + + guard let decodedData = Data(base64Encoded: base64, options: options) else { + return + } + + append(contentsOf: decodedData.bytes) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/Blowfish+Foundation.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/Blowfish+Foundation.swift new file mode 100644 index 0000000..a6bdcbb --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/Blowfish+Foundation.swift @@ -0,0 +1,23 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +extension Blowfish { + /// Initialize with CBC block mode. + internal convenience init(key: String, iv: String, padding: Padding = .pkcs7) throws { + try self.init(key: key.bytes, blockMode: CBC(iv: iv.bytes), padding: padding) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/ChaCha20+Foundation.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/ChaCha20+Foundation.swift new file mode 100644 index 0000000..3ac2058 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/ChaCha20+Foundation.swift @@ -0,0 +1,22 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +extension ChaCha20 { + internal convenience init(key: String, iv: String) throws { + try self.init(key: key.bytes, iv: iv.bytes) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/Data+Extension.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/Data+Extension.swift new file mode 100644 index 0000000..a99aa8c --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/Data+Extension.swift @@ -0,0 +1,92 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +extension Data { + /// Two octet checksum as defined in RFC-4880. Sum of all octets, mod 65536 + internal func checksum() -> UInt16 { + let s = self.withUnsafeBytes { buf in + return buf.lazy.map(UInt32.init).reduce(UInt32(0), +) + } + return UInt16(s % 65535) + } + + internal func md5() -> Data { + Data( Digest.md5(bytes)) + } + + internal func sha1() -> Data { + Data( Digest.sha1(bytes)) + } + + internal func sha224() -> Data { + Data( Digest.sha224(bytes)) + } + + internal func sha256() -> Data { + Data( Digest.sha256(bytes)) + } + + internal func sha384() -> Data { + Data( Digest.sha384(bytes)) + } + + internal func sha512() -> Data { + Data( Digest.sha512(bytes)) + } + + internal func sha3(_ variant: SHA3.Variant) -> Data { + Data( Digest.sha3(bytes, variant: variant)) + } + + internal func crc32(seed: UInt32? = nil, reflect: Bool = true) -> Data { + Data( Checksum.crc32(bytes, seed: seed, reflect: reflect).bytes()) + } + + internal func crc32c(seed: UInt32? = nil, reflect: Bool = true) -> Data { + Data( Checksum.crc32c(bytes, seed: seed, reflect: reflect).bytes()) + } + + internal func crc16(seed: UInt16? = nil) -> Data { + Data( Checksum.crc16(bytes, seed: seed).bytes()) + } + + internal func encrypt(cipher: Cipher) throws -> Data { + Data( try cipher.encrypt(bytes.slice)) + } + + internal func decrypt(cipher: Cipher) throws -> Data { + Data( try cipher.decrypt(bytes.slice)) + } + + internal func authenticate(with authenticator: Authenticator) throws -> Data { + Data( try authenticator.authenticate(bytes)) + } +} + +extension Data { + internal init(hex: String) { + self.init(Array(hex: hex)) + } + + internal var bytes: Array { + Array(self) + } + + internal func toHexString() -> String { + self.bytes.toHexString() + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/HMAC+Foundation.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/HMAC+Foundation.swift new file mode 100644 index 0000000..c4aab42 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/HMAC+Foundation.swift @@ -0,0 +1,22 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +extension HMAC { + internal convenience init(key: String, variant: HMAC.Variant = .md5) throws { + self.init(key: key.bytes, variant: variant) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/Rabbit+Foundation.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/Rabbit+Foundation.swift new file mode 100644 index 0000000..5aed8ea --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/Rabbit+Foundation.swift @@ -0,0 +1,26 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +extension Rabbit { + internal convenience init(key: String) throws { + try self.init(key: key.bytes) + } + + internal convenience init(key: String, iv: String) throws { + try self.init(key: key.bytes, iv: iv.bytes) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/String+FoundationExtension.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/String+FoundationExtension.swift new file mode 100644 index 0000000..b99f82e --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/String+FoundationExtension.swift @@ -0,0 +1,41 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +extension String { + /// Return Base64 back to String + internal func decryptBase64ToString(cipher: Cipher) throws -> String { + guard let decodedData = Data(base64Encoded: self, options: []) else { + throw CipherError.decrypt + } + + let decrypted = try decodedData.decrypt(cipher: cipher) + + if let decryptedString = String(data: decrypted, encoding: String.Encoding.utf8) { + return decryptedString + } + + throw CipherError.decrypt + } + + internal func decryptBase64(cipher: Cipher) throws -> Array { + guard let decodedData = Data(base64Encoded: self, options: []) else { + throw CipherError.decrypt + } + + return try decodedData.decrypt(cipher: cipher).bytes + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/Utils+Foundation.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/Utils+Foundation.swift new file mode 100644 index 0000000..b0ba0b4 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/Utils+Foundation.swift @@ -0,0 +1,27 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +func perf(_ text: String, closure: () -> Void) { + let measurementStart = Date() + + closure() + + let measurementStop = Date() + let executionTime = measurementStop.timeIntervalSince(measurementStart) + + print("\(text) \(executionTime)") +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/XChaCha20+Foundation.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/XChaCha20+Foundation.swift new file mode 100644 index 0000000..f096588 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Foundation/XChaCha20+Foundation.swift @@ -0,0 +1,29 @@ +// +// CryptoSwift +// +// Copyright (C) Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +extension XChaCha20 { + /// Convenience initializer that creates an XChaCha20 instance with the given key and IV + /// represented as hex-encoded strings. + /// + /// - Parameters: + /// - key: The encryption/decryption key as a hex-encoded string. + /// - iv: The initialization vector as a hex-encoded string. + /// - Throws: An error if the provided key or IV are of invalid length, format, or not hex-encoded. + internal convenience init(key: String, iv: String) throws { + try self.init(key: key.bytes, iv: iv.bytes) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Generics.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Generics.swift new file mode 100644 index 0000000..4a8740f --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Generics.swift @@ -0,0 +1,43 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +/// Array of bytes. Caution: don't use directly because generic is slow. +/// +/// - parameter value: integer value +/// - parameter length: length of output array. By default size of value type +/// +/// - returns: Array of bytes +@_specialize(where T == Int) +@_specialize(where T == UInt) +@_specialize(where T == UInt8) +@_specialize(where T == UInt16) +@_specialize(where T == UInt32) +@_specialize(where T == UInt64) + +func arrayOfBytes(value: T, length totalBytes: Int = MemoryLayout.size) -> Array { + let valuePointer = UnsafeMutablePointer.allocate(capacity: 1) + valuePointer.pointee = value + + let bytesPointer = UnsafeMutablePointer(OpaquePointer(valuePointer)) + var bytes = Array(repeating: 0, count: totalBytes) + for j in 0...size, totalBytes) { + bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee + } + + valuePointer.deinitialize(count: 1) + valuePointer.deallocate() + + return bytes +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/HKDF.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/HKDF.swift new file mode 100644 index 0000000..752b63a --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/HKDF.swift @@ -0,0 +1,92 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// https://www.ietf.org/rfc/rfc5869.txt +// + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#elseif canImport(ucrt) +import ucrt +#elseif canImport(WASILibc) +import WASILibc +#endif + +/// A key derivation function. +/// +/// HKDF - HMAC-based Extract-and-Expand Key Derivation Function. +internal struct HKDF { + internal enum Error: Swift.Error { + case invalidInput + case derivedKeyTooLong + } + + private let numBlocks: Int // l + private let dkLen: Int + private let info: Array + private let prk: Array + private let variant: HMAC.Variant + + /// - parameters: + /// - variant: hash variant + /// - salt: optional salt (if not provided, it is set to a sequence of variant.digestLength zeros) + /// - info: optional context and application specific information + /// - keyLength: intended length of derived key + internal init(password: Array, salt: Array? = nil, info: Array? = nil, keyLength: Int? = nil /* dkLen */, variant: HMAC.Variant = .sha2(.sha256)) throws { + guard !password.isEmpty else { + throw Error.invalidInput + } + + let dkLen = keyLength ?? variant.digestLength + let keyLengthFinal = Double(dkLen) + let hLen = Double(variant.digestLength) + let numBlocks = Int(ceil(keyLengthFinal / hLen)) // l = ceil(keyLength / hLen) + guard numBlocks <= 255 else { + throw Error.derivedKeyTooLong + } + + /// HKDF-Extract(salt, password) -> PRK + /// - PRK - a pseudo-random key; it is used by calculate() + self.prk = try HMAC(key: salt ?? [], variant: variant).authenticate(password) + self.info = info ?? [] + self.variant = variant + self.dkLen = dkLen + self.numBlocks = numBlocks + } + + internal func calculate() throws -> Array { + let hmac = HMAC(key: prk, variant: variant) + var ret = Array() + ret.reserveCapacity(self.numBlocks * self.variant.digestLength) + var value = Array() + for i in 1...self.numBlocks { + value.append(contentsOf: self.info) + value.append(UInt8(i)) + + let bytes = try hmac.authenticate(value) + ret.append(contentsOf: bytes) + + /// update value to use it as input for next iteration + value = bytes + } + return Array(ret.prefix(self.dkLen)) + } + + internal func callAsFunction() throws -> Array { + try calculate() + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/HMAC.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/HMAC.swift new file mode 100644 index 0000000..982234e --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/HMAC.swift @@ -0,0 +1,124 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal final class HMAC: Authenticator { + internal enum Error: Swift.Error { + case authenticateError + case invalidInput + } + + internal enum Variant { + case md5 + case sha1 + case sha2(SHA2.Variant) + case sha3(SHA3.Variant) + + @available(*, deprecated, message: "Use sha2(variant) instead.") + case sha256, sha384, sha512 + + var digestLength: Int { + switch self { + case .sha1: + return SHA1.digestLength + case .sha256: + return SHA2.Variant.sha256.digestLength + case .sha384: + return SHA2.Variant.sha384.digestLength + case .sha512: + return SHA2.Variant.sha512.digestLength + case .sha2(let variant): + return variant.digestLength + case .sha3(let variant): + return variant.digestLength + case .md5: + return MD5.digestLength + } + } + + func calculateHash(_ bytes: Array) -> Array { + switch self { + case .sha1: + return Digest.sha1(bytes) + case .sha256: + return Digest.sha256(bytes) + case .sha384: + return Digest.sha384(bytes) + case .sha512: + return Digest.sha512(bytes) + case .sha2(let variant): + return Digest.sha2(bytes, variant: variant) + case .sha3(let variant): + return Digest.sha3(bytes, variant: variant) + case .md5: + return Digest.md5(bytes) + } + } + + func blockSize() -> Int { + switch self { + case .md5: + return MD5.blockSize + case .sha1: + return SHA1.blockSize + case .sha256: + return SHA2.Variant.sha256.blockSize + case .sha384: + return SHA2.Variant.sha384.blockSize + case .sha512: + return SHA2.Variant.sha512.blockSize + case .sha2(let variant): + return variant.blockSize + case .sha3(let variant): + return variant.blockSize + } + } + } + + var key: Array + let variant: Variant + + internal init(key: Array, variant: HMAC.Variant = .md5) { + self.variant = variant + self.key = key + + if key.count > variant.blockSize() { + let hash = variant.calculateHash(key) + self.key = hash + } + + if key.count < variant.blockSize() { + self.key = ZeroPadding().add(to: key, blockSize: variant.blockSize()) + } + } + + // MARK: Authenticator + + internal func authenticate(_ bytes: Array) throws -> Array { + var opad = Array(repeating: 0x5c, count: variant.blockSize()) + for idx in self.key.indices { + opad[idx] = self.key[idx] ^ opad[idx] + } + var ipad = Array(repeating: 0x36, count: variant.blockSize()) + for idx in self.key.indices { + ipad[idx] = self.key[idx] ^ ipad[idx] + } + + let ipadAndMessageHash = self.variant.calculateHash(ipad + bytes) + let result = self.variant.calculateHash(opad + ipadAndMessageHash) + + // return Array(result[0..<10]) // 80 bits + return result + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/ISO10126Padding.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/ISO10126Padding.swift new file mode 100644 index 0000000..04297da --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/ISO10126Padding.swift @@ -0,0 +1,56 @@ +// +// CryptoSwift +// +// Copyright (C) Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +/// Padding with random bytes, ending with the number of added bytes. +/// Read the [Wikipedia](https://en.wikipedia.org/wiki/Padding_(cryptography)#ISO_10126) +/// and [Crypto-IT](http://www.crypto-it.net/eng/theory/padding.html) articles for more info. +struct ISO10126Padding: PaddingProtocol { + init() { + } + + + func add(to bytes: Array, blockSize: Int) -> Array { + let padding = UInt8(blockSize - (bytes.count % blockSize)) + var withPadding = bytes + if padding > 0 { + withPadding += (0..<(padding - 1)).map { _ in UInt8.random(in: 0...255) } + [padding] + } + return withPadding + } + + + func remove(from bytes: Array, blockSize: Int?) -> Array { + guard !bytes.isEmpty, let lastByte = bytes.last else { + return bytes + } + + assert(!bytes.isEmpty, "Need bytes to remove padding") + + let padding = Int(lastByte) // last byte + let finalLength = bytes.count - padding + + if finalLength < 0 { + return bytes + } + + if padding >= 1 { + return Array(bytes[0.. +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +// First byte is 0x80, rest is zero padding +// http://www.crypto-it.net/eng/theory/padding.html +// http://www.embedx.com/pdfs/ISO_STD_7816/info_isoiec7816-4%7Bed21.0%7Den.pdf +struct ISO78164Padding: PaddingProtocol { + init() { + } + + + func add(to bytes: Array, blockSize: Int) -> Array { + var padded = Array(bytes) + padded.append(0x80) + + while (padded.count % blockSize) != 0 { + padded.append(0x00) + } + return padded + } + + + func remove(from bytes: Array, blockSize _: Int?) -> Array { + if let idx = bytes.lastIndex(of: 0x80) { + return Array(bytes[.. +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#elseif canImport(ucrt) +import ucrt +#endif + +extension FixedWidthInteger { + + func bytes(totalBytes: Int = MemoryLayout.size) -> Array { + arrayOfBytes(value: self.littleEndian, length: totalBytes) + // TODO: adjust bytes order + // var value = self.littleEndian + // return withUnsafeBytes(of: &value, Array.init).reversed() + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/MD5.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/MD5.swift new file mode 100644 index 0000000..065f856 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/MD5.swift @@ -0,0 +1,166 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal final class MD5: DigestType { + static let blockSize: Int = 64 + static let digestLength: Int = 16 // 128 / 8 + fileprivate static let hashInitialValue: Array = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476] + + fileprivate var accumulated = Array() + fileprivate var processedBytesTotalCount: Int = 0 + fileprivate var accumulatedHash: Array = MD5.hashInitialValue + + /** specifies the per-round shift amounts */ + private let s: Array = [ + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 + ] + + /** binary integer part of the sines of integers (Radians) */ + private let k: Array = [ + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 + ] + + internal init() { + } + + internal func calculate(for bytes: Array) -> Array { + do { + return try update(withBytes: bytes.slice, isLast: true) + } catch { + fatalError() + } + } + + internal func callAsFunction(_ bytes: Array) -> Array { + calculate(for: bytes) + } + + + // mutating currentHash in place is way faster than returning new result + fileprivate func process(block chunk: ArraySlice, currentHash: inout Array) { + assert(chunk.count == 16 * 4) + + // Initialize hash value for this chunk: + var A: UInt32 = currentHash[0] + var B: UInt32 = currentHash[1] + var C: UInt32 = currentHash[2] + var D: UInt32 = currentHash[3] + + var dTemp: UInt32 = 0 + + // Main loop + for j in 0.., isLast: Bool = false) throws -> Array { + self.accumulated += bytes + + if isLast { + let lengthInBits = (processedBytesTotalCount + self.accumulated.count) * 8 + let lengthBytes = lengthInBits.bytes(totalBytes: 64 / 8) // A 64-bit representation of b + + // Step 1. Append padding + bitPadding(to: &self.accumulated, blockSize: MD5.blockSize, allowance: 64 / 8) + + // Step 2. Append Length a 64-bit representation of lengthInBits + self.accumulated += lengthBytes.reversed() + } + + var processedBytes = 0 + for chunk in self.accumulated.batched(by: MD5.blockSize) { + if isLast || (self.accumulated.count - processedBytes) >= MD5.blockSize { + self.process(block: chunk, currentHash: &self.accumulatedHash) + processedBytes += chunk.count + } + } + self.accumulated.removeFirst(processedBytes) + self.processedBytesTotalCount += processedBytes + + // output current hash + var result = Array() + result.reserveCapacity(MD5.digestLength) + + for hElement in self.accumulatedHash { + let hLE = hElement.littleEndian + result += Array(arrayLiteral: UInt8(hLE & 0xff), UInt8((hLE >> 8) & 0xff), UInt8((hLE >> 16) & 0xff), UInt8((hLE >> 24) & 0xff)) + } + + // reset hash value for instance + if isLast { + self.accumulatedHash = MD5.hashInitialValue + } + + return result + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/NoPadding.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/NoPadding.swift new file mode 100644 index 0000000..d231e5f --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/NoPadding.swift @@ -0,0 +1,27 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +struct NoPadding: PaddingProtocol { + init() { + } + + func add(to data: Array, blockSize _: Int) -> Array { + data + } + + func remove(from data: Array, blockSize _: Int?) -> Array { + data + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Operators.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Operators.swift new file mode 100644 index 0000000..4128b56 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Operators.swift @@ -0,0 +1,32 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +/* + Bit shifting with overflow protection using overflow operator "&". + Approach is consistent with standard overflow operators &+, &-, &*, &/ + and introduce new overflow operators for shifting: &<<, &>> + + Note: Works with unsigned integers values only + + Usage + + var i = 1 // init + var j = i &<< 2 //shift left + j &<<= 2 //shift left and assign + + @see: https://medium.com/@krzyzanowskim/swiftly-shift-bits-and-protect-yourself-be33016ce071 + + This fuctonality is now implemented as part of Swift 3, SE-0104 https://github.com/apple/swift-evolution/blob/master/proposals/0104-improved-integers.md + */ diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/PEM/DER.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/PEM/DER.swift new file mode 100644 index 0000000..218a905 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/PEM/DER.swift @@ -0,0 +1,90 @@ +// +// CryptoSwift +// +// Copyright (C) Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +/// Conform to this protocol if your type can both be instantiated and expressed as an ASN1 DER representation. +internal protocol DERCodable: DERDecodable, DEREncodable { } + +/// Conform to this protocol if your type can be instantiated from a ASN1 DER representation +internal protocol DERDecodable { + /// Attempts to instantiate an instance of your internal Key when given a DER representation of your internal Key + /// + /// - Parameter publicDER: The ASN.1 DER representation of your internal Key + init(publicDER: Array) throws + + /// Attempts to instantiate an instance of your Private Key when given a DER representation of your Private Key + /// + /// - Parameter privateDER: The ASN.1 DER representation of your Private Key + init(privateDER: Array) throws + + /// Attempts to instantiate a Key when given the ASN1 DER encoded external representation of the Key + /// + /// - Parameter rawRepresentation: The ASN1 DER Encoded external representation (either internal or private) + /// - Note: The external representation is identical to that of `SecKeyCopyExternalRepresentation` function from Apple's `Security` framework + init(rawRepresentation: Data) throws +} + +/// Conform to this protocol if your type can be described in an ASN1 DER representation +internal protocol DEREncodable { + /// Returns the DER encoded representation of the internal Key + func publicKeyDER() throws -> Array + + /// Returns the DER encoded representation of the Private Key + func privateKeyDER() throws -> Array + + /// A semantically similar function that mimics the `SecKeyCopyExternalRepresentation` function from Apple's `Security` framework + /// - Note: If called on a Private Key, this method will return the Private Keys DER Representation. Likewise, if called on a internal Key, this method will return the internal Keys DER Representation + /// - Note: If you'd like to export the internal Keys DER from a Private Key, use the `publicKeyExternalRepresentation()` function + func externalRepresentation() throws -> Data + + /// A semantically similar function that mimics the `SecKeyCopyExternalRepresentation` function from Apple's `Security` framework + /// - Note: This function only ever exports the internal Key's DER representation. If called on a Private Key, the corresponding internal Key will be extracted and exported. + func publicKeyExternalRepresentation() throws -> Data +} + +struct DER { + internal enum Error: Swift.Error { + /// We were provided invalid DER data + case invalidDERFormat + } + + /// Integer to Octet String Primitive + /// - Parameters: + /// - x: nonnegative integer to be converted + /// - size: intended length of the resulting octet string + /// - Returns: corresponding octet string of length xLen + /// - Note: https://datatracker.ietf.org/doc/html/rfc3447#section-4.1 + internal static func i2osp(x: [UInt8], size: Int) -> [UInt8] { + var modulus = x + while modulus.count < size { + modulus.insert(0x00, at: 0) + } + if modulus[0] >= 0x80 { + modulus.insert(0x00, at: 0) + } + return modulus + } + + /// Integer to Octet String Primitive + /// - Parameters: + /// - x: nonnegative integer to be converted + /// - size: intended length of the resulting octet string + /// - Returns: corresponding octet string of length xLen + /// - Note: https://datatracker.ietf.org/doc/html/rfc3447#section-4.1 + internal static func i2ospData(x: [UInt8], size: Int) -> Data { + return Data(DER.i2osp(x: x, size: size)) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/PKCS/PBKDF1.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/PKCS/PBKDF1.swift new file mode 100644 index 0000000..432853a --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/PKCS/PBKDF1.swift @@ -0,0 +1,101 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal extension PKCS5 { + /// A key derivation function. + /// + /// PBKDF1 is recommended only for compatibility with existing + /// applications since the keys it produces may not be large enough for + /// some applications. + struct PBKDF1 { + internal enum Error: Swift.Error { + case invalidInput + case derivedKeyTooLong + } + + internal enum Variant { + case md5, sha1 + + @usableFromInline + var size: Int { + switch self { + case .md5: + return MD5.digestLength + case .sha1: + return SHA1.digestLength + } + } + + @usableFromInline + func calculateHash(_ bytes: Array) -> Array { + switch self { + case .sha1: + return Digest.sha1(bytes) + case .md5: + return Digest.md5(bytes) + } + } + } + + @usableFromInline + let iterations: Int // c + + @usableFromInline + let variant: Variant + + @usableFromInline + let keyLength: Int + + @usableFromInline + let t1: Array + + /// - parameters: + /// - salt: salt, an eight-bytes + /// - variant: hash variant + /// - iterations: iteration count, a positive integer + /// - keyLength: intended length of derived key + internal init(password: Array, salt: Array, variant: Variant = .sha1, iterations: Int = 4096 /* c */, keyLength: Int? = nil /* dkLen */ ) throws { + precondition(iterations > 0) + precondition(salt.count == 8) + + let keyLength = keyLength ?? variant.size + + if keyLength > variant.size { + throw Error.derivedKeyTooLong + } + + let t1 = variant.calculateHash(password + salt) + + self.iterations = iterations + self.variant = variant + self.keyLength = keyLength + self.t1 = t1 + } + + /// Apply the underlying hash function Hash for c iterations + + internal func calculate() -> Array { + var t = self.t1 + for _ in 2...self.iterations { + t = self.variant.calculateHash(t) + } + return Array(t[0.. Array { + calculate() + } + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/PKCS/PBKDF2.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/PKCS/PBKDF2.swift new file mode 100644 index 0000000..06a5fae --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/PKCS/PBKDF2.swift @@ -0,0 +1,124 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// https://www.ietf.org/rfc/rfc2898.txt +// + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#elseif canImport(ucrt) +import ucrt +#elseif canImport(WASILibc) +import WASILibc +#endif + +internal extension PKCS5 { + /// A key derivation function. + /// + /// PBKDF2 - Password-Based Key Derivation Function 2. Key stretching technique. + /// DK = PBKDF2(PRF, Password, Salt, c, dkLen) + struct PBKDF2 { + internal enum Error: Swift.Error { + case invalidInput + case derivedKeyTooLong + } + + private let salt: Array // S + fileprivate let iterations: Int // c + private let numBlocks: Int // l + private let dkLen: Int + fileprivate let prf: HMAC + + /// - parameters: + /// - salt: salt + /// - variant: hash variant + /// - iterations: iteration count, a positive integer + /// - keyLength: intended length of derived key + /// - variant: MAC variant. Defaults to SHA256 + internal init(password: Array, salt: Array, iterations: Int = 4096 /* c */, keyLength: Int? = nil /* dkLen */, variant: HMAC.Variant = .sha2(.sha256)) throws { + precondition(iterations > 0) + + let prf = HMAC(key: password, variant: variant) + + guard iterations > 0 && !salt.isEmpty else { + throw Error.invalidInput + } + + self.dkLen = keyLength ?? variant.digestLength + let keyLengthFinal = Double(dkLen) + let hLen = Double(prf.variant.digestLength) + if keyLengthFinal > (pow(2, 32) - 1) * hLen { + throw Error.derivedKeyTooLong + } + + self.salt = salt + self.iterations = iterations + self.prf = prf + + self.numBlocks = Int(ceil(Double(keyLengthFinal) / hLen)) // l = ceil(keyLength / hLen) + } + + internal func calculate() throws -> Array { + var ret = Array() + ret.reserveCapacity(self.numBlocks * self.prf.variant.digestLength) + for i in 1...self.numBlocks { + // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter + if let value = try calculateBlock(self.salt, blockNum: i) { + ret.append(contentsOf: value) + } + } + return Array(ret.prefix(self.dkLen)) + } + + internal func callAsFunction() throws -> Array { + try calculate() + } + } +} + +private extension PKCS5.PBKDF2 { + func ARR(_ i: Int) -> Array { + var inti = Array(repeating: 0, count: 4) + inti[0] = UInt8((i >> 24) & 0xff) + inti[1] = UInt8((i >> 16) & 0xff) + inti[2] = UInt8((i >> 8) & 0xff) + inti[3] = UInt8(i & 0xff) + return inti + } + + // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c + // U_1 = PRF (P, S || INT (i)) + func calculateBlock(_ salt: Array, blockNum: Int) throws -> Array? { + guard let u1 = try? prf.authenticate(salt + ARR(blockNum)) else { // blockNum.bytes() is slower + return nil + } + + var u = u1 + var ret = u + if iterations > 1 { + // U_2 = PRF (P, U_1) , + // U_c = PRF (P, U_{c-1}) . + for _ in 2...iterations { + u = try prf.authenticate(u) + for x in 0.. +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// PKCS is a group of public-key cryptography standards devised +// and published by RSA Security Inc, starting in the early 1990s. +// + +/// EMSA PKCS1 v1.5 Padding Scheme +/// +/// The EMSA Version of the PKCS1 v1.5 padding scheme is **deterministic** (it pads the messages contents with 255 value bytes) +/// ``` +/// // The returned structure +/// // - PS is the applied padding +/// // - M is your original Message +/// EM = 0x00 || 0x01 || PS || 0x00 || M. +/// ``` +/// - Note: This Padding scheme is intended to be used for encoding RSA Signatures +/// +/// [EMSA-PKCS1v1_5 IETF Spec](https://datatracker.ietf.org/doc/html/rfc8017#section-9.2) +struct EMSAPKCS1v15Padding: PaddingProtocol { + + init() { + } + + + func add(to bytes: Array, blockSize: Int) -> Array { + var r = blockSize - ((bytes.count + 3) % blockSize) + if r <= 0 { r = blockSize - 3 } + + return [0x00, 0x01] + Array(repeating: 0xFF, count: r) + [0x00] + bytes + } + + + func remove(from bytes: Array, blockSize _: Int?) -> Array { + assert(!bytes.isEmpty, "Need bytes to remove padding") + + assert(bytes.prefix(2) == [0x00, 0x01], "Invalid padding prefix") + + guard let paddingLength = bytes.dropFirst(2).firstIndex(of: 0x00) else { return bytes } + + guard (paddingLength + 1) <= bytes.count else { return bytes } + + return Array(bytes[(paddingLength + 1)...]) + } +} + +/// EME PKCS1 v1.5 Padding Scheme +/// +/// The EME Version of the PKCS1 v1.5 padding scheme is **non deterministic** (it pads the messages contents with psuedo-random bytes) +/// ``` +/// // The returned structure +/// // - PS is the applied padding +/// // - M is your original Message +/// EM = 0x00 || 0x02 || PS || 0x00 || M. +/// ``` +/// - Note: This Padding scheme is intended to be used for encoding messages before RSA Encryption +/// +/// [EME-PKCS1v1_5 IETF Spec](https://datatracker.ietf.org/doc/html/rfc8017#section-7.2.1) +struct EMEPKCS1v15Padding: PaddingProtocol { + + init() { + } + + + func add(to bytes: Array, blockSize: Int) -> Array { + var r = blockSize - ((bytes.count + 3) % blockSize) + if r <= 0 { r = blockSize - 3 } + + return [0x00, 0x02] + (0.., blockSize _: Int?) -> Array { + assert(!bytes.isEmpty, "Need bytes to remove padding") + + assert(bytes.prefix(2) == [0x00, 0x02], "Invalid padding prefix") + + guard let paddingLength = bytes.dropFirst(2).firstIndex(of: 0x00) else { return bytes } + + guard (paddingLength + 1) <= bytes.count else { return bytes } + + return Array(bytes[(paddingLength + 1)...]) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/PKCS/PKCS5.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/PKCS/PKCS5.swift new file mode 100644 index 0000000..163ce71 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/PKCS/PKCS5.swift @@ -0,0 +1,22 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// PKCS is a group of public-key cryptography standards devised +// and published by RSA Security Inc, starting in the early 1990s. +// + +internal enum PKCS5 { + typealias Padding = PKCS7Padding +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/PKCS/PKCS7.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/PKCS/PKCS7.swift new file mode 100644 index 0000000..2835b66 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/PKCS/PKCS7.swift @@ -0,0 +1,18 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal enum PKCS7 { + typealias Padding = PKCS7Padding +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/PKCS/PKCS7Padding.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/PKCS/PKCS7Padding.swift new file mode 100644 index 0000000..ece465c --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/PKCS/PKCS7Padding.swift @@ -0,0 +1,55 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// PKCS is a group of public-key cryptography standards devised +// and published by RSA Security Inc, starting in the early 1990s. +// + +struct PKCS7Padding: PaddingProtocol { + enum Error: Swift.Error { + case invalidPaddingValue + } + + init() { + } + + + func add(to bytes: Array, blockSize: Int) -> Array { + let padding = UInt8(blockSize - (bytes.count % blockSize)) + // The value of each added byte is the number of bytes that are added + return bytes + Array(repeating: padding, count: Int(padding)) + } + + + func remove(from bytes: Array, blockSize _: Int?) -> Array { + guard !bytes.isEmpty, let lastByte = bytes.last else { + return bytes + } + + assert(!bytes.isEmpty, "Need bytes to remove padding") + + let padding = Int(lastByte) // last byte + let finalLength = bytes.count - padding + + if finalLength < 0 { + return bytes + } + + if padding >= 1 { + return Array(bytes[0.. +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal protocol PaddingProtocol { + func add(to: Array, blockSize: Int) -> Array + func remove(from: Array, blockSize: Int?) -> Array +} + +internal enum Padding: PaddingProtocol { + case noPadding, zeroPadding, pkcs7, pkcs5, eme_pkcs1v15, emsa_pkcs1v15, iso78164, iso10126 + + internal func add(to: Array, blockSize: Int) -> Array { + switch self { + case .noPadding: + return to // NoPadding().add(to: to, blockSize: blockSize) + case .zeroPadding: + return ZeroPadding().add(to: to, blockSize: blockSize) + case .pkcs7: + return PKCS7.Padding().add(to: to, blockSize: blockSize) + case .pkcs5: + return PKCS5.Padding().add(to: to, blockSize: blockSize) + case .eme_pkcs1v15: + return EMEPKCS1v15Padding().add(to: to, blockSize: blockSize) + case .emsa_pkcs1v15: + return EMSAPKCS1v15Padding().add(to: to, blockSize: blockSize) + case .iso78164: + return ISO78164Padding().add(to: to, blockSize: blockSize) + case .iso10126: + return ISO10126Padding().add(to: to, blockSize: blockSize) + } + } + + internal func remove(from: Array, blockSize: Int?) -> Array { + switch self { + case .noPadding: + return from //NoPadding().remove(from: from, blockSize: blockSize) + case .zeroPadding: + return ZeroPadding().remove(from: from, blockSize: blockSize) + case .pkcs7: + return PKCS7.Padding().remove(from: from, blockSize: blockSize) + case .pkcs5: + return PKCS5.Padding().remove(from: from, blockSize: blockSize) + case .eme_pkcs1v15: + return EMEPKCS1v15Padding().remove(from: from, blockSize: blockSize) + case .emsa_pkcs1v15: + return EMSAPKCS1v15Padding().remove(from: from, blockSize: blockSize) + case .iso78164: + return ISO78164Padding().remove(from: from, blockSize: blockSize) + case .iso10126: + return ISO10126Padding().remove(from: from, blockSize: blockSize) + } + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Poly1305.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Poly1305.swift new file mode 100644 index 0000000..3dde98b --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Poly1305.swift @@ -0,0 +1,165 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-4 +// nacl/crypto_onetimeauth/poly1305/ref/auth.c +// +/// Poly1305 takes a 32-byte, one-time key and a message and produces a 16-byte tag that authenticates the +/// message such that an attacker has a negligible chance of producing a valid tag for an inauthentic message. + +internal final class Poly1305: Authenticator { + internal enum Error: Swift.Error { + case authenticateError + } + + internal static let blockSize: Int = 16 + + private let key: SecureBytes + + /// - parameter key: 32-byte key + internal init(key: Array) { + self.key = SecureBytes(bytes: key) + } + + private func squeeze(h: inout Array) { + assert(h.count == 17) + var u: UInt32 = 0 + for j in 0..<16 { + u = u &+ h[j] + h[j] = u & 255 + u = u >> 8 + } + + u = u &+ h[16] + h[16] = u & 3 + u = 5 * (u >> 2) + + for j in 0..<16 { + u = u &+ h[j] + h[j] = u & 255 + u = u >> 8 + } + + u = u &+ h[16] + h[16] = u + } + + private func add(h: inout Array, c: Array) { + assert(h.count == 17 && c.count == 17) + + var u: UInt32 = 0 + for j in 0..<17 { + u = u &+ (h[j] &+ c[j]) + h[j] = u & 255 + u = u >> 8 + } + } + + private func mulmod(h: inout Array, r: Array) { + var hr = Array(repeating: 0, count: 17) + var u: UInt32 = 0 + for i in 0..<17 { + u = 0 + for j in 0...i { + u = u &+ (h[j] * r[i &- j]) + } + for j in (i + 1)..<17 { + u = u &+ (320 * h[j] * r[i &+ 17 &- j]) + } + hr[i] = u + } + h = hr + self.squeeze(h: &h) + } + + private func freeze(h: inout Array) { + let horig = h + self.add(h: &h, c: [5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252]) + let negative = UInt32(bitPattern: -Int32(h[16] >> 7)) + for j in 0..<17 { + h[j] ^= negative & (horig[j] ^ h[j]) + } + } + + /// the key is partitioned into two parts, called "r" and "s" + fileprivate func onetimeauth(message input: Array, key k: Array) -> Array { + // clamp + var r = Array(repeating: 0, count: 17) + var h = Array(repeating: 0, count: 17) + var c = Array(repeating: 0, count: 17) + + r[0] = UInt32(k[0]) + r[1] = UInt32(k[1]) + r[2] = UInt32(k[2]) + r[3] = UInt32(k[3] & 15) + r[4] = UInt32(k[4] & 252) + r[5] = UInt32(k[5]) + r[6] = UInt32(k[6]) + r[7] = UInt32(k[7] & 15) + r[8] = UInt32(k[8] & 252) + r[9] = UInt32(k[9]) + r[10] = UInt32(k[10]) + r[11] = UInt32(k[11] & 15) + r[12] = UInt32(k[12] & 252) + r[13] = UInt32(k[13]) + r[14] = UInt32(k[14]) + r[15] = UInt32(k[15] & 15) + r[16] = 0 + + var inlen = input.count + var inpos = 0 + while inlen > 0 { + for j in 0..) throws -> Array { + self.onetimeauth(message: bytes, key: Array(self.key)) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/PrivacyInfo.xcprivacy b/MixSdk/MixSdk/MixSdk/CryptoSwift/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..e08a130 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/PrivacyInfo.xcprivacy @@ -0,0 +1,14 @@ + + + + + NSPrivacyTracking + + NSPrivacyTrackingDomains + + NSPrivacyCollectedDataTypes + + NSPrivacyAccessedAPITypes + + + diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/RSA/RSA+Cipher.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/RSA/RSA+Cipher.swift new file mode 100644 index 0000000..b43ef26 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/RSA/RSA+Cipher.swift @@ -0,0 +1,128 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +// MARK: Cipher + +extension RSA: Cipher { + + + internal func encrypt(_ bytes: ArraySlice) throws -> Array { + return try self.encrypt(Array(bytes), variant: .pksc1v15) + } + + + internal func encrypt(_ bytes: Array, variant: RSAEncryptionVariant) throws -> Array { + // Prepare the data for the specified variant + let preparedData = try variant.prepare(bytes, blockSize: self.keySizeBytes) + + // Encrypt the prepared data + return try variant.formatEncryptedBytes(self.encryptPreparedBytes(preparedData), blockSize: self.keySizeBytes) + } + + + internal func encryptPreparedBytes(_ bytes: Array) throws -> Array { + // Calculate encrypted data + return BigUInteger(Data(bytes)).power(self.e, modulus: self.n).serialize().bytes + } + + + internal func decrypt(_ bytes: ArraySlice) throws -> Array { + return try self.decrypt(Array(bytes), variant: .pksc1v15) + } + + + internal func decrypt(_ bytes: Array, variant: RSAEncryptionVariant) throws -> Array { + // Decrypt the data + let decrypted = try self.decryptPreparedBytes(bytes) + + // Remove padding / unstructure data and return the raw plaintext + return variant.removePadding(decrypted, blockSize: self.keySizeBytes) + } + + + internal func decryptPreparedBytes(_ bytes: Array) throws -> Array { + // Check for Private Exponent presence + guard let d = d else { throw RSA.Error.noPrivateKey } + + // Calculate decrypted data + return BigUInteger(Data(bytes)).power(d, modulus: self.n).serialize().bytes + } +} + +extension RSA { + /// RSA Encryption Block Types + /// - [RFC2313 8.1 - Encryption block formatting](https://datatracker.ietf.org/doc/html/rfc2313#section-8.1) + @frozen + internal enum RSAEncryptionVariant { + /// The `unsafe` encryption variant, is fully deterministic and doesn't format the inbound data in any way. + /// + /// - Warning: This is considered an unsafe method of encryption. + case unsafe + /// The `raw` encryption variant formats the inbound data with a deterministic padding scheme. + /// + /// - Warning: This is also considered to be an unsafe method of encryption, but matches the `Security` frameworks functionality. + case raw + /// The `pkcs1v15` encryption variant formats the inbound data with a non deterministic pseudo random padding scheme. + /// + /// [EME PKCS1v1.5 Padding Scheme Spec](https://datatracker.ietf.org/doc/html/rfc2313#section-8.1) + case pksc1v15 + + + internal func prepare(_ bytes: Array, blockSize: Int) throws -> Array { + switch self { + case .unsafe: + return bytes + case .raw: + // We need at least 11 bytes of padding in order to safely encrypt messages + // - block types 1 and 2 have this minimum padding requirement, block type 0 isn't specified, but we enforce the minimum padding length here to be safe. + guard blockSize >= bytes.count + 11 else { throw RSA.Error.invalidMessageLengthForEncryption } + return Array(repeating: 0x00, count: blockSize - bytes.count) + bytes + case .pksc1v15: + // The `Security` framework refuses to encrypt a zero byte message using the pkcs1v15 padding scheme, so we do the same + guard !bytes.isEmpty else { throw RSA.Error.invalidMessageLengthForEncryption } + // We need at least 11 bytes of random padding in order to safely encrypt messages (RFC2313 Section 8.1 - Note 6) + guard blockSize >= bytes.count + 11 else { throw RSA.Error.invalidMessageLengthForEncryption } + return Padding.eme_pkcs1v15.add(to: bytes, blockSize: blockSize) + } + } + + + internal func formatEncryptedBytes(_ bytes: Array, blockSize: Int) -> Array { + switch self { + case .unsafe: + return bytes + case .raw, .pksc1v15: + // Format the encrypted bytes before returning + return Array(repeating: 0x00, count: blockSize - bytes.count) + bytes + } + } + + + internal func removePadding(_ bytes: Array, blockSize: Int) -> Array { + switch self { + case .unsafe: + return bytes + case .raw: + return bytes + case .pksc1v15: + // Convert the Octet String into an Integer Primitive using the BigInteger `serialize` method + // (this effectively just prefixes the data with a 0x00 byte indicating that its a positive integer) + return Padding.eme_pkcs1v15.remove(from: [0x00] + bytes, blockSize: blockSize) + } + } + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/RSA/RSA+Signature.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/RSA/RSA+Signature.swift new file mode 100644 index 0000000..9666e80 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/RSA/RSA+Signature.swift @@ -0,0 +1,289 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +// MARK: Signatures & Verification + +extension RSA: Signature { + internal func sign(_ bytes: ArraySlice) throws -> Array { + try self.sign(Array(bytes), variant: .message_pkcs1v15_SHA256) + } + + /// Signs the data using the Private key and the specified signature variant + /// - Parameters: + /// - bytes: The data to be signed + /// - variant: The variant to use (`digest` variants expect a pre-hashed digest matching that of the specified hash function, `message` variants will hash the data using the specified hash function before signing it) + /// - Returns: The signature of the data + internal func sign(_ bytes: Array, variant: SignatureVariant) throws -> Array { + // Check for Private Exponent presence + guard let d = d else { throw RSA.Error.noPrivateKey } + + // Hash & Encode Message + let hashedAndEncoded = try RSA.hashedAndEncoded(bytes, variant: variant, keySizeInBytes: self.keySizeBytes) + + /// Calculate the Signature + let signedData = BigUInteger(Data(hashedAndEncoded)).power(d, modulus: self.n).serialize().bytes + + return variant.formatSignedBytes(signedData, blockSize: self.keySizeBytes) + } + + internal func verify(signature: ArraySlice, for expectedData: ArraySlice) throws -> Bool { + try self.verify(signature: Array(signature), for: Array(expectedData), variant: .message_pkcs1v15_SHA256) + } + + /// Verifies whether a Signature is valid for the provided data + /// - Parameters: + /// - signature: The signature to verify + /// - expectedData: The original data that you expected to have been signed + /// - variant: The variant used to sign the data + /// - Returns: `True` when the signature is valid for the expected data, `False` otherwise. + /// + /// [IETF Verification Spec](https://datatracker.ietf.org/doc/html/rfc8017#section-8.2.2) + internal func verify(signature: Array, for bytes: Array, variant: SignatureVariant) throws -> Bool { + /// Step 1: Ensure the signature is the same length as the key's modulus + guard signature.count == self.keySizeBytes else { throw Error.invalidSignatureLength } + + /// Prepare the expected message for signature comparison + var expectedData = try RSA.hashedAndEncoded(bytes, variant: variant, keySizeInBytes: self.keySizeBytes) + if expectedData.count == self.keySizeBytes && expectedData.prefix(1) == [0x00] { expectedData = Array(expectedData.dropFirst()) } + + /// Step 2: 'Decrypt' the signature + let signatureResult = BigUInteger(Data(signature)).power(self.e, modulus: self.n).serialize().bytes + + /// Step 3: Compare the 'decrypted' signature with the prepared / encoded expected message.... + guard signatureResult == expectedData else { return false } + + return true + } + + /// Hashes and Encodes a message for signing and verifying + /// + /// - Note: [EMSA-PKCS1-v1_5](https://datatracker.ietf.org/doc/html/rfc8017#section-9.2) + fileprivate static func hashedAndEncoded(_ bytes: [UInt8], variant: SignatureVariant, keySizeInBytes: Int) throws -> Array { + /// 1. Apply the hash function to the message M to produce a hash + let hashedMessage = variant.calculateHash(bytes) + + guard variant.enforceLength(hashedMessage, keySizeInBytes: keySizeInBytes) else { throw RSA.Error.invalidMessageLengthForSigning } + + /// 2. Encode the algorithm ID for the hash function and the hash value into an ASN.1 value of type DigestInfo + /// PKCS#1_15 DER Structure (OID == sha256WithRSAEncryption) + let t = variant.encode(hashedMessage) + + if case .raw = variant { return t } + + /// 3. If emLen < tLen + 11, output "intended encoded message length too short" and stop + if keySizeInBytes < t.count + 11 { throw RSA.Error.invalidMessageLengthForSigning } + + /// 4. Generate an octet string PS consisting of emLen - tLen - 3 + /// octets with hexadecimal value 0xff. The length of PS will be + /// at least 8 octets. + /// 5. Concatenate PS, the DER encoding T, and other padding to form + /// the encoded message EM as EM = 0x00 || 0x01 || PS || 0x00 || T. + let padded = variant.pad(bytes: t, to: keySizeInBytes) + + /// Ensure the signature is of the correct length + guard padded.count == keySizeInBytes else { throw RSA.Error.invalidMessageLengthForSigning } + + return padded + } +} + +extension RSA { + internal enum SignatureVariant { + /// rsaSignatureRaw + case raw + /// Hashes the raw message using MD5 before signing the data + case message_pkcs1v15_MD5 + /// Hashes the raw message using SHA1 before signing the data + case message_pkcs1v15_SHA1 + /// Hashes the raw message using SHA224 before signing the data + case message_pkcs1v15_SHA224 + /// Hashes the raw message using SHA256 before signing the data + case message_pkcs1v15_SHA256 + /// Hashes the raw message using SHA384 before signing the data + case message_pkcs1v15_SHA384 + /// Hashes the raw message using SHA512 before signing the data + case message_pkcs1v15_SHA512 + /// Hashes the raw message using SHA512-224 before signing the data + case message_pkcs1v15_SHA512_224 + /// Hashes the raw message using SHA512-256 before signing the data + case message_pkcs1v15_SHA512_256 + /// Hashes the raw message using SHA3_256 before signing the data + case message_pkcs1v15_SHA3_256 + /// Hashes the raw message using SHA3_384 before signing the data + case message_pkcs1v15_SHA3_384 + /// Hashes the raw message using SHA3_512 before signing the data + case message_pkcs1v15_SHA3_512 + /// This variant isn't supported yet + case digest_pkcs1v15_RAW + /// This variant expects that the data to be signed is a valid MD5 Hash Digest + case digest_pkcs1v15_MD5 + /// This variant expects that the data to be signed is a valid SHA1 Hash Digest + case digest_pkcs1v15_SHA1 + /// This variant expects that the data to be signed is a valid SHA224 Hash Digest + case digest_pkcs1v15_SHA224 + /// This variant expects that the data to be signed is a valid SHA256 Hash Digest + case digest_pkcs1v15_SHA256 + /// This variant expects that the data to be signed is a valid SHA384 Hash Digest + case digest_pkcs1v15_SHA384 + /// This variant expects that the data to be signed is a valid SHA512 Hash Digest + case digest_pkcs1v15_SHA512 + /// This variant expects that the data to be signed is a valid SHA512-224 Hash Digest + case digest_pkcs1v15_SHA512_224 + /// This variant expects that the data to be signed is a valid SHA512-256 Hash Digest + case digest_pkcs1v15_SHA512_256 + /// This variant expects that the data to be signed is a valid SHA3-256 Hash Digest + case digest_pkcs1v15_SHA3_256 + /// This variant expects that the data to be signed is a valid SHA3-384 Hash Digest + case digest_pkcs1v15_SHA3_384 + /// This variant expects that the data to be signed is a valid SHA3-512 Hash Digest + case digest_pkcs1v15_SHA3_512 + + internal var identifier: Array { + switch self { + case .raw, .digest_pkcs1v15_RAW: return [] + case .message_pkcs1v15_MD5, .digest_pkcs1v15_MD5: return Array(arrayLiteral: 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05) + case .message_pkcs1v15_SHA1, .digest_pkcs1v15_SHA1: return Array(arrayLiteral: 0x2b, 0x0e, 0x03, 0x02, 0x1a) + case .message_pkcs1v15_SHA256, .digest_pkcs1v15_SHA256: return Array(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01) + case .message_pkcs1v15_SHA384, .digest_pkcs1v15_SHA384: return Array(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02) + case .message_pkcs1v15_SHA512, .digest_pkcs1v15_SHA512: return Array(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03) + case .message_pkcs1v15_SHA224, .digest_pkcs1v15_SHA224: return Array(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04) + case .message_pkcs1v15_SHA512_224, .digest_pkcs1v15_SHA512_224: return Array(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x05) + case .message_pkcs1v15_SHA512_256, .digest_pkcs1v15_SHA512_256: return Array(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x06) + case .message_pkcs1v15_SHA3_256, .digest_pkcs1v15_SHA3_256: return Array(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08) + case .message_pkcs1v15_SHA3_384, .digest_pkcs1v15_SHA3_384: return Array(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x09) + case .message_pkcs1v15_SHA3_512, .digest_pkcs1v15_SHA3_512: return Array(arrayLiteral: 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0A) + } + } + + internal func calculateHash(_ bytes: Array) -> Array { + switch self { + case .message_pkcs1v15_MD5: + return Digest.md5(bytes) + case .message_pkcs1v15_SHA1: + return Digest.sha1(bytes) + case .message_pkcs1v15_SHA224: + return Digest.sha224(bytes) + case .message_pkcs1v15_SHA256: + return Digest.sha256(bytes) + case .message_pkcs1v15_SHA384: + return Digest.sha384(bytes) + case .message_pkcs1v15_SHA512: + return Digest.sha512(bytes) + case .message_pkcs1v15_SHA512_224: + return Digest.sha2(bytes, variant: .sha224) + case .message_pkcs1v15_SHA512_256: + return Digest.sha2(bytes, variant: .sha256) + case .message_pkcs1v15_SHA3_256: + return Digest.sha3(bytes, variant: .sha256) + case .message_pkcs1v15_SHA3_384: + return Digest.sha3(bytes, variant: .sha384) + case .message_pkcs1v15_SHA3_512: + return Digest.sha3(bytes, variant: .sha512) + case .raw, + .digest_pkcs1v15_RAW, + .digest_pkcs1v15_MD5, + .digest_pkcs1v15_SHA1, + .digest_pkcs1v15_SHA224, + .digest_pkcs1v15_SHA256, + .digest_pkcs1v15_SHA384, + .digest_pkcs1v15_SHA512, + .digest_pkcs1v15_SHA512_224, + .digest_pkcs1v15_SHA512_256, + .digest_pkcs1v15_SHA3_256, + .digest_pkcs1v15_SHA3_384, + .digest_pkcs1v15_SHA3_512: + return bytes + } + } + + internal func enforceLength(_ bytes: Array, keySizeInBytes: Int) -> Bool { + switch self { + case .raw, .digest_pkcs1v15_RAW: + return bytes.count <= keySizeInBytes + case .digest_pkcs1v15_MD5: + return bytes.count <= 16 + case .digest_pkcs1v15_SHA1: + return bytes.count <= 20 + case .digest_pkcs1v15_SHA224: + return bytes.count <= 28 + case .digest_pkcs1v15_SHA256, .digest_pkcs1v15_SHA3_256: + return bytes.count <= 32 + case .digest_pkcs1v15_SHA384, .digest_pkcs1v15_SHA3_384: + return bytes.count <= 48 + case .digest_pkcs1v15_SHA512, .digest_pkcs1v15_SHA3_512: + return bytes.count <= 64 + case .digest_pkcs1v15_SHA512_224: + return bytes.count <= 28 + case .digest_pkcs1v15_SHA512_256: + return bytes.count <= 32 + case .message_pkcs1v15_MD5, + .message_pkcs1v15_SHA1, + .message_pkcs1v15_SHA224, + .message_pkcs1v15_SHA256, + .message_pkcs1v15_SHA384, + .message_pkcs1v15_SHA512, + .message_pkcs1v15_SHA512_224, + .message_pkcs1v15_SHA512_256, + .message_pkcs1v15_SHA3_256, + .message_pkcs1v15_SHA3_384, + .message_pkcs1v15_SHA3_512: + return true + } + } + + internal func encode(_ bytes: Array) -> Array { + switch self { + case .raw, .digest_pkcs1v15_RAW: + return bytes + + default: + let asn: ASN1.Node = .sequence(nodes: [ + .sequence(nodes: [ + .objectIdentifier(data: Data(self.identifier)), + .null + ]), + .octetString(data: Data(bytes)) + ]) + + return ASN1.Encoder.encode(asn) + } + } + + /// Right now the only Padding Scheme supported is [EMCS-PKCS1v15](https://www.rfc-editor.org/rfc/rfc8017#section-9.2) (others include [EMSA-PSS](https://www.rfc-editor.org/rfc/rfc8017#section-9.1)) + internal func pad(bytes: Array, to blockSize: Int) -> Array { + switch self { + case .raw: + return bytes + default: + return Padding.emsa_pkcs1v15.add(to: bytes, blockSize: blockSize) + } + } + + /// Zero pads a signature to the specified block size + /// - Parameters: + /// - bytes: The signed bytes + /// - blockSize: The block size to pad until + /// - Returns: A zero padded (prepended) bytes array of length blockSize + internal func formatSignedBytes(_ bytes: Array, blockSize: Int) -> Array { + switch self { + default: + // Format the encrypted bytes before returning + return Array(repeating: 0x00, count: blockSize - bytes.count) + bytes + } + } + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/RSA/RSA.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/RSA/RSA.swift new file mode 100644 index 0000000..928bc3d --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/RSA/RSA.swift @@ -0,0 +1,429 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// Foundation is required for `Data` to be found +import Foundation + +// Note: The `BigUInt` struct was copied from: +// https://github.com/attaswift/BigInt +// It allows fast calculation for RSA big numbers + +internal final class RSA: DERCodable { + /// RSA Key Errors + internal enum Error: Swift.Error { + /// No private key specified + case noPrivateKey + /// Failed to calculate the inverse e and phi + case invalidInverseNotCoprimes + /// We only support Version 0 RSA keys (we don't support Version 1 introduced in RFC 3447) + case unsupportedRSAVersion + /// Failed to verify primes during initialiization (the provided primes don't reproduce the provided private exponent) + case invalidPrimes + /// We attempted to export a private key without our underlying primes + case noPrimes + /// Unable to calculate the coefficient during a private key import / export + case unableToCalculateCoefficient + /// The signature to verify is of an invalid length + case invalidSignatureLength + /// The message to be signed is of an invalid length + case invalidMessageLengthForSigning + /// The message to be encrypted is of an invalid length + case invalidMessageLengthForEncryption + /// The error thrown when Decryption fails + case invalidDecryption + } + + /// RSA Modulus + internal let n: BigUInteger + + /// RSA internal Exponent + internal let e: BigUInteger + + /// RSA Private Exponent + internal let d: BigUInteger? + + /// The size of the modulus, in bits + internal let keySize: Int + + /// The size of the modulus, in bytes (rounded up to the nearest full byte) + internal let keySizeBytes: Int + + /// The underlying primes used to generate the Private Exponent + private let primes: (p: BigUInteger, q: BigUInteger)? + + /// Initialize with RSA parameters + /// - Parameters: + /// - n: The RSA Modulus + /// - e: The RSA internal Exponent + /// - d: The RSA Private Exponent (or nil if unknown, e.g. if only internal key is known) + internal init(n: BigUInteger, e: BigUInteger, d: BigUInteger? = nil) { + self.n = n + self.e = e + self.d = d + self.primes = nil + + self.keySize = n.bitWidth + self.keySizeBytes = n.byteWidth + } + + /// Initialize with RSA parameters + /// - Parameters: + /// - n: The RSA Modulus + /// - e: The RSA internal Exponent + /// - d: The RSA Private Exponent (or nil if unknown, e.g. if only internal key is known) + internal convenience init(n: Array, e: Array, d: Array? = nil) { + if let d = d { + self.init(n: BigUInteger(Data(n)), e: BigUInteger(Data(e)), d: BigUInteger(Data(d))) + } else { + self.init(n: BigUInteger(Data(n)), e: BigUInteger(Data(e))) + } + } + + /// Initialize with a generated key pair + /// - Parameter keySize: The size of the modulus + internal convenience init(keySize: Int) throws { + // Generate prime numbers + let p = BigUInteger.generatePrime(keySize / 2) + let q = BigUInteger.generatePrime(keySize / 2) + + // Calculate modulus + let n = p * q + + // Calculate internal and private exponent + let e: BigUInteger = 65537 + let phi = (p - 1) * (q - 1) + guard let d = e.inverse(phi) else { + throw RSA.Error.invalidInverseNotCoprimes + } + + // Initialize + try self.init(n: n, e: e, d: d, p: p, q: q) + } + + /// Initialize with RSA parameters + /// - Parameters: + /// - n: The RSA Modulus + /// - e: The RSA internal Exponent + /// - d: The RSA Private Exponent + /// - p: The 1st Prime used to generate the Private Exponent + /// - q: The 2nd Prime used to generate the Private Exponent + internal init(n: BigUInteger, e: BigUInteger, d: BigUInteger, p: BigUInteger, q: BigUInteger) throws { + // Ensure the supplied parameters are correct... + // Calculate modulus + guard n == p * q else { throw Error.invalidPrimes } + + // Calculate internal and private exponent + let phi = (p - 1) * (q - 1) + guard d == e.inverse(phi) else { throw Error.invalidPrimes } + + // Regular initialization + self.n = n + self.e = e + self.d = d + self.primes = (p, q) + + self.keySize = n.bitWidth + self.keySizeBytes = n.byteWidth + } +} + +// MARK: BigUInt Extension + +internal extension CS.BigUInt { + /// The minimum number of bytes required to represent this integer in binary. + var byteWidth: Int { + let bytes = self.bitWidth / 8 + return self.bitWidth % 8 == 0 ? bytes : bytes + 1 + } +} + +// MARK: DER Initializers (See #892) + +extension RSA { + /// Decodes the provided data into a internal RSA Key + /// + /// [IETF Spec RFC2313](https://datatracker.ietf.org/doc/html/rfc2313#section-7.1) + /// ``` + /// ========================= + /// RSA PublicKey Structure + /// ========================= + /// + /// RSAPublicKey ::= SEQUENCE { + /// modulus INTEGER, -- n + /// publicExponent INTEGER, -- e + /// } + /// ``` + internal convenience init(publicDER der: Array) throws { + let asn = try ASN1.Decoder.decode(data: Data(der)) + + // Enforce the above ASN Structure + guard case .sequence(let params) = asn else { throw DER.Error.invalidDERFormat } + guard params.count == 2 else { throw DER.Error.invalidDERFormat } + + guard case .integer(let modulus) = params[0] else { throw DER.Error.invalidDERFormat } + guard case .integer(let publicExponent) = params[1] else { throw DER.Error.invalidDERFormat } + + self.init(n: BigUInteger(modulus), e: BigUInteger(publicExponent)) + } + + /// Decodes the provided data into a Private RSA Key + /// + /// [IETF Spec RFC2313](https://datatracker.ietf.org/doc/html/rfc2313#section-7.2) + /// ``` + /// ========================== + /// RSA PrivateKey Structure + /// ========================== + /// + /// RSAPrivateKey ::= SEQUENCE { + /// version Version, + /// modulus INTEGER, -- n + /// publicExponent INTEGER, -- e + /// privateExponent INTEGER, -- d + /// prime1 INTEGER, -- p + /// prime2 INTEGER, -- q + /// exponent1 INTEGER, -- d mod (p-1) + /// exponent2 INTEGER, -- d mod (q-1) + /// coefficient INTEGER, -- (inverse of q) mod p + /// } + /// ``` + internal convenience init(privateDER der: Array) throws { + let asn = try ASN1.Decoder.decode(data: Data(der)) + + // Enforce the above ASN Structure (do we need to extract and verify the eponents and coefficients?) + guard case .sequence(let params) = asn else { throw DER.Error.invalidDERFormat } + guard params.count == 9 else { throw DER.Error.invalidDERFormat } + guard case .integer(let version) = params[0] else { throw DER.Error.invalidDERFormat } + guard case .integer(let modulus) = params[1] else { throw DER.Error.invalidDERFormat } + guard case .integer(let publicExponent) = params[2] else { throw DER.Error.invalidDERFormat } + guard case .integer(let privateExponent) = params[3] else { throw DER.Error.invalidDERFormat } + guard case .integer(let prime1) = params[4] else { throw DER.Error.invalidDERFormat } + guard case .integer(let prime2) = params[5] else { throw DER.Error.invalidDERFormat } + guard case .integer(let exponent1) = params[6] else { throw DER.Error.invalidDERFormat } + guard case .integer(let exponent2) = params[7] else { throw DER.Error.invalidDERFormat } + guard case .integer(let coefficient) = params[8] else { throw DER.Error.invalidDERFormat } + + // We only support version 0x00 == RFC2313 at the moment + // - TODO: Support multiple primes 0x01 version defined in [RFC3447](https://www.rfc-editor.org/rfc/rfc3447#appendix-A.1.2) + guard version == Data(hex: "0x00") else { throw Error.unsupportedRSAVersion } + + // Calculate internal and private exponent + let phi = (BigUInteger(prime1) - 1) * (BigUInteger(prime2) - 1) + guard let d = BigUInteger(publicExponent).inverse(phi) else { throw Error.invalidPrimes } + guard BigUInteger(privateExponent) == d else { throw Error.invalidPrimes } + + // Ensure the provided coefficient is correct (derived from the primes) + guard let calculatedCoefficient = BigUInteger(prime2).inverse(BigUInteger(prime1)) else { throw RSA.Error.unableToCalculateCoefficient } + guard calculatedCoefficient == BigUInteger(coefficient) else { throw RSA.Error.invalidPrimes } + + // Ensure the provided exponents are correct as well + guard (d % (BigUInteger(prime1) - 1)) == BigUInteger(exponent1) else { throw RSA.Error.invalidPrimes } + guard (d % (BigUInteger(prime2) - 1)) == BigUInteger(exponent2) else { throw RSA.Error.invalidPrimes } + + // Proceed with regular initialization + try self.init(n: BigUInteger(modulus), e: BigUInteger(publicExponent), d: BigUInteger(privateExponent), p: BigUInteger(prime1), q: BigUInteger(prime2)) + } + + /// Attempts to instantiate an RSA Key when given the ASN1 DER encoded external representation of the Key + /// + /// An example of importing a SecKey RSA key (from Apple's `Security` framework) for use within CryptoSwift + /// ``` + /// /// Starting with a SecKey RSA Key + /// let rsaSecKey:SecKey + /// + /// /// Copy the External Representation + /// var externalRepError:Unmanaged? + /// guard let externalRep = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) as? Data else { + /// /// Failed to copy external representation for RSA SecKey + /// return + /// } + /// + /// /// Instantiate the RSA Key from the raw external representation + /// let rsaKey = try RSA(rawRepresentation: externalRep) + /// + /// /// You now have a CryptoSwift RSA Key + /// // rsaKey.encrypt(...) + /// // rsaKey.decrypt(...) + /// // rsaKey.sign(...) + /// // rsaKey.verify(...) + /// ``` + internal convenience init(rawRepresentation raw: Data) throws { + do { try self.init(privateDER: raw.bytes) } catch { + try self.init(publicDER: raw.bytes) + } + } +} + +// MARK: DER Exports (See #892) + +extension RSA { + /// The DER representation of this internal key + /// + /// [IETF Spec RFC2313](https://datatracker.ietf.org/doc/html/rfc2313#section-7.1) + /// ``` + /// ========================= + /// RSA PublicKey Structure + /// ========================= + /// + /// RSAPublicKey ::= SEQUENCE { + /// modulus INTEGER, -- n + /// publicExponent INTEGER -- e + /// } + /// ``` + func publicKeyDER() throws -> Array { + let mod = self.n.serialize() + let exp = self.e.serialize() + let pubKeyAsnNode: ASN1.Node = + .sequence(nodes: [ + .integer(data: DER.i2ospData(x: mod.bytes, size: self.keySizeBytes)), + .integer(data: DER.i2ospData(x: exp.bytes, size: exp.bytes.count)) + ]) + return ASN1.Encoder.encode(pubKeyAsnNode) + } + + /// The DER representation of this private key + /// + /// [IETF Spec RFC2313](https://datatracker.ietf.org/doc/html/rfc2313#section-7.2) + /// ``` + /// ========================== + /// RSA PrivateKey Structure + /// ========================== + /// + /// RSAPrivateKey ::= SEQUENCE { + /// version Version, + /// modulus INTEGER, -- n + /// publicExponent INTEGER, -- e + /// privateExponent INTEGER, -- d + /// prime1 INTEGER, -- p + /// prime2 INTEGER, -- q + /// exponent1 INTEGER, -- d mod (p-1) + /// exponent2 INTEGER, -- d mod (q-1) + /// coefficient INTEGER, -- (inverse of q) mod p + /// } + /// ``` + func privateKeyDER() throws -> Array { + // Make sure we have a private key + guard let d = d else { throw RSA.Error.noPrivateKey } + // Make sure we have access to our primes + guard let primes = primes else { throw RSA.Error.noPrimes } + // Make sure we can calculate our coefficient (inverse of q mod p) + guard let coefficient = primes.q.inverse(primes.p) else { throw RSA.Error.unableToCalculateCoefficient } + + let paramWidth = self.keySizeBytes / 2 + // Structure the data (according to RFC2313, version 0x00 RSA Private Key Syntax) + let mod = self.n.serialize() + let privateKeyAsnNode: ASN1.Node = + .sequence(nodes: [ + .integer(data: Data(hex: "0x00")), + .integer(data: DER.i2ospData(x: mod.bytes, size: self.keySizeBytes)), + .integer(data: DER.i2ospData(x: self.e.serialize().bytes, size: 3)), + .integer(data: DER.i2ospData(x: d.serialize().bytes, size: self.keySizeBytes)), + .integer(data: DER.i2ospData(x: primes.p.serialize().bytes, size: paramWidth)), + .integer(data: DER.i2ospData(x: primes.q.serialize().bytes, size: paramWidth)), + .integer(data: DER.i2ospData(x: (d % (primes.p - 1)).serialize().bytes, size: paramWidth)), + .integer(data: DER.i2ospData(x: (d % (primes.q - 1)).serialize().bytes, size: paramWidth)), + .integer(data: DER.i2ospData(x: coefficient.serialize().bytes, size: paramWidth)) + ]) + + // Encode and return the data + return ASN1.Encoder.encode(privateKeyAsnNode) + } + + /// This method returns the DER encoding of the RSA Key. + /// + /// - Returns: The ASN1 DER Encoding of the internal or Private RSA Key + /// - Note: If the RSA Key is a private key, the private key representation is returned + /// - Note: If the RSA Key is a internal key, the internal key representation is returned + /// - Note: If you'd like to only export the internal DER of an RSA Key call the `publicKeyExternalRepresentation()` method + /// - Note: This method returns the same data as Apple's `SecKeyCopyExternalRepresentation` method. + /// + /// An example of converting a CryptoSwift RSA key to a SecKey RSA key + /// ``` + /// /// Starting with a CryptoSwift RSA Key + /// let rsaKey = try RSA(keySize: 1024) + /// + /// /// Define your Keys attributes + /// let attributes: [String:Any] = [ + /// kSecAttrKeyType as String: kSecAttrKeyTypeRSA, + /// kSecAttrKeyClass as String: kSecAttrKeyClassPrivate, // or kSecAttrKeyClassPublic + /// kSecAttrKeySizeInBits as String: 1024, // The appropriate bits + /// kSecAttrIsPermanent as String: false + /// ] + /// var error:Unmanaged? = nil + /// guard let rsaSecKey = try SecKeyCreateWithData(rsaKey.externalRepresentation() as CFData, attributes as CFDictionary, &error) else { + /// /// Error constructing SecKey from raw key data + /// return + /// } + /// + /// /// You now have an RSA SecKey for use with Apple's Security framework + /// ``` + /// + /// An example of converting a SecKey RSA key to a CryptoSwift RSA key + /// ``` + /// /// Starting with a SecKey RSA Key + /// let rsaSecKey:SecKey + /// + /// /// Copy External Representation + /// var externalRepError:Unmanaged? + /// guard let cfdata = SecKeyCopyExternalRepresentation(rsaSecKey, &externalRepError) else { + /// /// Failed to copy external representation for RSA SecKey + /// return + /// } + /// + /// /// Instantiate the RSA Key from the raw external representation + /// let rsaKey = try RSA(rawRepresentation: cfdata as Data) + /// + /// /// You now have a CryptoSwift RSA Key + /// ``` + /// + internal func externalRepresentation() throws -> Data { + if self.primes != nil { + return try Data(self.privateKeyDER()) + } else { + return try Data(self.publicKeyDER()) + } + } + + internal func publicKeyExternalRepresentation() throws -> Data { + return try Data(self.publicKeyDER()) + } +} + +// MARK: CS.BigUInt extension + +extension BigUInteger { + + internal static func generatePrime(_ width: Int) -> BigUInteger { + // Note: Need to find a better way to generate prime numbers + while true { + var random = BigUInteger.randomInteger(withExactWidth: width) + random |= BigUInteger(1) + if random.isPrime() { + return random + } + } + } +} + +// MARK: CustomStringConvertible Conformance + +extension RSA: CustomStringConvertible { + internal var description: String { + if self.d != nil { + return "CryptoSwift.RSA.PrivateKey<\(self.keySize)>" + } else { + return "CryptoSwift.RSA.PublicKey<\(self.keySize)>" + } + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Rabbit.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Rabbit.swift new file mode 100644 index 0000000..f837a87 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Rabbit.swift @@ -0,0 +1,221 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal final class Rabbit: BlockCipher { + internal enum Error: Swift.Error { + case invalidKeyOrInitializationVector + } + + /// Size of IV in bytes + internal static let ivSize = 64 / 8 + + /// Size of key in bytes + internal static let keySize = 128 / 8 + + /// Size of block in bytes + internal static let blockSize = 128 / 8 + + internal var keySize: Int { + self.key.count + } + + /// Key + private let key: Key + + /// IV (optional) + private let iv: Array? + + /// State variables + private var x = Array(repeating: 0, count: 8) + + /// Counter variables + private var c = Array(repeating: 0, count: 8) + + /// Counter carry + private var p7: UInt32 = 0 + + /// 'a' constants + private var a: Array = [ + 0x4d34d34d, + 0xd34d34d3, + 0x34d34d34, + 0x4d34d34d, + 0xd34d34d3, + 0x34d34d34, + 0x4d34d34d, + 0xd34d34d3 + ] + + // MARK: - Initializers + + internal convenience init(key: Array) throws { + try self.init(key: key, iv: nil) + } + + internal init(key: Array, iv: Array?) throws { + self.key = Key(bytes: key) + self.iv = iv + + guard key.count == Rabbit.keySize && (iv == nil || iv!.count == Rabbit.ivSize) else { + throw Error.invalidKeyOrInitializationVector + } + } + + // MARK: - + + fileprivate func setup() { + self.p7 = 0 + + // Key divided into 8 subkeys + let k = Array(unsafeUninitializedCapacity: 8) { buf, count in + for j in 0..<8 { + buf[j] = UInt32(self.key[Rabbit.blockSize - (2 * j + 1)]) | (UInt32(self.key[Rabbit.blockSize - (2 * j + 2)]) << 8) + } + count = 8 + } + + // Initialize state and counter variables from subkeys + for j in 0..<8 { + if j % 2 == 0 { + self.x[j] = (k[(j + 1) % 8] << 16) | k[j] + self.c[j] = (k[(j + 4) % 8] << 16) | k[(j + 5) % 8] + } else { + self.x[j] = (k[(j + 5) % 8] << 16) | k[(j + 4) % 8] + self.c[j] = (k[j] << 16) | k[(j + 1) % 8] + } + } + + // Iterate system four times + self.nextState() + self.nextState() + self.nextState() + self.nextState() + + // Reinitialize counter variables + for j in 0..<8 { + self.c[j] = self.c[j] ^ self.x[(j + 4) % 8] + } + + if let iv = iv { + self.setupIV(iv) + } + } + + private func setupIV(_ iv: Array) { + // 63...56 55...48 47...40 39...32 31...24 23...16 15...8 7...0 IV bits + // 0 1 2 3 4 5 6 7 IV bytes in array + let iv0 = UInt32(bytes: [iv[4], iv[5], iv[6], iv[7]]) + let iv1 = UInt32(bytes: [iv[0], iv[1], iv[4], iv[5]]) + let iv2 = UInt32(bytes: [iv[0], iv[1], iv[2], iv[3]]) + let iv3 = UInt32(bytes: [iv[2], iv[3], iv[6], iv[7]]) + + // Modify the counter state as function of the IV + c[0] = self.c[0] ^ iv0 + self.c[1] = self.c[1] ^ iv1 + self.c[2] = self.c[2] ^ iv2 + self.c[3] = self.c[3] ^ iv3 + self.c[4] = self.c[4] ^ iv0 + self.c[5] = self.c[5] ^ iv1 + self.c[6] = self.c[6] ^ iv2 + self.c[7] = self.c[7] ^ iv3 + + // Iterate system four times + self.nextState() + self.nextState() + self.nextState() + self.nextState() + } + + private func nextState() { + // Before an iteration the counters are incremented + var carry = self.p7 + for j in 0..<8 { + let prev = self.c[j] + self.c[j] = prev &+ self.a[j] &+ carry + carry = prev > self.c[j] ? 1 : 0 // detect overflow + } + self.p7 = carry // save last carry bit + + // Iteration of the system + self.x = Array(unsafeUninitializedCapacity: 8) { newX, count in + newX[0] = self.g(0) &+ rotateLeft(self.g(7), by: 16) &+ rotateLeft(self.g(6), by: 16) + newX[1] = self.g(1) &+ rotateLeft(self.g(0), by: 8) &+ self.g(7) + newX[2] = self.g(2) &+ rotateLeft(self.g(1), by: 16) &+ rotateLeft(self.g(0), by: 16) + newX[3] = self.g(3) &+ rotateLeft(self.g(2), by: 8) &+ self.g(1) + newX[4] = self.g(4) &+ rotateLeft(self.g(3), by: 16) &+ rotateLeft(self.g(2), by: 16) + newX[5] = self.g(5) &+ rotateLeft(self.g(4), by: 8) &+ self.g(3) + newX[6] = self.g(6) &+ rotateLeft(self.g(5), by: 16) &+ rotateLeft(self.g(4), by: 16) + newX[7] = self.g(7) &+ rotateLeft(self.g(6), by: 8) &+ self.g(5) + count = 8 + } + } + + private func g(_ j: Int) -> UInt32 { + let sum = self.x[j] &+ self.c[j] + let square = UInt64(sum) * UInt64(sum) + return UInt32(truncatingIfNeeded: square ^ (square >> 32)) + } + + fileprivate func nextOutput() -> Array { + self.nextState() + + var output16 = Array(repeating: 0, count: Rabbit.blockSize / 2) + output16[7] = UInt16(truncatingIfNeeded: self.x[0]) ^ UInt16(truncatingIfNeeded: self.x[5] >> 16) + output16[6] = UInt16(truncatingIfNeeded: self.x[0] >> 16) ^ UInt16(truncatingIfNeeded: self.x[3]) + output16[5] = UInt16(truncatingIfNeeded: self.x[2]) ^ UInt16(truncatingIfNeeded: self.x[7] >> 16) + output16[4] = UInt16(truncatingIfNeeded: self.x[2] >> 16) ^ UInt16(truncatingIfNeeded: self.x[5]) + output16[3] = UInt16(truncatingIfNeeded: self.x[4]) ^ UInt16(truncatingIfNeeded: self.x[1] >> 16) + output16[2] = UInt16(truncatingIfNeeded: self.x[4] >> 16) ^ UInt16(truncatingIfNeeded: self.x[7]) + output16[1] = UInt16(truncatingIfNeeded: self.x[6]) ^ UInt16(truncatingIfNeeded: self.x[3] >> 16) + output16[0] = UInt16(truncatingIfNeeded: self.x[6] >> 16) ^ UInt16(truncatingIfNeeded: self.x[1]) + + var output8 = Array(repeating: 0, count: Rabbit.blockSize) + for j in 0..> 8) + output8[j * 2 + 1] = UInt8(truncatingIfNeeded: output16[j]) + } + return output8 + } +} + +// MARK: Cipher + +extension Rabbit: Cipher { + internal func encrypt(_ bytes: ArraySlice) throws -> Array { + self.setup() + + return Array(unsafeUninitializedCapacity: bytes.count) { result, count in + var output = self.nextOutput() + var byteIdx = 0 + var outputIdx = 0 + while byteIdx < bytes.count { + if outputIdx == Rabbit.blockSize { + output = self.nextOutput() + outputIdx = 0 + } + + result[byteIdx] = bytes[byteIdx] ^ output[outputIdx] + + byteIdx += 1 + outputIdx += 1 + } + count = bytes.count + } + } + + internal func decrypt(_ bytes: ArraySlice) throws -> Array { + try self.encrypt(bytes) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/SHA1.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/SHA1.swift new file mode 100644 index 0000000..7e52826 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/SHA1.swift @@ -0,0 +1,162 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal final class SHA1: DigestType { + + @usableFromInline + static let digestLength: Int = 20 // 160 / 8 + + @usableFromInline + static let blockSize: Int = 64 + + @usableFromInline + static let hashInitialValue: ContiguousArray = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0] + + @usableFromInline + var accumulated = Array() + + @usableFromInline + var processedBytesTotalCount: Int = 0 + + @usableFromInline + var accumulatedHash: ContiguousArray = SHA1.hashInitialValue + + internal init() { + } + + + internal func calculate(for bytes: Array) -> Array { + do { + return try update(withBytes: bytes.slice, isLast: true) + } catch { + return [] + } + } + + internal func callAsFunction(_ bytes: Array) -> Array { + calculate(for: bytes) + } + + @usableFromInline + func process(block chunk: ArraySlice, currentHash hh: inout ContiguousArray) { + // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15, big-endian + // Extend the sixteen 32-bit words into eighty 32-bit words: + let M = UnsafeMutablePointer.allocate(capacity: 80) + M.initialize(repeating: 0, count: 80) + defer { + M.deinitialize(count: 80) + M.deallocate() + } + + for x in 0..<80 { + switch x { + case 0...15: + let start = chunk.startIndex.advanced(by: x * 4) // * MemoryLayout.size + M[x] = UInt32(bytes: chunk, fromIndex: start) + default: + M[x] = rotateLeft(M[x - 3] ^ M[x - 8] ^ M[x - 14] ^ M[x - 16], by: 1) + } + } + + var A = hh[0] + var B = hh[1] + var C = hh[2] + var D = hh[3] + var E = hh[4] + + // Main loop + for j in 0...79 { + var f: UInt32 = 0 + var k: UInt32 = 0 + + switch j { + case 0...19: + f = (B & C) | ((~B) & D) + k = 0x5a827999 + case 20...39: + f = B ^ C ^ D + k = 0x6ed9eba1 + case 40...59: + f = (B & C) | (B & D) | (C & D) + k = 0x8f1bbcdc + case 60...79: + f = B ^ C ^ D + k = 0xca62c1d6 + default: + break + } + + let temp = rotateLeft(A, by: 5) &+ f &+ E &+ M[j] &+ k + E = D + D = C + C = rotateLeft(B, by: 30) + B = A + A = temp + } + + hh[0] = hh[0] &+ A + hh[1] = hh[1] &+ B + hh[2] = hh[2] &+ C + hh[3] = hh[3] &+ D + hh[4] = hh[4] &+ E + } +} + +extension SHA1: Updatable { + @discardableResult + internal func update(withBytes bytes: ArraySlice, isLast: Bool = false) throws -> Array { + self.accumulated += bytes + + if isLast { + let lengthInBits = (processedBytesTotalCount + self.accumulated.count) * 8 + let lengthBytes = lengthInBits.bytes(totalBytes: 64 / 8) // A 64-bit representation of b + + // Step 1. Append padding + bitPadding(to: &self.accumulated, blockSize: SHA1.blockSize, allowance: 64 / 8) + + // Step 2. Append Length a 64-bit representation of lengthInBits + self.accumulated += lengthBytes + } + + var processedBytes = 0 + for chunk in self.accumulated.batched(by: SHA1.blockSize) { + if isLast || (self.accumulated.count - processedBytes) >= SHA1.blockSize { + self.process(block: chunk, currentHash: &self.accumulatedHash) + processedBytes += chunk.count + } + } + self.accumulated.removeFirst(processedBytes) + self.processedBytesTotalCount += processedBytes + + // output current hash + var result = Array(repeating: 0, count: SHA1.digestLength) + var pos = 0 + for idx in 0..> 24) & 0xff) + result[pos + 1] = UInt8((h >> 16) & 0xff) + result[pos + 2] = UInt8((h >> 8) & 0xff) + result[pos + 3] = UInt8(h & 0xff) + pos += 4 + } + + // reset hash value for instance + if isLast { + self.accumulatedHash = SHA1.hashInitialValue + } + + return result + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/SHA2.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/SHA2.swift new file mode 100644 index 0000000..0ece565 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/SHA2.swift @@ -0,0 +1,372 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// TODO: generic for process32/64 (UInt32/UInt64) +// + +internal final class SHA2: DigestType { + @usableFromInline + let variant: Variant + + @usableFromInline + let size: Int + + @usableFromInline + let blockSize: Int + + @usableFromInline + let digestLength: Int + + private let k: Array + + @usableFromInline + var accumulated = Array() + + @usableFromInline + var processedBytesTotalCount: Int = 0 + + @usableFromInline + var accumulatedHash32 = Array() + + @usableFromInline + var accumulatedHash64 = Array() + + @frozen + internal enum Variant: RawRepresentable { + case sha224, sha256, sha384, sha512 + + internal var digestLength: Int { + self.rawValue / 8 + } + + internal var blockSize: Int { + switch self { + case .sha224, .sha256: + return 64 + case .sha384, .sha512: + return 128 + } + } + + internal typealias RawValue = Int + internal var rawValue: RawValue { + switch self { + case .sha224: + return 224 + case .sha256: + return 256 + case .sha384: + return 384 + case .sha512: + return 512 + } + } + + internal init?(rawValue: RawValue) { + switch rawValue { + case 224: + self = .sha224 + case 256: + self = .sha256 + case 384: + self = .sha384 + case 512: + self = .sha512 + default: + return nil + } + } + + @usableFromInline + var h: Array { + switch self { + case .sha224: + return [0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4] + case .sha256: + return [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19] + case .sha384: + return [0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939, 0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4] + case .sha512: + return [0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179] + } + } + + @usableFromInline + var finalLength: Int { + switch self { + case .sha224: + return 7 + case .sha384: + return 6 + default: + return Int.max + } + } + } + + internal init(variant: SHA2.Variant) { + self.variant = variant + switch self.variant { + case .sha224, .sha256: + self.accumulatedHash32 = variant.h.map { UInt32($0) } // FIXME: UInt64 for process64 + self.blockSize = variant.blockSize + self.size = variant.rawValue + self.digestLength = variant.digestLength + self.k = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + ] + case .sha384, .sha512: + self.accumulatedHash64 = variant.h + self.blockSize = variant.blockSize + self.size = variant.rawValue + self.digestLength = variant.digestLength + self.k = [ + 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 0x3956c25bf348b538, + 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x12835b0145706fbe, + 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, + 0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, + 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 0x983e5152ee66dfab, + 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725, + 0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, + 0x53380d139d95b3df, 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, + 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218, + 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, + 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, + 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, + 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 0xca273eceea26619c, + 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, 0x0a637dc5a2c898a6, + 0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, + 0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 + ] + } + } + + + internal func calculate(for bytes: Array) -> Array { + do { + return try update(withBytes: bytes.slice, isLast: true) + } catch { + return [] + } + } + + internal func callAsFunction(_ bytes: Array) -> Array { + calculate(for: bytes) + } + + @usableFromInline + func process64(block chunk: ArraySlice, currentHash hh: inout Array) { + // break chunk into sixteen 64-bit words M[j], 0 ≤ j ≤ 15, big-endian + // Extend the sixteen 64-bit words into eighty 64-bit words: + let M = UnsafeMutablePointer.allocate(capacity: self.k.count) + M.initialize(repeating: 0, count: self.k.count) + defer { + M.deinitialize(count: self.k.count) + M.deallocate() + } + for x in 0...size + M[x] = UInt64(bytes: chunk, fromIndex: start) + default: + let s0 = rotateRight(M[x - 15], by: 1) ^ rotateRight(M[x - 15], by: 8) ^ (M[x - 15] >> 7) + let s1 = rotateRight(M[x - 2], by: 19) ^ rotateRight(M[x - 2], by: 61) ^ (M[x - 2] >> 6) + M[x] = M[x - 16] &+ s0 &+ M[x - 7] &+ s1 + } + } + + var A = hh[0] + var B = hh[1] + var C = hh[2] + var D = hh[3] + var E = hh[4] + var F = hh[5] + var G = hh[6] + var H = hh[7] + + // Main loop + for j in 0.., currentHash hh: inout Array) { + // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15, big-endian + // Extend the sixteen 32-bit words into sixty-four 32-bit words: + let M = UnsafeMutablePointer.allocate(capacity: self.k.count) + M.initialize(repeating: 0, count: self.k.count) + defer { + M.deinitialize(count: self.k.count) + M.deallocate() + } + + for x in 0...size + M[x] = UInt32(bytes: chunk, fromIndex: start) + default: + let s0 = rotateRight(M[x - 15], by: 7) ^ rotateRight(M[x - 15], by: 18) ^ (M[x - 15] >> 3) + let s1 = rotateRight(M[x - 2], by: 17) ^ rotateRight(M[x - 2], by: 19) ^ (M[x - 2] >> 10) + M[x] = M[x - 16] &+ s0 &+ M[x - 7] &+ s1 + } + } + + var A = hh[0] + var B = hh[1] + var C = hh[2] + var D = hh[3] + var E = hh[4] + var F = hh[5] + var G = hh[6] + var H = hh[7] + + // Main loop + for j in 0.., isLast: Bool = false) throws -> Array { + self.accumulated += bytes + + if isLast { + let lengthInBits = (processedBytesTotalCount + self.accumulated.count) * 8 + let lengthBytes = lengthInBits.bytes(totalBytes: self.blockSize / 8) // A 64-bit/128-bit representation of b. blockSize fit by accident. + + // Step 1. Append padding + bitPadding(to: &self.accumulated, blockSize: self.blockSize, allowance: self.blockSize / 8) + + // Step 2. Append Length a 64-bit representation of lengthInBits + self.accumulated += lengthBytes + } + + var processedBytes = 0 + for chunk in self.accumulated.batched(by: self.blockSize) { + if isLast || (self.accumulated.count - processedBytes) >= self.blockSize { + switch self.variant { + case .sha224, .sha256: + self.process32(block: chunk, currentHash: &self.accumulatedHash32) + case .sha384, .sha512: + self.process64(block: chunk, currentHash: &self.accumulatedHash64) + } + processedBytes += chunk.count + } + } + self.accumulated.removeFirst(processedBytes) + self.processedBytesTotalCount += processedBytes + + // output current hash + var result = Array(repeating: 0, count: variant.digestLength) + switch self.variant { + case .sha224, .sha256: + var pos = 0 + for idx in 0..> 24) & 0xff) + result[pos + 1] = UInt8((h >> 16) & 0xff) + result[pos + 2] = UInt8((h >> 8) & 0xff) + result[pos + 3] = UInt8(h & 0xff) + pos += 4 + } + case .sha384, .sha512: + var pos = 0 + for idx in 0..> 56) & 0xff) + result[pos + 1] = UInt8((h >> 48) & 0xff) + result[pos + 2] = UInt8((h >> 40) & 0xff) + result[pos + 3] = UInt8((h >> 32) & 0xff) + result[pos + 4] = UInt8((h >> 24) & 0xff) + result[pos + 5] = UInt8((h >> 16) & 0xff) + result[pos + 6] = UInt8((h >> 8) & 0xff) + result[pos + 7] = UInt8(h & 0xff) + pos += 8 + } + } + + // reset hash value for instance + if isLast { + switch self.variant { + case .sha224, .sha256: + self.accumulatedHash32 = self.variant.h.lazy.map { UInt32($0) } // FIXME: UInt64 for process64 + case .sha384, .sha512: + self.accumulatedHash64 = self.variant.h + } + } + + return result + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/SHA3.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/SHA3.swift new file mode 100644 index 0000000..b562acf --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/SHA3.swift @@ -0,0 +1,303 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf +// http://keccak.noekeon.org/specs_summary.html +// + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#elseif canImport(ucrt) +import ucrt +#endif + +internal final class SHA3: DigestType { + let round_constants: Array = [ + 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000, + 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, + 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, + 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003, + 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a, + 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 + ] + + internal let blockSize: Int + internal let digestLength: Int + internal let markByte: UInt8 + + @usableFromInline + var accumulated = Array() + + + @usableFromInline + var accumulatedHash: Array + + internal enum Variant { + case sha224, sha256, sha384, sha512, keccak224, keccak256, keccak384, keccak512 + + var digestLength: Int { + 100 - (self.blockSize / 2) + } + + var blockSize: Int { + (1600 - self.outputLength * 2) / 8 + } + + var markByte: UInt8 { + switch self { + case .sha224, .sha256, .sha384, .sha512: + return 0x06 // 0x1F for SHAKE + case .keccak224, .keccak256, .keccak384, .keccak512: + return 0x01 + } + } + + internal var outputLength: Int { + switch self { + case .sha224, .keccak224: + return 224 + case .sha256, .keccak256: + return 256 + case .sha384, .keccak384: + return 384 + case .sha512, .keccak512: + return 512 + } + } + } + + internal init(variant: SHA3.Variant) { + self.blockSize = variant.blockSize + self.digestLength = variant.digestLength + self.markByte = variant.markByte + self.accumulatedHash = Array(repeating: 0, count: self.digestLength) + } + + + internal func calculate(for bytes: Array) -> Array { + do { + return try update(withBytes: bytes.slice, isLast: true) + } catch { + return [] + } + } + + internal func callAsFunction(_ bytes: Array) -> Array { + calculate(for: bytes) + } + + /// 1. For all pairs (x,z) such that 0≤x<5 and 0≤z) { + let c = UnsafeMutablePointer.allocate(capacity: 5) + c.initialize(repeating: 0, count: 5) + defer { + c.deinitialize(count: 5) + c.deallocate() + } + let d = UnsafeMutablePointer.allocate(capacity: 5) + d.initialize(repeating: 0, count: 5) + defer { + d.deinitialize(count: 5) + d.deallocate() + } + + for i in 0..<5 { + c[i] = a[i] ^ a[i &+ 5] ^ a[i &+ 10] ^ a[i &+ 15] ^ a[i &+ 20] + } + + d[0] = rotateLeft(c[1], by: 1) ^ c[4] + d[1] = rotateLeft(c[2], by: 1) ^ c[0] + d[2] = rotateLeft(c[3], by: 1) ^ c[1] + d[3] = rotateLeft(c[4], by: 1) ^ c[2] + d[4] = rotateLeft(c[0], by: 1) ^ c[3] + + for i in 0..<5 { + a[i] ^= d[i] + a[i &+ 5] ^= d[i] + a[i &+ 10] ^= d[i] + a[i &+ 15] ^= d[i] + a[i &+ 20] ^= d[i] + } + } + + /// A′[x, y, z]=A[(x &+ 3y) mod 5, x, z] + private func π(_ a: inout Array) { + let a1 = a[1] + a[1] = a[6] + a[6] = a[9] + a[9] = a[22] + a[22] = a[14] + a[14] = a[20] + a[20] = a[2] + a[2] = a[12] + a[12] = a[13] + a[13] = a[19] + a[19] = a[23] + a[23] = a[15] + a[15] = a[4] + a[4] = a[24] + a[24] = a[21] + a[21] = a[8] + a[8] = a[16] + a[16] = a[5] + a[5] = a[3] + a[3] = a[18] + a[18] = a[17] + a[17] = a[11] + a[11] = a[7] + a[7] = a[10] + a[10] = a1 + } + + /// For all triples (x, y, z) such that 0≤x<5, 0≤y<5, and 0≤z) { + for i in stride(from: 0, to: 25, by: 5) { + let a0 = a[0 &+ i] + let a1 = a[1 &+ i] + a[0 &+ i] ^= ~a1 & a[2 &+ i] + a[1 &+ i] ^= ~a[2 &+ i] & a[3 &+ i] + a[2 &+ i] ^= ~a[3 &+ i] & a[4 &+ i] + a[3 &+ i] ^= ~a[4 &+ i] & a0 + a[4 &+ i] ^= ~a0 & a1 + } + } + + private func ι(_ a: inout Array, round: Int) { + a[0] ^= self.round_constants[round] + } + + @usableFromInline + func process(block chunk: ArraySlice, currentHash hh: inout Array) { + // expand + hh[0] ^= chunk[0].littleEndian + hh[1] ^= chunk[1].littleEndian + hh[2] ^= chunk[2].littleEndian + hh[3] ^= chunk[3].littleEndian + hh[4] ^= chunk[4].littleEndian + hh[5] ^= chunk[5].littleEndian + hh[6] ^= chunk[6].littleEndian + hh[7] ^= chunk[7].littleEndian + hh[8] ^= chunk[8].littleEndian + if self.blockSize > 72 { // 72 / 8, sha-512 + hh[9] ^= chunk[9].littleEndian + hh[10] ^= chunk[10].littleEndian + hh[11] ^= chunk[11].littleEndian + hh[12] ^= chunk[12].littleEndian + if self.blockSize > 104 { // 104 / 8, sha-384 + hh[13] ^= chunk[13].littleEndian + hh[14] ^= chunk[14].littleEndian + hh[15] ^= chunk[15].littleEndian + hh[16] ^= chunk[16].littleEndian + if self.blockSize > 136 { // 136 / 8, sha-256 + hh[17] ^= chunk[17].littleEndian + // FULL_SHA3_FAMILY_SUPPORT + if self.blockSize > 144 { // 144 / 8, sha-224 + hh[18] ^= chunk[18].littleEndian + hh[19] ^= chunk[19].littleEndian + hh[20] ^= chunk[20].littleEndian + hh[21] ^= chunk[21].littleEndian + hh[22] ^= chunk[22].littleEndian + hh[23] ^= chunk[23].littleEndian + hh[24] ^= chunk[24].littleEndian + } + } + } + } + + // Keccak-f + for round in 0..<24 { + self.θ(&hh) + + hh[1] = rotateLeft(hh[1], by: 1) + hh[2] = rotateLeft(hh[2], by: 62) + hh[3] = rotateLeft(hh[3], by: 28) + hh[4] = rotateLeft(hh[4], by: 27) + hh[5] = rotateLeft(hh[5], by: 36) + hh[6] = rotateLeft(hh[6], by: 44) + hh[7] = rotateLeft(hh[7], by: 6) + hh[8] = rotateLeft(hh[8], by: 55) + hh[9] = rotateLeft(hh[9], by: 20) + hh[10] = rotateLeft(hh[10], by: 3) + hh[11] = rotateLeft(hh[11], by: 10) + hh[12] = rotateLeft(hh[12], by: 43) + hh[13] = rotateLeft(hh[13], by: 25) + hh[14] = rotateLeft(hh[14], by: 39) + hh[15] = rotateLeft(hh[15], by: 41) + hh[16] = rotateLeft(hh[16], by: 45) + hh[17] = rotateLeft(hh[17], by: 15) + hh[18] = rotateLeft(hh[18], by: 21) + hh[19] = rotateLeft(hh[19], by: 8) + hh[20] = rotateLeft(hh[20], by: 18) + hh[21] = rotateLeft(hh[21], by: 2) + hh[22] = rotateLeft(hh[22], by: 61) + hh[23] = rotateLeft(hh[23], by: 56) + hh[24] = rotateLeft(hh[24], by: 14) + + self.π(&hh) + self.χ(&hh) + self.ι(&hh, round: round) + } + } +} + +extension SHA3: Updatable { + + + internal func update(withBytes bytes: ArraySlice, isLast: Bool = false) throws -> Array { + self.accumulated += bytes + + if isLast { + // Add padding + let markByteIndex = self.accumulated.count + + // We need to always pad the input. Even if the input is a multiple of blockSize. + let r = self.blockSize * 8 + let q = (r / 8) - (accumulated.count % (r / 8)) + self.accumulated += Array(repeating: 0, count: q) + + self.accumulated[markByteIndex] |= self.markByte + self.accumulated[self.accumulated.count - 1] |= 0x80 + } + + var processedBytes = 0 + for chunk in self.accumulated.batched(by: self.blockSize) { + if isLast || (self.accumulated.count - processedBytes) >= self.blockSize { + self.process(block: chunk.toUInt64Array().slice, currentHash: &self.accumulatedHash) + processedBytes += chunk.count + } + } + self.accumulated.removeFirst(processedBytes) + + // TODO: verify performance, reduce vs for..in + let result = self.accumulatedHash.reduce(into: Array()) { (result, value) in + result += value.bigEndian.bytes() + } + + // reset hash value for instance + if isLast { + self.accumulatedHash = Array(repeating: 0, count: self.digestLength) + } + + return Array(result[0.. +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +// +// https://tools.ietf.org/html/rfc7914 +// + +/// Implementation of the scrypt key derivation function. +internal final class Scrypt { + enum Error: Swift.Error { + case nIsTooLarge + case rIsTooLarge + case nMustBeAPowerOf2GreaterThan1 + case invalidInput + } + + /// Configuration parameters. + private let salt: SecureBytes + private let password: SecureBytes + private let blocksize: Int // 128 * r + private let salsaBlock = UnsafeMutableRawPointer.allocate(byteCount: 64, alignment: 64) + private let dkLen: Int + private let N: Int + private let r: Int + private let p: Int + + /// - parameters: + /// - password: password + /// - salt: salt + /// - dkLen: output length + /// - N: determines extra memory used + /// - r: determines a block size + /// - p: determines parallelicity degree + internal init(password: Array, salt: Array, dkLen: Int, N: Int, r: Int, p: Int) throws { + precondition(dkLen > 0) + precondition(N > 0) + precondition(r > 0) + precondition(p > 0) + + guard !(N < 2 || (N & (N - 1)) != 0) else { throw Error.nMustBeAPowerOf2GreaterThan1 } + + guard N <= .max / 128 / r else { throw Error.nIsTooLarge } + guard r <= .max / 128 / p else { throw Error.rIsTooLarge } + + guard !salt.isEmpty else { + throw Error.invalidInput + } + + self.blocksize = 128 * r + self.N = N + self.r = r + self.p = p + self.password = SecureBytes(bytes: password) + self.salt = SecureBytes(bytes: salt) + self.dkLen = dkLen + } + + /// Runs the key derivation function with a specific password. + internal func calculate() throws -> [UInt8] { + // Allocate memory (as bytes for now) for further use in mixing steps + let B = UnsafeMutableRawPointer.allocate(byteCount: 128 * self.r * self.p, alignment: 64) + let XY = UnsafeMutableRawPointer.allocate(byteCount: 256 * self.r + 64, alignment: 64) + let V = UnsafeMutableRawPointer.allocate(byteCount: 128 * self.r * self.N, alignment: 64) + + // Deallocate memory when done + defer { + B.deallocate() + XY.deallocate() + V.deallocate() + } + + /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ + // Expand the initial key + let barray = try PKCS5.PBKDF2(password: Array(self.password), salt: Array(self.salt), iterations: 1, keyLength: self.p * 128 * self.r, variant: .sha2(.sha256)).calculate() + barray.withUnsafeBytes { p in + B.copyMemory(from: p.baseAddress!, byteCount: barray.count) + } + + /* 2: for i = 0 to p - 1 do */ + // do the mixing + for i in 0 ..< self.p { + /* 3: B_i <-- MF(B_i, N) */ + smix(B + i * 128 * self.r, V.assumingMemoryBound(to: UInt32.self), XY.assumingMemoryBound(to: UInt32.self)) + } + + /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ + let pointer = B.assumingMemoryBound(to: UInt8.self) + let bufferPointer = UnsafeBufferPointer(start: pointer, count: p * 128 * self.r) + let block = [UInt8](bufferPointer) + return try PKCS5.PBKDF2(password: Array(self.password), salt: block, iterations: 1, keyLength: self.dkLen, variant: .sha2(.sha256)).calculate() + } + + internal func callAsFunction() throws -> Array { + try calculate() + } + +} + +private extension Scrypt { + /// Computes `B = SMix_r(B, N)`. + /// + /// The input `block` must be `128*r` bytes in length; the temporary storage `v` must be `128*r*n` bytes in length; + /// the temporary storage `xy` must be `256*r + 64` bytes in length. The arrays `block`, `v`, and `xy` must be + /// aligned to a multiple of 64 bytes. + @inline(__always) func smix(_ block: UnsafeMutableRawPointer, _ v: UnsafeMutablePointer, _ xy: UnsafeMutablePointer) { + let X = xy + let Y = xy + 32 * self.r + let Z = xy + 64 * self.r + + /* 1: X <-- B */ + let typedBlock = block.assumingMemoryBound(to: UInt32.self) +#if compiler(>=5.8) + X.update(from: typedBlock, count: 32 * self.r) +#else + X.assign(from: typedBlock, count: 32 * self.r) +#endif + + /* 2: for i = 0 to N - 1 do */ + for i in stride(from: 0, to: self.N, by: 2) { + /* 3: V_i <-- X */ + UnsafeMutableRawPointer(v + i * (32 * self.r)).copyMemory(from: X, byteCount: 128 * self.r) + + /* 4: X <-- H(X) */ + self.blockMixSalsa8(X, Y, Z) + + /* 3: V_i <-- X */ + UnsafeMutableRawPointer(v + (i + 1) * (32 * self.r)).copyMemory(from: Y, byteCount: 128 * self.r) + + /* 4: X <-- H(X) */ + self.blockMixSalsa8(Y, X, Z) + } + + /* 6: for i = 0 to N - 1 do */ + for _ in stride(from: 0, to: self.N, by: 2) { + /* + 7: j <-- Integerify (X) mod N + where Integerify (B[0] ... B[2 * r - 1]) is defined + as the result of interpreting B[2 * r - 1] as a little-endian integer. + */ + var j = Int(integerify(X) & UInt64(self.N - 1)) + + /* 8: X <-- H(X \xor V_j) */ + self.blockXor(X, v + j * 32 * self.r, 128 * self.r) + self.blockMixSalsa8(X, Y, Z) + + /* 7: j <-- Integerify(X) mod N */ + j = Int(self.integerify(Y) & UInt64(self.N - 1)) + + /* 8: X <-- H(X \xor V_j) */ + self.blockXor(Y, v + j * 32 * self.r, 128 * self.r) + self.blockMixSalsa8(Y, X, Z) + } + + /* 10: B' <-- X */ + for k in 0 ..< 32 * self.r { + UnsafeMutableRawPointer(block + 4 * k).storeBytes(of: X[k], as: UInt32.self) + } + } + + /// Returns the result of parsing `B_{2r-1}` as a little-endian integer. + @inline(__always) func integerify(_ block: UnsafeRawPointer) -> UInt64 { + let bi = block + (2 * self.r - 1) * 64 + return bi.load(as: UInt64.self).littleEndian + } + + /// Compute `bout = BlockMix_{salsa20/8, r}(bin)`. + /// + /// The input `bin` must be `128*r` bytes in length; the output `bout` must also be the same size. The temporary + /// space `x` must be 64 bytes. + @inline(__always) func blockMixSalsa8(_ bin: UnsafePointer, _ bout: UnsafeMutablePointer, _ x: UnsafeMutablePointer) { + /* 1: X <-- B_{2r - 1} */ + UnsafeMutableRawPointer(x).copyMemory(from: bin + (2 * self.r - 1) * 16, byteCount: 64) + + /* 2: for i = 0 to 2r - 1 do */ + for i in stride(from: 0, to: 2 * self.r, by: 2) { + /* 3: X <-- H(X \xor B_i) */ + self.blockXor(x, bin + i * 16, 64) + self.salsa20_8_typed(x) + + /* 4: Y_i <-- X */ + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + UnsafeMutableRawPointer(bout + i * 8).copyMemory(from: x, byteCount: 64) + + /* 3: X <-- H(X \xor B_i) */ + self.blockXor(x, bin + i * 16 + 16, 64) + self.salsa20_8_typed(x) + + /* 4: Y_i <-- X */ + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + UnsafeMutableRawPointer(bout + i * 8 + self.r * 16).copyMemory(from: x, byteCount: 64) + } + } + + @inline(__always) func salsa20_8_typed(_ block: UnsafeMutablePointer) { + self.salsaBlock.copyMemory(from: UnsafeRawPointer(block), byteCount: 64) + let salsaBlockTyped = self.salsaBlock.assumingMemoryBound(to: UInt32.self) + + for _ in stride(from: 0, to: 8, by: 2) { + salsaBlockTyped[4] ^= rotateLeft(salsaBlockTyped[0] &+ salsaBlockTyped[12], by: 7) + salsaBlockTyped[8] ^= rotateLeft(salsaBlockTyped[4] &+ salsaBlockTyped[0], by: 9) + salsaBlockTyped[12] ^= rotateLeft(salsaBlockTyped[8] &+ salsaBlockTyped[4], by: 13) + salsaBlockTyped[0] ^= rotateLeft(salsaBlockTyped[12] &+ salsaBlockTyped[8], by: 18) + + salsaBlockTyped[9] ^= rotateLeft(salsaBlockTyped[5] &+ salsaBlockTyped[1], by: 7) + salsaBlockTyped[13] ^= rotateLeft(salsaBlockTyped[9] &+ salsaBlockTyped[5], by: 9) + salsaBlockTyped[1] ^= rotateLeft(salsaBlockTyped[13] &+ salsaBlockTyped[9], by: 13) + salsaBlockTyped[5] ^= rotateLeft(salsaBlockTyped[1] &+ salsaBlockTyped[13], by: 18) + + salsaBlockTyped[14] ^= rotateLeft(salsaBlockTyped[10] &+ salsaBlockTyped[6], by: 7) + salsaBlockTyped[2] ^= rotateLeft(salsaBlockTyped[14] &+ salsaBlockTyped[10], by: 9) + salsaBlockTyped[6] ^= rotateLeft(salsaBlockTyped[2] &+ salsaBlockTyped[14], by: 13) + salsaBlockTyped[10] ^= rotateLeft(salsaBlockTyped[6] &+ salsaBlockTyped[2], by: 18) + + salsaBlockTyped[3] ^= rotateLeft(salsaBlockTyped[15] &+ salsaBlockTyped[11], by: 7) + salsaBlockTyped[7] ^= rotateLeft(salsaBlockTyped[3] &+ salsaBlockTyped[15], by: 9) + salsaBlockTyped[11] ^= rotateLeft(salsaBlockTyped[7] &+ salsaBlockTyped[3], by: 13) + salsaBlockTyped[15] ^= rotateLeft(salsaBlockTyped[11] &+ salsaBlockTyped[7], by: 18) + + salsaBlockTyped[1] ^= rotateLeft(salsaBlockTyped[0] &+ salsaBlockTyped[3], by: 7) + salsaBlockTyped[2] ^= rotateLeft(salsaBlockTyped[1] &+ salsaBlockTyped[0], by: 9) + salsaBlockTyped[3] ^= rotateLeft(salsaBlockTyped[2] &+ salsaBlockTyped[1], by: 13) + salsaBlockTyped[0] ^= rotateLeft(salsaBlockTyped[3] &+ salsaBlockTyped[2], by: 18) + + salsaBlockTyped[6] ^= rotateLeft(salsaBlockTyped[5] &+ salsaBlockTyped[4], by: 7) + salsaBlockTyped[7] ^= rotateLeft(salsaBlockTyped[6] &+ salsaBlockTyped[5], by: 9) + salsaBlockTyped[4] ^= rotateLeft(salsaBlockTyped[7] &+ salsaBlockTyped[6], by: 13) + salsaBlockTyped[5] ^= rotateLeft(salsaBlockTyped[4] &+ salsaBlockTyped[7], by: 18) + + salsaBlockTyped[11] ^= rotateLeft(salsaBlockTyped[10] &+ salsaBlockTyped[9], by: 7) + salsaBlockTyped[8] ^= rotateLeft(salsaBlockTyped[11] &+ salsaBlockTyped[10], by: 9) + salsaBlockTyped[9] ^= rotateLeft(salsaBlockTyped[8] &+ salsaBlockTyped[11], by: 13) + salsaBlockTyped[10] ^= rotateLeft(salsaBlockTyped[9] &+ salsaBlockTyped[8], by: 18) + + salsaBlockTyped[12] ^= rotateLeft(salsaBlockTyped[15] &+ salsaBlockTyped[14], by: 7) + salsaBlockTyped[13] ^= rotateLeft(salsaBlockTyped[12] &+ salsaBlockTyped[15], by: 9) + salsaBlockTyped[14] ^= rotateLeft(salsaBlockTyped[13] &+ salsaBlockTyped[12], by: 13) + salsaBlockTyped[15] ^= rotateLeft(salsaBlockTyped[14] &+ salsaBlockTyped[13], by: 18) + } + for i in 0 ..< 16 { + block[i] = block[i] &+ salsaBlockTyped[i] + } + } + + @inline(__always) func blockXor(_ dest: UnsafeMutableRawPointer, _ src: UnsafeRawPointer, _ len: Int) { + let D = dest.assumingMemoryBound(to: UInt64.self) + let S = src.assumingMemoryBound(to: UInt64.self) + let L = len / MemoryLayout.size + + for i in 0 ..< L { + D[i] ^= S[i] + } + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/SecureBytes.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/SecureBytes.swift new file mode 100644 index 0000000..38e5749 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/SecureBytes.swift @@ -0,0 +1,91 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#elseif canImport(WinSDK) +import WinSDK +#endif + +typealias Key = SecureBytes + +/// Keeps bytes in memory. Because this is class, bytes are not copied +/// and memory area is locked as long as referenced, then unlocked on deinit +final class SecureBytes { + private let bytes: Array + let count: Int + + init(bytes: Array) { + self.bytes = bytes + self.count = bytes.count + self.bytes.withUnsafeBufferPointer { (pointer) -> Void in + #if os(Windows) + VirtualLock(UnsafeMutableRawPointer(mutating: pointer.baseAddress), SIZE_T(pointer.count)) + #elseif os(WASI) + // not supported on WASI + #else + mlock(pointer.baseAddress, pointer.count) + #endif + } + } + + deinit { + self.bytes.withUnsafeBufferPointer { (pointer) -> Void in + #if os(Windows) + VirtualUnlock(UnsafeMutableRawPointer(mutating: pointer.baseAddress), SIZE_T(pointer.count)) + #elseif os(WASI) + // not supported on WASI + #else + munlock(pointer.baseAddress, pointer.count) + #endif + } + } +} + +extension SecureBytes: Collection { + typealias Index = Int + + var endIndex: Int { + self.bytes.endIndex + } + + var startIndex: Int { + self.bytes.startIndex + } + + subscript(position: Index) -> UInt8 { + self.bytes[position] + } + + subscript(bounds: Range) -> ArraySlice { + self.bytes[bounds] + } + + func formIndex(after i: inout Int) { + self.bytes.formIndex(after: &i) + } + + func index(after i: Int) -> Int { + self.bytes.index(after: i) + } +} + +extension SecureBytes: ExpressibleByArrayLiteral { + internal convenience init(arrayLiteral elements: UInt8...) { + self.init(bytes: elements) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Signature.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Signature.swift new file mode 100644 index 0000000..7648667 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Signature.swift @@ -0,0 +1,66 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +internal enum SignatureError: Error { + case sign + case verify +} + +internal protocol Signature: AnyObject { + var keySize: Int { get } + + /// Sign the given bytes at once + /// + /// - parameter bytes: Plaintext data to be signed + /// - returns: The signed data + func sign(_ bytes: ArraySlice) throws -> Array + /// Sign the given bytes at once + /// + /// - parameter bytes: Plaintext data to be signed + /// - returns: The signed data + func sign(_ bytes: Array) throws -> Array + + /// Verify the given bytes against the expected data + /// + /// - parameter signature: Signature data + /// - parameter expectedData: The original data that you expected to be signed + /// - returns: `True` when the signature is valid for the expected data, `False` otherwise. + func verify(signature: ArraySlice, for expectedData: ArraySlice) throws -> Bool + /// Verify the given bytes against the expected data + /// + /// - parameter signature: Signature data + /// - parameter expectedData: The original data that you expected to be signed + /// - returns: `True` when the signature is valid for the expected data, `False` otherwise. + func verify(signature: Array, for expectedData: Array) throws -> Bool +} + +extension Signature { + /// Sign the given bytes at once + /// + /// - parameter bytes: Plaintext data to be signed + /// - returns: The signed data + internal func sign(_ bytes: Array) throws -> Array { + try self.sign(bytes.slice) + } + + /// Verify the given bytes against the expected data + /// + /// - parameter signature: Signature data + /// - parameter expectedData: The original data that you expected to be signed + /// - returns: `True` when the signature is valid for the expected data, `False` otherwise. + internal func verify(signature: Array, for expectedData: Array) throws -> Bool { + try self.verify(signature: signature.slice, for: expectedData.slice) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/StreamDecryptor.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/StreamDecryptor.swift new file mode 100644 index 0000000..323bb8d --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/StreamDecryptor.swift @@ -0,0 +1,97 @@ +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +@usableFromInline +final class StreamDecryptor: Cryptor, Updatable { + + @usableFromInline + enum Error: Swift.Error { + case unsupported + } + + @usableFromInline + internal let blockSize: Int + + @usableFromInline + internal var worker: CipherModeWorker + + @usableFromInline + internal let padding: Padding + + @usableFromInline + internal var accumulated = Array() + + @usableFromInline + internal var lastBlockRemainder = 0 + + @usableFromInline + init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws { + self.blockSize = blockSize + self.padding = padding + self.worker = worker + } + + // MARK: Updatable + + + internal func update(withBytes bytes: ArraySlice, isLast: Bool) throws -> Array { + self.accumulated += bytes + + let toProcess = self.accumulated.prefix(max(self.accumulated.count - self.worker.additionalBufferSize, 0)) + + if var finalizingWorker = worker as? FinalizingDecryptModeWorker, isLast == true { + // will truncate suffix if needed + try finalizingWorker.willDecryptLast(bytes: self.accumulated.slice) + } + + var processedBytesCount = 0 + var plaintext = Array(reserveCapacity: bytes.count + self.worker.additionalBufferSize) + for chunk in toProcess.batched(by: self.blockSize) { + plaintext += self.worker.decrypt(block: chunk) + processedBytesCount += chunk.count + } + + if var finalizingWorker = worker as? FinalizingDecryptModeWorker, isLast == true { + plaintext = Array(try finalizingWorker.didDecryptLast(bytes: plaintext.slice)) + } + + // omit unecessary calculation if not needed + if self.padding != .noPadding { + self.lastBlockRemainder = plaintext.count.quotientAndRemainder(dividingBy: self.blockSize).remainder + } + + if isLast { + // CTR doesn't need padding. Really. Add padding to the last block if really want. but... don't. + plaintext = self.padding.remove(from: plaintext, blockSize: self.blockSize - self.lastBlockRemainder) + } + + self.accumulated.removeFirst(processedBytesCount) // super-slow + + if var finalizingWorker = worker as? FinalizingDecryptModeWorker, isLast == true { + plaintext = Array(try finalizingWorker.finalize(decrypt: plaintext.slice)) + } + + return plaintext + } + + + internal func seek(to position: Int) throws { + guard var worker = self.worker as? SeekableModeWorker else { + throw Error.unsupported + } + + try worker.seek(to: position) + self.worker = worker + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/StreamEncryptor.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/StreamEncryptor.swift new file mode 100644 index 0000000..5174e16 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/StreamEncryptor.swift @@ -0,0 +1,73 @@ +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +@usableFromInline +final class StreamEncryptor: Cryptor, Updatable { + + @usableFromInline + enum Error: Swift.Error { + case unsupported + } + + @usableFromInline + internal let blockSize: Int + + @usableFromInline + internal var worker: CipherModeWorker + + @usableFromInline + internal let padding: Padding + + @usableFromInline + internal var lastBlockRemainder = 0 + + @usableFromInline + init(blockSize: Int, padding: Padding, _ worker: CipherModeWorker) throws { + self.blockSize = blockSize + self.padding = padding + self.worker = worker + } + + // MARK: Updatable + + + internal func update(withBytes bytes: ArraySlice, isLast: Bool) throws -> Array { + var accumulated = Array(bytes) + if isLast { + // CTR doesn't need padding. Really. Add padding to the last block if really want. but... don't. + accumulated = self.padding.add(to: accumulated, blockSize: self.blockSize - self.lastBlockRemainder) + } + + var encrypted = Array(reserveCapacity: bytes.count) + for chunk in accumulated.batched(by: self.blockSize) { + encrypted += self.worker.encrypt(block: chunk) + } + + // omit unecessary calculation if not needed + if self.padding != .noPadding { + self.lastBlockRemainder = encrypted.count.quotientAndRemainder(dividingBy: self.blockSize).remainder + } + + if var finalizingWorker = worker as? FinalizingEncryptModeWorker, isLast == true { + encrypted = Array(try finalizingWorker.finalize(encrypt: encrypted.slice)) + } + + return encrypted + } + + @usableFromInline + func seek(to: Int) throws { + throw Error.unsupported + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/String+Extension.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/String+Extension.swift new file mode 100644 index 0000000..6b9760a --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/String+Extension.swift @@ -0,0 +1,98 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +/** String extension */ +extension String { + + + internal var bytes: Array { + data(using: String.Encoding.utf8, allowLossyConversion: true)?.bytes ?? Array(utf8) + } + + + internal func md5() -> String { + self.bytes.md5().toHexString() + } + + + internal func sha1() -> String { + self.bytes.sha1().toHexString() + } + + + internal func sha224() -> String { + self.bytes.sha224().toHexString() + } + + + internal func sha256() -> String { + self.bytes.sha256().toHexString() + } + + + internal func sha384() -> String { + self.bytes.sha384().toHexString() + } + + + internal func sha512() -> String { + self.bytes.sha512().toHexString() + } + + + internal func sha3(_ variant: SHA3.Variant) -> String { + self.bytes.sha3(variant).toHexString() + } + + + internal func crc32(seed: UInt32? = nil, reflect: Bool = true) -> String { + self.bytes.crc32(seed: seed, reflect: reflect).bytes().toHexString() + } + + + internal func crc32c(seed: UInt32? = nil, reflect: Bool = true) -> String { + self.bytes.crc32c(seed: seed, reflect: reflect).bytes().toHexString() + } + + + internal func crc16(seed: UInt16? = nil) -> String { + self.bytes.crc16(seed: seed).bytes().toHexString() + } + + /// - parameter cipher: Instance of `Cipher` + /// - returns: hex string of bytes + + internal func encrypt(cipher: Cipher) throws -> String { + try self.bytes.encrypt(cipher: cipher).toHexString() + } + + /// - parameter cipher: Instance of `Cipher` + /// - returns: base64 encoded string of encrypted bytes + + internal func encryptToBase64(cipher: Cipher) throws -> String { + try self.bytes.encrypt(cipher: cipher).toBase64() + } + + // decrypt() does not make sense for String + + /// - parameter authenticator: Instance of `Authenticator` + /// - returns: hex string of string + + internal func authenticate(with authenticator: A) throws -> String { + try self.bytes.authenticate(with: authenticator).toHexString() + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/UInt128.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/UInt128.swift new file mode 100644 index 0000000..76b3b3f --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/UInt128.swift @@ -0,0 +1,90 @@ +// +// UInt128.swift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +import Foundation + +struct UInt128: Equatable, ExpressibleByIntegerLiteral { + let i: (a: UInt64, b: UInt64) + + typealias IntegerLiteralType = UInt64 + + init(integerLiteral value: IntegerLiteralType) { + self = UInt128(value) + } + + init(_ raw: Array) { + self = raw.prefix(MemoryLayout.stride).withUnsafeBytes({ (rawBufferPointer) -> UInt128 in + let arr = rawBufferPointer.bindMemory(to: UInt64.self) + return UInt128((arr[0].bigEndian, arr[1].bigEndian)) + }) + } + + init(_ raw: ArraySlice) { + self.init(Array(raw)) + } + + init(_ i: (a: UInt64, b: UInt64)) { + self.i = i + } + + init(a: UInt64, b: UInt64) { + self.init((a, b)) + } + + init(_ b: UInt64) { + self.init((0, b)) + } + + // Bytes + var bytes: Array { + var at = self.i.a.bigEndian + var bt = self.i.b.bigEndian + + let ar = Data(bytes: &at, count: MemoryLayout.size(ofValue: at)) + let br = Data(bytes: &bt, count: MemoryLayout.size(ofValue: bt)) + + var result = Data() + result.append(ar) + result.append(br) + return result.bytes + } + + static func ^ (n1: UInt128, n2: UInt128) -> UInt128 { + UInt128((n1.i.a ^ n2.i.a, n1.i.b ^ n2.i.b)) + } + + static func & (n1: UInt128, n2: UInt128) -> UInt128 { + UInt128((n1.i.a & n2.i.a, n1.i.b & n2.i.b)) + } + + static func >> (value: UInt128, by: Int) -> UInt128 { + var result = value + for _ in 0..> 1 + let b = result.i.b >> 1 + ((result.i.a & 1) << 63) + result = UInt128((a, b)) + } + return result + } + + // Equatable. + static func == (lhs: UInt128, rhs: UInt128) -> Bool { + lhs.i == rhs.i + } + + static func != (lhs: UInt128, rhs: UInt128) -> Bool { + !(lhs == rhs) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/UInt16+Extension.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/UInt16+Extension.swift new file mode 100644 index 0000000..a9b795a --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/UInt16+Extension.swift @@ -0,0 +1,37 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +/** array of bytes */ +extension UInt16 { + @_specialize(where T == ArraySlice) + init(bytes: T) where T.Element == UInt8, T.Index == Int { + self = UInt16(bytes: bytes, fromIndex: bytes.startIndex) + } + + @_specialize(where T == ArraySlice) + init(bytes: T, fromIndex index: T.Index) where T.Element == UInt8, T.Index == Int { + if bytes.isEmpty { + self = 0 + return + } + + let count = bytes.count + + let val0 = count > 0 ? UInt16(bytes[index.advanced(by: 0)]) << 8 : 0 + let val1 = count > 1 ? UInt16(bytes[index.advanced(by: 1)]) : 0 + + self = val0 | val1 + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/UInt32+Extension.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/UInt32+Extension.swift new file mode 100644 index 0000000..2159116 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/UInt32+Extension.swift @@ -0,0 +1,51 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#elseif canImport(ucrt) +import ucrt +#endif + +protocol _UInt32Type {} +extension UInt32: _UInt32Type {} + +/** array of bytes */ +extension UInt32 { + @_specialize(where T == ArraySlice) + init(bytes: T) where T.Element == UInt8, T.Index == Int { + self = UInt32(bytes: bytes, fromIndex: bytes.startIndex) + } + + @_specialize(where T == ArraySlice) + + init(bytes: T, fromIndex index: T.Index) where T.Element == UInt8, T.Index == Int { + if bytes.isEmpty { + self = 0 + return + } + + let count = bytes.count + + let val0 = count > 0 ? UInt32(bytes[index.advanced(by: 0)]) << 24 : 0 + let val1 = count > 1 ? UInt32(bytes[index.advanced(by: 1)]) << 16 : 0 + let val2 = count > 2 ? UInt32(bytes[index.advanced(by: 2)]) << 8 : 0 + let val3 = count > 3 ? UInt32(bytes[index.advanced(by: 3)]) : 0 + + self = val0 | val1 | val2 | val3 + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/UInt64+Extension.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/UInt64+Extension.swift new file mode 100644 index 0000000..ef698ec --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/UInt64+Extension.swift @@ -0,0 +1,44 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +/** array of bytes */ +extension UInt64 { + @_specialize(where T == ArraySlice) + init(bytes: T) where T.Element == UInt8, T.Index == Int { + self = UInt64(bytes: bytes, fromIndex: bytes.startIndex) + } + + @_specialize(where T == ArraySlice) + + init(bytes: T, fromIndex index: T.Index) where T.Element == UInt8, T.Index == Int { + if bytes.isEmpty { + self = 0 + return + } + + let count = bytes.count + + let val0 = count > 0 ? UInt64(bytes[index.advanced(by: 0)]) << 56 : 0 + let val1 = count > 1 ? UInt64(bytes[index.advanced(by: 1)]) << 48 : 0 + let val2 = count > 2 ? UInt64(bytes[index.advanced(by: 2)]) << 40 : 0 + let val3 = count > 3 ? UInt64(bytes[index.advanced(by: 3)]) << 32 : 0 + let val4 = count > 4 ? UInt64(bytes[index.advanced(by: 4)]) << 24 : 0 + let val5 = count > 5 ? UInt64(bytes[index.advanced(by: 5)]) << 16 : 0 + let val6 = count > 6 ? UInt64(bytes[index.advanced(by: 6)]) << 8 : 0 + let val7 = count > 7 ? UInt64(bytes[index.advanced(by: 7)]) : 0 + + self = val0 | val1 | val2 | val3 | val4 | val5 | val6 | val7 + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/UInt8+Extension.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/UInt8+Extension.swift new file mode 100644 index 0000000..98ab57c --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/UInt8+Extension.swift @@ -0,0 +1,74 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#elseif canImport(ucrt) +import ucrt +#endif + +internal protocol _UInt8Type {} +extension UInt8: _UInt8Type {} + +/** casting */ +extension UInt8 { + /** cast because UInt8() because std initializer crash if value is > byte */ + static func with(value: UInt64) -> UInt8 { + let tmp = value & 0xff + return UInt8(tmp) + } + + static func with(value: UInt32) -> UInt8 { + let tmp = value & 0xff + return UInt8(tmp) + } + + static func with(value: UInt16) -> UInt8 { + let tmp = value & 0xff + return UInt8(tmp) + } +} + +/** Bits */ +extension UInt8 { + /** array of bits */ + internal func bits() -> [Bit] { + let totalBitsCount = MemoryLayout.size * 8 + + var bitsArray = [Bit](repeating: Bit.zero, count: totalBitsCount) + + for j in 0.. String { + var s = String() + let arr: [Bit] = self.bits() + for idx in arr.indices { + s += (arr[idx] == Bit.one ? "1" : "0") + if idx.advanced(by: 1) % 8 == 0 { s += " " } + } + return s + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Updatable.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Updatable.swift new file mode 100644 index 0000000..0ef9a09 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Updatable.swift @@ -0,0 +1,107 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +/// A type that supports incremental updates. For example Digest or Cipher may be updatable +/// and calculate result incerementally. +internal protocol Updatable { + /// Update given bytes in chunks. + /// + /// - parameter bytes: Bytes to process. + /// - parameter isLast: Indicate if given chunk is the last one. No more updates after this call. + /// - returns: Processed partial result data or empty array. + mutating func update(withBytes bytes: ArraySlice, isLast: Bool) throws -> Array + + /// Update given bytes in chunks. + /// + /// - Parameters: + /// - bytes: Bytes to process. + /// - isLast: Indicate if given chunk is the last one. No more updates after this call. + /// - output: Resulting bytes callback. + /// - Returns: Processed partial result data or empty array. + mutating func update(withBytes bytes: ArraySlice, isLast: Bool, output: (_ bytes: Array) -> Void) throws +} + +extension Updatable { + + internal mutating func update(withBytes bytes: ArraySlice, isLast: Bool = false, output: (_ bytes: Array) -> Void) throws { + let processed = try update(withBytes: bytes, isLast: isLast) + if !processed.isEmpty { + output(processed) + } + } + + + internal mutating func update(withBytes bytes: ArraySlice, isLast: Bool = false) throws -> Array { + try self.update(withBytes: bytes, isLast: isLast) + } + + + internal mutating func update(withBytes bytes: Array, isLast: Bool = false) throws -> Array { + try self.update(withBytes: bytes.slice, isLast: isLast) + } + + + internal mutating func update(withBytes bytes: Array, isLast: Bool = false, output: (_ bytes: Array) -> Void) throws { + try self.update(withBytes: bytes.slice, isLast: isLast, output: output) + } + + /// Finish updates. This may apply padding. + /// - parameter bytes: Bytes to process + /// - returns: Processed data. + + internal mutating func finish(withBytes bytes: ArraySlice) throws -> Array { + try self.update(withBytes: bytes, isLast: true) + } + + + internal mutating func finish(withBytes bytes: Array) throws -> Array { + try self.finish(withBytes: bytes.slice) + } + + /// Finish updates. May add padding. + /// + /// - Returns: Processed data + /// - Throws: Error + + internal mutating func finish() throws -> Array { + try self.update(withBytes: [], isLast: true) + } + + /// Finish updates. This may apply padding. + /// - parameter bytes: Bytes to process + /// - parameter output: Resulting data + /// - returns: Processed data. + + internal mutating func finish(withBytes bytes: ArraySlice, output: (_ bytes: Array) -> Void) throws { + let processed = try update(withBytes: bytes, isLast: true) + if !processed.isEmpty { + output(processed) + } + } + + + internal mutating func finish(withBytes bytes: Array, output: (_ bytes: Array) -> Void) throws { + try self.finish(withBytes: bytes.slice, output: output) + } + + /// Finish updates. May add padding. + /// + /// - Parameter output: Processed data + /// - Throws: Error + + internal mutating func finish(output: (Array) -> Void) throws { + try self.finish(withBytes: [], output: output) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/Utils.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/Utils.swift new file mode 100644 index 0000000..3f62d8d --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/Utils.swift @@ -0,0 +1,117 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + + +func rotateLeft(_ value: UInt8, by: UInt8) -> UInt8 { + ((value << by) & 0xff) | (value >> (8 - by)) +} + + +func rotateLeft(_ value: UInt16, by: UInt16) -> UInt16 { + ((value << by) & 0xffff) | (value >> (16 - by)) +} + + +func rotateLeft(_ value: UInt32, by: UInt32) -> UInt32 { + ((value << by) & 0xffffffff) | (value >> (32 - by)) +} + + +func rotateLeft(_ value: UInt64, by: UInt64) -> UInt64 { + (value << by) | (value >> (64 - by)) +} + + +func rotateRight(_ value: UInt16, by: UInt16) -> UInt16 { + (value >> by) | (value << (16 - by)) +} + + +func rotateRight(_ value: UInt32, by: UInt32) -> UInt32 { + (value >> by) | (value << (32 - by)) +} + + +func rotateRight(_ value: UInt64, by: UInt64) -> UInt64 { + ((value >> by) | (value << (64 - by))) +} + + +func reversed(_ uint8: UInt8) -> UInt8 { + var v = uint8 + v = (v & 0xf0) >> 4 | (v & 0x0f) << 4 + v = (v & 0xcc) >> 2 | (v & 0x33) << 2 + v = (v & 0xaa) >> 1 | (v & 0x55) << 1 + return v +} + + +func reversed(_ uint32: UInt32) -> UInt32 { + var v = uint32 + v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1) + v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2) + v = ((v >> 4) & 0x0f0f0f0f) | ((v & 0x0f0f0f0f) << 4) + v = ((v >> 8) & 0x00ff00ff) | ((v & 0x00ff00ff) << 8) + v = ((v >> 16) & 0xffff) | ((v & 0xffff) << 16) + return v +} + + +func xor(_ left: T, _ right: V) -> ArraySlice where T: RandomAccessCollection, V: RandomAccessCollection, T.Element == UInt8, T.Index == Int, V.Element == UInt8, V.Index == Int { + return xor(left, right).slice +} + + +func xor(_ left: T, _ right: V) -> Array where T: RandomAccessCollection, V: RandomAccessCollection, T.Element == UInt8, T.Index == Int, V.Element == UInt8, V.Index == Int { + let length = Swift.min(left.count, right.count) + + let buf = UnsafeMutablePointer.allocate(capacity: length) + buf.initialize(repeating: 0, count: length) + defer { + buf.deinitialize(count: length) + buf.deallocate() + } + + // xor + for i in 0.., blockSize: Int, allowance: Int = 0) { + let msgLength = data.count + // Step 1. Append Padding Bits + // append one bit (UInt8 with one bit) to message + data.append(0x80) + + // Step 2. append "0" bit until message length in bits ≡ 448 (mod 512) + let max = blockSize - allowance // 448, 986 + if msgLength % blockSize < max { // 448 + data += Array(repeating: 0, count: max - 1 - (msgLength % blockSize)) + } else { + data += Array(repeating: 0, count: blockSize + max - 1 - (msgLength % blockSize)) + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/XChaCha20.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/XChaCha20.swift new file mode 100644 index 0000000..c65ca28 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/XChaCha20.swift @@ -0,0 +1,232 @@ +// +// CryptoSwift +// +// Copyright (C) Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +/// XChaCha20 is a Swift implementation of the XChaCha20 stream cipher, which is an extension of the ChaCha20 cipher +/// that uses a 192-bit nonce instead of the original 64-bit nonce. XChaCha20 provides a higher security level by +/// allowing a larger number of safe random nonces, reducing the risk of nonce reuse. +/// +/// For more information about the XChaCha20 algorithm, refer to the IETF draft: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha +internal final class XChaCha20: BlockCipher, BlockMode { + + internal enum Error: Swift.Error { + case invalidKeyOrInitializationVector + case notSupported + } + + fileprivate var chacha20: ChaCha20 + + // MARK: BlockCipher + + internal static let blockSize = 64 // 512 / 8 + + // MARK: Cipher + + internal let keySize: Int + + /// Initializes a new instance of XChaCha20 with the provided key, nonce, and optional block counter. + /// - Parameters: + /// - key: A 256-bit (32-byte) key for the XChaCha20 cipher. + /// - nonce: A 192-bit (24-byte) nonce for the XChaCha20 cipher. + /// - blockCounter: An optional initial block counter value, defaulting to 0. + /// - Throws: Error.invalidKeyOrInitializationVector if the key or nonce lengths are not valid. + internal init(key: Array, iv nonce: Array, blockCounter: UInt32 = 0) throws { + guard key.count == 32 && nonce.count == 24 else { + throw Error.invalidKeyOrInitializationVector + } + + self.keySize = key.count + + // From https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#section-2.3 + // XChaCha20 can be constructed from an existing ChaCha20 implementation + // and HChaCha20. All one needs to do is: + // + // 1. Pass the key and the first 16 bytes of the 24-byte nonce to + // HChaCha20 to obtain the subkey. + // + // 2. Use the subkey and remaining 8 byte nonce with ChaCha20 as normal + // (prefixed by 4 NUL bytes, since [RFC8439] specifies a 12-byte + // nonce). + self.chacha20 = try .init( + key: XChaCha20.hChaCha20(key: key, nonce: Array(nonce[0..<16])), + iv: [0, 0, 0, 0] + Array(nonce[16..<24]), + blockCounter: blockCounter + ) + } + + // MARK: BlockMode + + /// Options specific to the block mode. + internal let options: BlockModeOption = [.none] + /// The custom block size for the block mode, if any. XChaCha20 does not have a custom block size. + internal let customBlockSize: Int? = nil + + internal func worker(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, encryptionOperation: @escaping CipherOperationOnBlock) throws -> CipherModeWorker { + return XChaCha20Worker( + blockSize: blockSize, + cipherOperation: cipherOperation, + xChaCha20: self + ) + } + + /// Computes the HChaCha20 function on the provided key and nonce. + /// + /// See: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#section-2.2 + /// + /// - Parameters: + /// - key: A 256-bit (32-byte) key. + /// - nonce: A 128-bit (16-byte) nonce. + /// - Returns: A 256-bit (32-byte) derived key. + static func hChaCha20(key: [UInt8], nonce: [UInt8]) -> [UInt8] { + precondition(key.count == 32) + precondition(nonce.count == 16) + + // HChaCha20 is initialized the same way as the ChaCha cipher, except + // that HChaCha20 uses a 128-bit nonce and has no counter. Instead, the + // block counter is replaced by the first 32 bits of the nonce. + + var state = Array(repeating: 0, count: 16) + + state[0] = 0x61707865 + state[1] = 0x3320646e + state[2] = 0x79622d32 + state[3] = 0x6b206574 + for i in 0..<8 { + state[4 + i] = UInt32(bytes: key[i * 4..<(i + 1) * 4]).bigEndian + } + for i in 0..<4 { + state[12 + i] = UInt32(bytes: nonce[i * 4..<(i + 1) * 4]).bigEndian + } + + // After initialization, proceed through the ChaCha rounds as usual. + + for _ in 1...10 { + self.innerBlock(&state) + } + + // Once the 20 ChaCha rounds have been completed, the first 128 bits and + // last 128 bits of the ChaCha state (both little-endian) are + // concatenated, and this 256-bit subkey is returned. + + var output = Array() + for i in 0..<4 { + output += state[i].bigEndian.bytes() + } + for i in 0..<4 { + output += state[12 + i].bigEndian.bytes() + } + + return output + } + + /// Performs the "quarter round" operation on the provided state at the specified indices. + /// - Parameters: + /// - state: The state on which to perform the operation. + /// - a: The index of the first element in the state. + /// - b: The index of the second element in the state. + /// - c: The index of the third element in the state. + /// - d: The index of the fourth element in the state. + static func qRound(_ state: inout [UInt32], _ a: Int, _ b: Int, _ c: Int, _ d: Int) { + state[a] = state[a] &+ state[b] + state[d] ^= state[a] + state[d] = (state[d] << 16) | (state[d] >> 16) + state[c] = state[c] &+ state[d] + state[b] ^= state[c] + state[b] = (state[b] << 12) | (state[b] >> 20) + state[a] = state[a] &+ state[b] + state[d] ^= state[a] + state[d] = (state[d] << 8) | (state[d] >> 24) + state[c] = state[c] &+ state[d] + state[b] ^= state[c] + state[b] = (state[b] << 7) | (state[b] >> 25) + } + + /// Performs the inner block operation on the provided state. + /// - Parameter state: The state on which to perform the operation. + static func innerBlock(_ state: inout [UInt32]) { + self.qRound(&state, 0, 4, 8, 12) + self.qRound(&state, 1, 5, 9, 13) + self.qRound(&state, 2, 6, 10, 14) + self.qRound(&state, 3, 7, 11, 15) + self.qRound(&state, 0, 5, 10, 15) + self.qRound(&state, 1, 6, 11, 12) + self.qRound(&state, 2, 7, 8, 13) + self.qRound(&state, 3, 4, 9, 14) + } +} + +// MARK: Cipher + +extension XChaCha20: Cipher { + internal func encrypt(_ bytes: ArraySlice) throws -> Array { + try self.chacha20.encrypt(bytes) + } + + internal func decrypt(_ bytes: ArraySlice) throws -> Array { + try self.encrypt(bytes) + } +} + +// MARK: Cryptors + +extension XChaCha20: Cryptors { + + internal func makeEncryptor() throws -> Cryptor & Updatable { + return try BlockEncryptor( + blockSize: XChaCha20.blockSize, + padding: .noPadding, + self.worker( + blockSize: XChaCha20.blockSize, + cipherOperation: { _ in nil }, + encryptionOperation: { _ in nil } + ) + ) + } + + internal func makeDecryptor() throws -> Cryptor & Updatable { + return try BlockDecryptor( + blockSize: XChaCha20.blockSize, + padding: .noPadding, + self.worker( + blockSize: XChaCha20.blockSize, + cipherOperation: { _ in nil }, + encryptionOperation: { _ in nil } + ) + ) + } +} + +class XChaCha20Worker: CipherModeWorker { + let blockSize: Int + let cipherOperation: CipherOperationOnBlock + let xChaCha20: XChaCha20 + + init(blockSize: Int, cipherOperation: @escaping CipherOperationOnBlock, xChaCha20: XChaCha20) { + self.blockSize = blockSize + self.cipherOperation = cipherOperation + self.xChaCha20 = xChaCha20 + } + + var additionalBufferSize: Int { + return 0 + } + + func encrypt(block plaintext: ArraySlice) -> Array { + return (try? self.xChaCha20.encrypt(plaintext)) ?? .init() + } + + func decrypt(block ciphertext: ArraySlice) -> Array { + return (try? self.xChaCha20.decrypt(ciphertext)) ?? .init() + } +} diff --git a/MixSdk/MixSdk/MixSdk/CryptoSwift/ZeroPadding.swift b/MixSdk/MixSdk/MixSdk/CryptoSwift/ZeroPadding.swift new file mode 100644 index 0000000..36981c5 --- /dev/null +++ b/MixSdk/MixSdk/MixSdk/CryptoSwift/ZeroPadding.swift @@ -0,0 +1,40 @@ +// +// CryptoSwift +// +// Copyright (C) 2014-2022 Marcin Krzyżanowski +// This software is provided 'as-is', without any express or implied warranty. +// +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +// +// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required. +// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +// - This notice may not be removed or altered from any source or binary distribution. +// + +/// All the bytes that are required to be padded are padded with zero. +/// Zero padding may not be reversible if the original file ends with one or more zero bytes. +struct ZeroPadding: PaddingProtocol { + init() { + } + + + func add(to bytes: Array, blockSize: Int) -> Array { + let paddingCount = blockSize - (bytes.count % blockSize) + if paddingCount > 0 { + return bytes + Array(repeating: 0, count: paddingCount) + } + return bytes + } + + + func remove(from bytes: Array, blockSize _: Int?) -> Array { + for (idx, value) in bytes.reversed().enumerated() { + if value != 0 { + return Array(bytes[0.. Void +public typealias MSRegisterCallBack = (_ password: String?,_ error:MSError?) -> Void /// 登录回调 -public typealias MSLoginCallBack = (_ isSuccess: Bool,_ error:MSError?) -> Void +public typealias MSLoginCallBack = (_ token: String?,_ error:MSError?) -> Void /// 鉴权管理类 @@ -19,22 +19,24 @@ public class MSAuth { /// 是否已经鉴权 public private(set) var authorized = false + /// 随机数 +// var nonce = "WFY51NbA4lUff3Jd" + func getUrl(_ api:String)->String{ return "http://180.184.206.142/bz-api/" + api } /// 注册 /// - Parameters: - /// - productKey: 产品Key - /// - secretKey: 产品秘钥 + /// - productId: 产品id + /// - productKey: 产品秘钥 /// - deviceId: 设备ID - /// - sn: SN号 /// - completeHandler: 结果回调 - public func register(productKey:String,secretKey:String,deviceId:String,completeHandler: @escaping MSRegisterCallBack){ + public func register(productId:String,productKey:String,deviceId:String,completeHandler: @escaping MSRegisterCallBack){ let timeStamp = "\(Int(Date().timeIntervalSince1970)*1000)" let value = deviceId + productKey + timeStamp - let signValue = MSUtils.string(toHmacSha1: secretKey, text: value) + let signValue = MSUtils.string(toHmacSha1: productKey, text: value) let params = [ "pid":productKey, "deviceId":deviceId, @@ -43,7 +45,7 @@ public class MSAuth { "timeStamp":timeStamp, ] let url = getUrl("dev/auth/active") - Logger.log(value,secretKey,signValue) + Logger.log(value,productKey,signValue) Logger.log(url,params) HTTP.PUT(url,parameters: params,requestSerializer: JSONParameterSerializer()){ response in @@ -59,7 +61,14 @@ public class MSAuth { model.deviceId = dataDict["deviceId"] as? String ?? "" model.password = dataDict["password"] as? String ?? "" model.nonce = dataDict["nonce"] as? String ?? "" - completeHandler(model,nil) + + /// 解密 + let key1 = productKey + let enCode1 = model.password + let iv1 = model.nonce + + let decode1 = try? AESEncyptUtil.decrypt_AES_CBC(decryptText: enCode1, key: key1, ivs: iv1) + completeHandler(decode1 ?? "",nil) } else{ completeHandler(nil,MSError.reponseError(dict["code"] as? Int ?? 0,dict["msg"] as? String ?? "")) @@ -71,18 +80,23 @@ public class MSAuth { /// 登录 /// - Parameters: /// - productKey:产品Key - /// - secretKey: 产品秘钥 /// - deviceId: 设备ID /// - deviceSecrect: 注册返回的密码 /// - completeHandler: 结果回调 - public func login(deviceId:String,password:String,nonce:String,completeHandler: @escaping MSLoginCallBack){ + public func login(productKey:String,deviceId:String,password:String,completeHandler: @escaping MSLoginCallBack){ + /// 加密 + let nonce = MSTool.generateRandomString() + let key = productKey + guard let pwd = try? AESEncyptUtil.encrypt_AES_CBC(encryptText: password, key: key, ivs: nonce) else { + completeHandler(nil,MSError.invalidRequest) + return + } + let params = [ "deviceId":deviceId, - "password":password, + "password":pwd, "nonce":nonce, ] - - let url = getUrl("dev/auth/sign_in") Logger.log(url,params) @@ -90,17 +104,18 @@ public class MSAuth { DispatchQueue.main.sync { let (dict,error) = MSHttpManager.receiveData(response) if let error = error { - completeHandler(false,error) + completeHandler(nil,error) return } - guard let dict = dict else {return completeHandler(false,error)} + guard let dict = dict else {return completeHandler(nil,error)} - if let result = dict["success"] as? Bool,result == true{ + if let result = dict["success"] as? Bool,result == true,let dataDict = dict["data"] as? [String:Any]{ self.authorized = true - completeHandler(true,nil) + let token = dataDict["token"] as? String + completeHandler(token,nil) } else{ - completeHandler(false,MSError.reponseError(dict["code"] as? Int ?? 0,dict["msg"] as? String ?? "")) + completeHandler(nil,MSError.reponseError(dict["code"] as? Int ?? 0,dict["msg"] as? String ?? "")) } } } diff --git a/MixSdk/MixSdk/MixSdk/MSStt.swift b/MixSdk/MixSdk/MixSdk/MSStt.swift index d295288..20dac11 100644 --- a/MixSdk/MixSdk/MixSdk/MSStt.swift +++ b/MixSdk/MixSdk/MixSdk/MSStt.swift @@ -125,7 +125,7 @@ public extension MSStt{ Logger.log("还未鉴权") return } - let taskId = "\(Int(Date().timeIntervalSince1970)*1000)".md5.lowercased(); + let taskId = "\(Int(Date().timeIntervalSince1970)*1000)".md5().lowercased(); self.taskId = taskId let params = [ "header":[ diff --git a/MixSdk/MixSdk/MixSdk/MSTts.swift b/MixSdk/MixSdk/MixSdk/MSTts.swift index 4df3517..025b0bc 100644 --- a/MixSdk/MixSdk/MixSdk/MSTts.swift +++ b/MixSdk/MixSdk/MixSdk/MSTts.swift @@ -65,7 +65,7 @@ public extension MSTts{ Logger.log("还未鉴权") return } - let taskId = "\(Int(Date().timeIntervalSince1970)*1000)".md5.lowercased(); + let taskId = "\(Int(Date().timeIntervalSince1970)*1000)".md5().lowercased(); self.taskId = taskId let params = [ "header": [ @@ -130,7 +130,7 @@ public extension MSTts{ Logger.log("还未鉴权") return } - let taskId = "\(Int(Date().timeIntervalSince1970)*1000)".md5.lowercased(); + let taskId = "\(Int(Date().timeIntervalSince1970)*1000)".md5().lowercased(); self.taskId = taskId let params = [ "header": [ diff --git a/MixSdk/MixSdk/MixSdk/Tools/Any.swift b/MixSdk/MixSdk/MixSdk/Tools/Any.swift index d164046..9a3bc49 100644 --- a/MixSdk/MixSdk/MixSdk/Tools/Any.swift +++ b/MixSdk/MixSdk/MixSdk/Tools/Any.swift @@ -147,7 +147,7 @@ extension Double { } extension Data { - var bytes: [UInt8] { map { $0 } } +// var bytes: [UInt8] { map { $0 } } func joined(format: String = "%02x", _ separator: String = "") -> String { self diff --git a/MixSdk/MixSdk/MixSdk/Tools/MSTool.swift b/MixSdk/MixSdk/MixSdk/Tools/MSTool.swift index bc82197..6ef8bea 100644 --- a/MixSdk/MixSdk/MixSdk/Tools/MSTool.swift +++ b/MixSdk/MixSdk/MixSdk/Tools/MSTool.swift @@ -61,5 +61,13 @@ class MSTool{ return nil } - + static func generateRandomString(length: Int = 16) -> String { + let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + var randomString: String = "" + for _ in 0..=5.0) +// +// _ = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in +// return CC_MD5(bytes.baseAddress, CC_LONG(data.count), &digest) +// } +// +// #else +// +// _ = data.withUnsafeBytes { bytes in +// return CC_MD5(bytes, CC_LONG(data.count), &digest) +// } +// +// #endif +// +// return digest.map { String(format: "%02X", $0) }.joined() +// } +// +// +//} +import Foundation +//import CryptoSwift + + + +//let iv:Array = [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F] +//let encryptData = AESEncode_C.aes_encrypt(deviceSecret, aes_key: secretKey, iv: iv) - #if swift(>=5.0) +/// 加密工具类 +public class AESEncyptUtil: NSObject { + /// AES-ECB 在进行 AES 加密时,CryptoSwift 会根据密钥的长度自动选择对应的加密算法(AES128, AES192, AES256) + /// - Parameters: + /// - encryptText: 需要加密的数据 + /// - key: 密钥 AES-128 = 16 bytes, AES-192 = 24 bytes, AES-256 = 32 bytes,不够位数,则自动尾部补0,直到自动补齐至加密位数 + /// - Returns: 加密后的数据 + public static func encrypt_AES_ECB(encryptText: String, key: String) throws -> String { + /// 使用AES_ECB模式 + let aes = try AES(key: Padding.zeroPadding.add(to: key.bytes, blockSize: AES.blockSize), + blockMode: ECB()) + /// 开始加密 + let encrypted = try aes.encrypt(encryptText.bytes) + /// 将加密结果转成base64形式 + return encrypted.toBase64() + } + + /// AES-ECB 在进行 AES 加密时,CryptoSwift 会根据密钥的长度自动选择对应的加密算法(AES128, AES192, AES256) + /// - Parameters: + /// - encryptData: 需要加密的数据 + /// - key: 密钥 AES-128 = 16 bytes, AES-192 = 24 bytes, AES-256 = 32 bytes,不够位数,则自动尾部补0,直到自动补齐至加密位数 + /// - Returns: 加密后的数据 + public static func encryptData_AES_ECB(encryptData: Data, key: String) throws -> Data { + /// 使用AES_ECB模式 + let aes = try AES(key: Padding.zeroPadding.add(to: key.bytes, blockSize: AES.blockSize), + blockMode: ECB()) + /// 开始加密 + let encrypted = try aes.encrypt(encryptData.bytes) + /// 将加密结果转成base64形式 + return Data(encrypted) + } - _ = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in - return CC_MD5(bytes.baseAddress, CC_LONG(data.count), &digest) - } + /// AES-ECB解密 + /// - Parameters: + /// - decryptText: 需要解密的数据 + /// - key: 密钥 + /// - Returns: 解密后的内容 + public static func decrypt_AES_ECB(decryptText: String, key: String) throws -> String { + /// 使用AES_ECB模式 + let aes = try AES(key: Padding.zeroPadding.add(to: key.bytes, blockSize: AES.blockSize), + blockMode: ECB()) + let decrypted = try decryptText.decryptBase64ToString(cipher: aes) + return decrypted + } + + /// AES-ECB解密 + /// - Parameters: + /// - decryptData: 需要解密的数据 + /// - key: 密钥 + /// - Returns: 解密后的内容 + public static func decryptData_AES_ECB(decryptData: Data, key: String) throws -> Data { + /// 使用AES_ECB模式 + let aes = try AES(key: Padding.zeroPadding.add(to: key.bytes, blockSize: AES.blockSize), + blockMode: ECB()) + let decrypted = try aes.decrypt(decryptData.bytes) + return Data(decrypted) + } - #else + /// AES_CBC 加密 + /// - Parameters: + /// - encryptText: 需要加密的数据 + /// - key: 密钥 + /// - ivs: 密钥偏移量 + /// - Returns: 加密后的数据 + public static func encrypt_AES_CBC(encryptText: String, key: String, ivs: String) throws -> String { + /// 使用AES_CBC模式 + let aes = try AES(key: key.bytes, blockMode: CBC(iv: ivs.bytes)) - _ = data.withUnsafeBytes { bytes in - return CC_MD5(bytes, CC_LONG(data.count), &digest) - } + /// 开始加密 + let encrypted = try aes.encrypt(encryptText.bytes) + /// 将加密结果转成base64形式 + return encrypted.toBase64() + } - #endif + /// AES_CBC 解密 + /// - Parameters: + /// - decryptText: 需要解密的数据 + /// - key: 秘钥 + /// - ivs: 密钥偏移量 + /// - Returns: 解密后的数据 + public static func decrypt_AES_CBC(decryptText: String, key: String, ivs: String) throws -> String { + /// 使用AES_CBC模式 + let aes = try AES(key: key.bytes, blockMode: CBC(iv: ivs.bytes)) + /// 开始解密 从加密后的base64字符串解密 + let decrypted = try decryptText.decryptBase64ToString(cipher: aes) + return decrypted + } - return digest.map { String(format: "%02X", $0) }.joined() + /// AES_GCM 加密 + /// - Parameters: + /// - encryptText: 需要加密的数据 + /// - key: 密钥 + /// - ivs: 密钥偏移量 + /// - Returns: 加密后的数据 + public static func encrypt_AES_GCM(encryptText: Array, key: Array, ivs: Array) throws -> Array { + let gcm = GCM(iv: ivs, mode: .combined) + let aes = try AES(key: key, blockMode: gcm, padding: .noPadding) + let encrypted = try aes.encrypt(encryptText) + _ = gcm.authenticationTag + return encrypted + } + + /// AES_GCM 解密 + /// - Parameters: + /// - decryptText: 需要解密的数据 + /// - key: 秘钥 + /// - ivs: 密钥偏移量 + /// - Returns: 解密后的数据 + public static func decrypt_AES_GCM(decryptText: Array, key: Array, ivs: Array) throws -> Array { + let gcm = GCM(iv: ivs, mode: .combined) + let aes = try AES(key: key, blockMode: gcm, padding: .noPadding) + return try aes.decrypt(decryptText) } + /// AES加密 + /// aes_key : key + /// iv : 偏移量 + static func aes_encrypt(_ str: String, aes_key: String, ivs: String) -> String{ + var encryptedStr = "" + do { + let encrypted = try AES(key: aes_key.bytes, blockMode: CBC(iv: ivs.bytes)).encrypt(str.bytes) + + let data = Data(bytes: encrypted, count: encrypted.count) + + //加密结果从data转成string 转换失败 返回"" + encryptedStr = data.base64EncodedString(options: .lineLength64Characters) + + } catch { + print(error.localizedDescription) + } + return encryptedStr + + } }