├── Cartfile.resolved ├── lib └── include │ └── .gitkeep ├── Brewfile ├── Sources ├── OpenSSL │ ├── Signature │ │ ├── .swiftlint.yml │ │ ├── Signature.swift │ │ ├── BrainpoolP256r1+ECDSA.swift │ │ └── ECDSASignature.swift │ ├── Resources │ │ ├── debug_macOS.xcconfig │ │ ├── release_macOS.xcconfig │ │ ├── debug_iOS.xcconfig │ │ ├── release_iOS.xcconfig │ │ ├── base.xcconfig │ │ ├── PrivacyInfo.xcprivacy │ │ └── Info.plist │ ├── OpenSSLError.swift │ ├── String+CChar.swift │ ├── EC │ │ ├── BrainpoolP256r1.swift │ │ ├── OpenSSLECGroup.swift │ │ ├── EllipticCurvePoint.swift │ │ └── ECPublicKey.swift │ ├── KeyExchange │ │ ├── DiffieHellman.swift │ │ ├── BrainpoolP256r1+DiffieHellman.swift │ │ └── PACE.swift │ ├── Hash │ │ └── Hash.swift │ ├── FiniteBigNumberFieldArithmeticContext.swift │ ├── BigNumber.swift │ ├── CMS │ │ └── CMSContentInfo.swift │ ├── Mac │ │ └── CMAC.swift │ └── Certificate │ │ └── OCSPResponse.swift └── COpenSSL │ ├── module │ └── module.modulemap │ ├── define_wrapper.c │ ├── shim.h │ └── define_wrapper.h ├── fastlane ├── Scanfile ├── Pluginfile ├── Matchfile ├── README.md └── Fastfile ├── Tests ├── .swiftlint.yml └── OpenSSLTests │ ├── Resources │ └── Info.plist │ ├── Resources.bundle │ ├── X509 │ │ ├── GEM.KOMP-CA28 TEST-ONLY.der.base64 │ │ ├── GEM.RCA3-TEST-ONLY.pem │ │ ├── GEM.DISCOVERY-DOC-TEST-ONLY.pem │ │ ├── c.fd.enc-erp-erpserverReferenz.pem │ │ ├── GEM.KOMP-CA10-TEST-ONLY.pem │ │ ├── PHARMACY.ADELHEID-EC-TEST-ONLY.pem │ │ └── PHARMACY.ADELHEID-RSA-TEST-ONLY.pem │ └── OCSP │ │ ├── vau-oscp-response.der.base64 │ │ └── vau-ocsp-response-no-signing-ca.der.base64 │ ├── KeyExchange │ ├── BrainpoolP256r1KeyGenerationTests.swift │ ├── BrainpoolP256r1ExtPACETests.swift │ └── BrainpoolP256r1ExtDiffieHellmanTests.swift │ ├── Hash │ └── HashTests.swift │ ├── Certificate │ ├── ResourceFileReader.swift │ ├── OCSPResponseTests.swift │ └── X509Tests.swift │ ├── Data+Hex.swift │ ├── Signature │ ├── ECDSASignatureTest.swift │ └── BrainpoolP256r1ExtECDSATests.swift │ ├── EC │ ├── ECPublicKeyTests.swift │ ├── ECPrivateKeyTests.swift │ └── EllipticCurvePointTests.swift │ ├── Mac │ └── CMACTests.swift │ └── CMS │ └── CMSContentInfoTests.swift ├── Mintfile ├── OpenSSL-Swift.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── OpenSSL_iOS.xcscheme │ └── OpenSSL_macOS.xcscheme ├── scripts ├── build ├── lint ├── format ├── setup ├── test ├── update ├── cibuild ├── bootstrap └── install_openssl ├── .jazzy.yml ├── SECURITY.md ├── Gemfile ├── .swiftformat ├── Config ├── shared-info.pl.patch └── ios-conf.patch ├── Makefile ├── devops ├── Jenkinsfile_GitHubAttachToRelease └── Jenkinsfile ├── project.yml ├── .swiftlint.yml ├── Package.swift ├── ReleaseNotes.md ├── .gitignore └── Gemfile.lock /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/include/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Brewfile: -------------------------------------------------------------------------------- 1 | brew 'mint' 2 | brew 'xcodesorg/made/xcodes' -------------------------------------------------------------------------------- /Sources/OpenSSL/Signature/.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - file_name 3 | -------------------------------------------------------------------------------- /fastlane/Scanfile: -------------------------------------------------------------------------------- 1 | derived_data_path("./DerivedData") 2 | configuration("Debug") 3 | -------------------------------------------------------------------------------- /Sources/COpenSSL/module/module.modulemap: -------------------------------------------------------------------------------- 1 | module COpenSSL { 2 | header "../shim.h" 3 | } 4 | -------------------------------------------------------------------------------- /Tests/.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - force_unwrapping 3 | - force_try 4 | - line_length 5 | -------------------------------------------------------------------------------- /fastlane/Pluginfile: -------------------------------------------------------------------------------- 1 | gem "fastlane-plugin-ci_cd", git: "https://github.com/gematik/Fastlane-Plugin-CI-CD.git", tag: "0.1.0" 2 | -------------------------------------------------------------------------------- /Mintfile: -------------------------------------------------------------------------------- 1 | realm/SwiftLint@0.52.2 2 | yonaskolb/xcodegen@2.29.0 3 | Carthage/Carthage@0.40.0 4 | nicklockwood/SwiftFormat@0.49.9 5 | -------------------------------------------------------------------------------- /Sources/OpenSSL/Resources/debug_macOS.xcconfig: -------------------------------------------------------------------------------- 1 | #include "base.xcconfig" 2 | 3 | OTHER_LDFLAGS = $(inherited) -lssl_release_macos -lcrypto_release_macos 4 | -------------------------------------------------------------------------------- /Sources/OpenSSL/Resources/release_macOS.xcconfig: -------------------------------------------------------------------------------- 1 | #include "base.xcconfig" 2 | 3 | OTHER_LDFLAGS = $(inherited) -lssl_release_macos -lcrypto_release_macos 4 | -------------------------------------------------------------------------------- /Sources/COpenSSL/define_wrapper.c: -------------------------------------------------------------------------------- 1 | #include "define_wrapper.h" 2 | #include 3 | 4 | EVP_PKEY *EVP_EC_gen_wrapped(const char *curve) { 5 | return EVP_EC_gen(curve); 6 | } 7 | -------------------------------------------------------------------------------- /OpenSSL-Swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /scripts/build: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # scripts/build: Build the application 4 | 5 | set -ev 6 | 7 | cd "$(dirname "$0")/.." 8 | 9 | [ -z "$DEBUG" ] || set -x 10 | 11 | echo "==> Building…" 12 | 13 | bundle exec fastlane build_all 14 | -------------------------------------------------------------------------------- /Sources/OpenSSL/Resources/debug_iOS.xcconfig: -------------------------------------------------------------------------------- 1 | #include "base.xcconfig" 2 | 3 | OTHER_LDFLAGS[sdk=iphonesimulator*] = -lssl_release_iphonesimulator -lcrypto_release_iphonesimulator 4 | OTHER_LDFLAGS[sdk=iphoneos*] = -lssl_release_iphoneos -lcrypto_release_iphoneos 5 | -------------------------------------------------------------------------------- /Sources/OpenSSL/Resources/release_iOS.xcconfig: -------------------------------------------------------------------------------- 1 | #include "base.xcconfig" 2 | 3 | OTHER_LDFLAGS[sdk=iphonesimulator*] = -lssl_release_iphonesimulator -lcrypto_release_iphonesimulator 4 | OTHER_LDFLAGS[sdk=iphoneos*] = -lssl_release_iphoneos -lcrypto_release_iphoneos 5 | -------------------------------------------------------------------------------- /scripts/lint: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # scripts/lint: Run lint for application. 4 | 5 | set -ev 6 | 7 | cd "$(dirname "$0")/.." 8 | 9 | [ -z "$DEBUG" ] || set -x 10 | 11 | echo "==> Running static analysis…" 12 | 13 | bundle exec fastlane static_code_analysis 14 | -------------------------------------------------------------------------------- /scripts/format: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # scripts/format: Run code formatting. 4 | 5 | set -ev 6 | 7 | cd "$(dirname "$0")/.." 8 | 9 | [ -z "$DEBUG" ] || set -x 10 | 11 | echo "==> Running code formatting…" 12 | 13 | mint run swiftformat . --config .swiftformat 14 | -------------------------------------------------------------------------------- /.jazzy.yml: -------------------------------------------------------------------------------- 1 | output: docs 2 | author: gematik GmbH 3 | author_url: http://www.gematik.de 4 | exclude: /*/internal* 5 | module: OpenSSL 6 | github_url: https://github.com/gematik/OpenSSL-Swift 7 | theme: jony 8 | xcodebuild_arguments: 9 | - "-project" 10 | - 'OpenSSL-Swift.xcodeproj' 11 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | Since this software is not a productive version, please submit an issue or pull request for any bugs 4 | or vulnerabilities you find. 5 | 6 | In case of a responsible disclosure, please follow instructions 7 | on https://www.gematik.de/datensicherheit#c1227. 8 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | ruby "3.1.2" 4 | 5 | gem "fastlane", "~>2.187" 6 | gem "jazzy", "~>0.14" 7 | gem "xcode-install", "~> 2.6.6" 8 | 9 | plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') 10 | eval_gemfile(plugins_path) if File.exist?(plugins_path) 11 | -------------------------------------------------------------------------------- /scripts/setup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # scripts/setup: Set up application for the first time after cloning, or set it 4 | # back to the initial first unused state. 5 | 6 | set -ev 7 | 8 | cd "$(dirname "$0")/.." 9 | 10 | scripts/bootstrap 11 | 12 | echo "==> Project is now ready to go!" 13 | -------------------------------------------------------------------------------- /scripts/test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # scripts/test: Run test suite for application. Optionally pass in a path to an 4 | # individual test file to run a single test. 5 | 6 | set -evo pipefail 7 | 8 | cd "$(dirname "$0")/.." 9 | 10 | echo "==> Testing…" 11 | 12 | bundle exec fastlane test 13 | -------------------------------------------------------------------------------- /OpenSSL-Swift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Sources/OpenSSL/Resources/base.xcconfig: -------------------------------------------------------------------------------- 1 | HEADER_SEARCH_PATHS = $(inherited) $(SRCROOT)/lib/include $(SRCROOT)/lib/include/openssl/** 2 | LIBRARY_SEARCH_PATHS = $(inherited) $(SRCROOT)/lib 3 | SWIFT_INCLUDE_PATHS = $(SRCROOT)/Sources/COpenSSL 4 | CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES 5 | PRODUCT_NAME = OpenSSL 6 | INFOPLIST_FILE = Sources/OpenSSL/Resources/Info.plist 7 | -------------------------------------------------------------------------------- /Sources/OpenSSL/Resources/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyCollectedDataTypes 6 | 7 | NSPrivacyAccessedAPITypes 8 | 9 | NSPrivacyTrackingDomains 10 | 11 | NSPrivacyTracking 12 | 13 | 14 | -------------------------------------------------------------------------------- /scripts/update: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # scripts/update: Update all dependencies that the application requires to 4 | # run. 5 | 6 | set -ev 7 | 8 | cd "$(dirname "$0")/.." 9 | 10 | scripts/bootstrap 11 | 12 | if [[ -f "Cartfile" || -f "Cartfile.private" ]]; then 13 | carthage update --no-build &2>/dev/null 14 | fi 15 | if [ -f "Gemfile" ]; then 16 | bundle update 17 | fi 18 | if [ -f "Package.swift" ]; then 19 | swift package update &2>/dev/null 20 | fi 21 | 22 | scripts/setup -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | --exclude Package.swift,.build,vendor,Carthage,DerivedData,build,**/Generated 2 | 3 | # format options 4 | --swiftversion 5.5 5 | 6 | --indent 4 7 | --ifdef no-indent 8 | --maxwidth 120 9 | --operatorfunc no-space 10 | --self remove 11 | --trimwhitespace always 12 | --wraparguments preserve 13 | --wrapcollections preserve 14 | 15 | # rules 16 | 17 | --enable isEmpty 18 | --allman false 19 | --elseposition same-line 20 | --disable elseOnSameLine, wrapMultilineStatementBraces, extensionAccessControl 21 | -------------------------------------------------------------------------------- /Config/shared-info.pl.patch: -------------------------------------------------------------------------------- 1 | --- Configurations/shared-info.pl 2022-03-25 13:37:12.000000000 +0100 2 | +++ Configurations/shared-info.pl 2022-03-25 13:37:18.000000000 +0100 3 | @@ -44,2 +44,2 @@ 4 | - module_ldflags => '-bundle', 5 | - shared_ldflag => '-dynamiclib -current_version $(SHLIB_VERSION_NUMBER) -compatibility_version $(SHLIB_VERSION_NUMBER)', 6 | + module_ldflags => '', 7 | + shared_ldflag => '-current_version $(SHLIB_VERSION_NUMBER) -compatibility_version $(SHLIB_VERSION_NUMBER)', 8 | -------------------------------------------------------------------------------- /scripts/cibuild: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # scripts/cibuild: Setup environment for CI to run tests. This is primarily 4 | # designed to run on the continuous integration server. 5 | 6 | set -ev 7 | 8 | cd "$(dirname "$0")/.." 9 | 10 | echo "CI started at…" 11 | date "+%H:%M:%S" 12 | 13 | # Setup project 14 | scripts/setup 15 | 16 | # Lint 17 | echo "Linting" 18 | date "+%H:%M:%S" 19 | scripts/lint 20 | 21 | rm -f lib/*.a 22 | scripts/build 23 | 24 | # run tests 25 | echo "Running tests…" 26 | date "+%H:%M:%S" 27 | scripts/test 28 | 29 | echo "Done" 30 | date "+%H:%M:%S" 31 | -------------------------------------------------------------------------------- /fastlane/Matchfile: -------------------------------------------------------------------------------- 1 | keychain_name("gematik") 2 | keychain_password("gematikpassword") 3 | 4 | git_branch("master") 5 | readonly(true) 6 | 7 | # fastlane will ask for an app identifier if not provided 8 | # The provided one won't be used when skipping the download of the provisioning profiles 9 | app_identifier("unused.dummy.app.identifier") 10 | 11 | team_name("gematik Gesellschaft für Telematikanwendungen der Gesundheitskarte mbH") 12 | team_id("A9FL89PFFL") 13 | 14 | # Uncomment and fill this lines or add the corresponding ENV (MATCH_USERNAME, MATCH_GIT_URL) to call fastlane locally 15 | # username("") 16 | # git_url("") 17 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile 3 | # 4 | 5 | CIBUILD ?= false 6 | BUILD_TYPE ?= Debug 7 | PROJECT_DIR := $(PWD) 8 | 9 | ifeq ($(CIBUILD), true) 10 | BUILD_TYPE = Release 11 | endif 12 | 13 | .PHONY: setup test build update lint format cibuild 14 | 15 | setup: 16 | $(PROJECT_DIR)/scripts/setup ${BUILD_TYPE} 17 | 18 | test: 19 | $(PROJECT_DIR)/scripts/test 20 | 21 | build: 22 | $(PROJECT_DIR)/scripts/build ${BUILD_TYPE} 23 | 24 | update: 25 | $(PROJECT_DIR)/scripts/update 26 | 27 | lint: 28 | $(PROJECT_DIR)/scripts/lint 29 | 30 | format: 31 | $(PROJECT_DIR)/scripts/format 32 | 33 | cibuild: 34 | $(PROJECT_DIR)/scripts/cibuild ${BUILD_TYPE} 35 | -------------------------------------------------------------------------------- /Sources/COpenSSL/shim.h: -------------------------------------------------------------------------------- 1 | #ifndef COpenSSL_H 2 | #define COpenSSL_H 3 | 4 | #include "define_wrapper.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /scripts/bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # scripts/bootstrap: Resolve all dependencies that the application requires to 4 | # run. 5 | 6 | set -ev 7 | 8 | cd "$(dirname "$0")/.." 9 | 10 | hash brew 2>/dev/null || { 11 | echo >&2 "The command 'brew' is required but not installed. See https://brew.sh for installation guidelines."; 12 | exit 1; 13 | } 14 | 15 | brew bundle check >/dev/null 2>&1 || { 16 | echo "==> Installing Homebrew dependencies…" 17 | brew bundle 18 | } 19 | 20 | mint bootstrap 21 | 22 | hash bundle 2>/dev/null || { 23 | echo "==> Installing Bundler 2.0.1…" 24 | gem install bundler -v 2.0.1 --no-document --quiet 25 | } 26 | echo "==> Installing gem dependencies…" 27 | bundle install --system 28 | -------------------------------------------------------------------------------- /Sources/OpenSSL/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Resources.bundle/X509/GEM.KOMP-CA28 TEST-ONLY.der.base64: -------------------------------------------------------------------------------- 1 | MIICuTCCAmCgAwIBAgIBIjAKBggqhkjOPQQDAjCBgTELMAkGA1UEBhMCREUxHzAdBgNVBAoMFmdlbWF0aWsgR21iSCBOT1QtVkFMSUQxNDAyBgNVBAsMK1plbnRyYWxlIFJvb3QtQ0EgZGVyIFRlbGVtYXRpa2luZnJhc3RydWt0dXIxGzAZBgNVBAMMEkdFTS5SQ0EzIFRFU1QtT05MWTAeFw0xODA2MjAwOTQ5NTlaFw0yNjA2MTgwOTQ5NThaMIGEMQswCQYDVQQGEwJERTEfMB0GA1UECgwWZ2VtYXRpayBHbWJIIE5PVC1WQUxJRDEyMDAGA1UECwwpS29tcG9uZW50ZW4tQ0EgZGVyIFRlbGVtYXRpa2luZnJhc3RydWt0dXIxIDAeBgNVBAMMF0dFTS5LT01QLUNBMjggVEVTVC1PTkxZMFowFAYHKoZIzj0CAQYJKyQDAwIIAQEHA0IABFuRbiWZxMO8y+FvvQAPuAC+1NbkMRMTEyuFj9REDzkCYwFezS+WwbsCmsXgcDRnkDZ0lqFpQR0eS+T+/6etrtijgcIwgb8wHQYDVR0OBBYEFABqOJDzma4hj1La7sGMboCtYSLJMB8GA1UdIwQYMBaAFAeQMy11U15/+Mg3v37JJldo3zjSMEIGCCsGAQUFBwEBBDYwNDAyBggrBgEFBQcwAYYmaHR0cDovL29jc3Aucm9vdC1jYS50aS1kaWVuc3RlLmRlL29jc3AwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwFQYDVR0gBA4wDDAKBggqghQATASBIzAKBggqhkjOPQQDAgNHADBEAiBlbZRIayrzTk21ghnuni8u3trfhqwoNOHHRYpEMTTpLQIgdjiy1cKJSwLfRQ2RKI1NrI7ogEk/PQvw7c3iGbFVGMY= -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Resources.bundle/X509/GEM.RCA3-TEST-ONLY.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICkzCCAjmgAwIBAgIBATAKBggqhkjOPQQDAjCBgTELMAkGA1UEBhMCREUxHzAd 3 | BgNVBAoMFmdlbWF0aWsgR21iSCBOT1QtVkFMSUQxNDAyBgNVBAsMK1plbnRyYWxl 4 | IFJvb3QtQ0EgZGVyIFRlbGVtYXRpa2luZnJhc3RydWt0dXIxGzAZBgNVBAMMEkdF 5 | TS5SQ0EzIFRFU1QtT05MWTAeFw0xNzA4MTEwODM4NDVaFw0yNzA4MDkwODM4NDVa 6 | MIGBMQswCQYDVQQGEwJERTEfMB0GA1UECgwWZ2VtYXRpayBHbWJIIE5PVC1WQUxJ 7 | RDE0MDIGA1UECwwrWmVudHJhbGUgUm9vdC1DQSBkZXIgVGVsZW1hdGlraW5mcmFz 8 | dHJ1a3R1cjEbMBkGA1UEAwwSR0VNLlJDQTMgVEVTVC1PTkxZMFowFAYHKoZIzj0C 9 | AQYJKyQDAwIIAQEHA0IABG+raY8OSxIEfrDwz4K4K1HXLXbd0ZzAKtD9SUDtSexn 10 | fsai8lkY8rM59TLky//HB8QDkyZewRPXClwpXCrj5HOjgZ4wgZswHQYDVR0OBBYE 11 | FAeQMy11U15/+Mg3v37JJldo3zjSMEIGCCsGAQUFBwEBBDYwNDAyBggrBgEFBQcw 12 | AYYmaHR0cDovL29jc3Aucm9vdC1jYS50aS1kaWVuc3RlLmRlL29jc3AwDwYDVR0T 13 | AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwFQYDVR0gBA4wDDAKBggqghQATASB 14 | IzAKBggqhkjOPQQDAgNIADBFAiEAo4kNteSBVR4ovNeTBhkiSXsWzdRC0tQeMfIt 15 | sE0s7/8CIDZ3EQxclVBV3huM8Bzl9ePbNsV+Lvnjv+Fo1om5+xJ2 16 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /Sources/COpenSSL/define_wrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef define_wrapper_h 2 | #define define_wrapper_h 3 | #include 4 | #include 5 | #include 6 | 7 | static const int EVP_PKEY_KEYPAIR_W = EVP_PKEY_KEYPAIR; 8 | static const int EVP_PKEY_PUBLIC_KEY_W = EVP_PKEY_PUBLIC_KEY; 9 | static const char* OSSL_PKEY_PARAM_GROUP_NAME_W = OSSL_PKEY_PARAM_GROUP_NAME; 10 | static const char* OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT_W = OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT; 11 | static const char* OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_COMPRESSED_W = OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_COMPRESSED; 12 | static const char* OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_UNCOMPRESSED_W = OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_UNCOMPRESSED; 13 | static const char* OSSL_PKEY_PARAM_PUB_KEY_W = OSSL_PKEY_PARAM_PUB_KEY; 14 | static const char* OSSL_PKEY_PARAM_PRIV_KEY_W = OSSL_PKEY_PARAM_PRIV_KEY; 15 | static const char* EVP_PKEY_CTX_NAME_EC = "EC"; // no constant defined 16 | 17 | EVP_PKEY *EVP_EC_gen_wrapped(const char *curve); 18 | 19 | #endif /* define_wrapper_h */ 20 | -------------------------------------------------------------------------------- /Sources/OpenSSL/OpenSSLError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | import Foundation 22 | 23 | public struct OpenSSLError: Swift.Error { 24 | public let name: String 25 | public let info: [String: String] = [:] 26 | 27 | public init(name: String) { 28 | self.name = name 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Resources.bundle/X509/GEM.DISCOVERY-DOC-TEST-ONLY.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICsTCCAligAwIBAgIHA61I5ACUjTAKBggqhkjOPQQDAjCBhDELMAkGA1UEBhMC 3 | REUxHzAdBgNVBAoMFmdlbWF0aWsgR21iSCBOT1QtVkFMSUQxMjAwBgNVBAsMKUtv 4 | bXBvbmVudGVuLUNBIGRlciBUZWxlbWF0aWtpbmZyYXN0cnVrdHVyMSAwHgYDVQQD 5 | DBdHRU0uS09NUC1DQTEwIFRFU1QtT05MWTAeFw0yMDA4MDQwMDAwMDBaFw0yNTA4 6 | MDQyMzU5NTlaMEkxCzAJBgNVBAYTAkRFMSYwJAYDVQQKDB1nZW1hdGlrIFRFU1Qt 7 | T05MWSAtIE5PVC1WQUxJRDESMBAGA1UEAwwJSURQIFNpZyAxMFowFAYHKoZIzj0C 8 | AQYJKyQDAwIIAQEHA0IABJZQrG1NWxIB3kz/6Z2zojlkJqN3vJXZ3EZnJ6JXTXw5 9 | ZDFZ5XjwWmtgfomv3VOV7qzI5ycUSJysMWDEu3mqRcajge0wgeowHQYDVR0OBBYE 10 | FJ8DVLAZWT+BlojTD4MT/Na+ES8YMDgGCCsGAQUFBwEBBCwwKjAoBggrBgEFBQcw 11 | AYYcaHR0cDovL2VoY2EuZ2VtYXRpay5kZS9vY3NwLzAMBgNVHRMBAf8EAjAAMCEG 12 | A1UdIAQaMBgwCgYIKoIUAEwEgUswCgYIKoIUAEwEgSMwHwYDVR0jBBgwFoAUKPD4 13 | 5qnId8xDRduartc6g6wOD6gwLQYFKyQIAwMEJDAiMCAwHjAcMBowDAwKSURQLURp 14 | ZW5zdDAKBggqghQATASCBDAOBgNVHQ8BAf8EBAMCB4AwCgYIKoZIzj0EAwIDRwAw 15 | RAIgVBPhAwyX8HAVH0O0b3+VazpBAWkQNjkEVRkv+EYX1e8CIFdn4O+nivM+XVi9 16 | xiKK4dW1R7MD334OpOPTFjeEhIVV 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/KeyExchange/BrainpoolP256r1KeyGenerationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @testable import OpenSSL 22 | import XCTest 23 | 24 | final class BrainpoolP256r1KeyGenerationTests: XCTestCase { 25 | func testBrainpoolP256r1GenerateKey() throws { 26 | XCTAssertNotNil(try BrainpoolP256r1.KeyExchange.generateKey()) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Resources.bundle/X509/c.fd.enc-erp-erpserverReferenz.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC7jCCApWgAwIBAgIHATwrYu8gtzAKBggqhkjOPQQDAjCBhDELMAkGA1UEBhMC 3 | REUxHzAdBgNVBAoMFmdlbWF0aWsgR21iSCBOT1QtVkFMSUQxMjAwBgNVBAsMKUtv 4 | bXBvbmVudGVuLUNBIGRlciBUZWxlbWF0aWtpbmZyYXN0cnVrdHVyMSAwHgYDVQQD 5 | DBdHRU0uS09NUC1DQTEwIFRFU1QtT05MWTAeFw0yMDEwMDcwMDAwMDBaFw0yNTA4 6 | MDcwMDAwMDBaMF4xCzAJBgNVBAYTAkRFMSYwJAYDVQQKDB1nZW1hdGlrIFRFU1Qt 7 | T05MWSAtIE5PVC1WQUxJRDEnMCUGA1UEAwweRVJQIFJlZmVyZW56ZW50d2lja2x1 8 | bmcgRkQgRW5jMFowFAYHKoZIzj0CAQYJKyQDAwIIAQEHA0IABKYLzjl704qFX+oE 9 | uUOyLV70i2Bn2K4jekh/YOxExtdADB3X/q7fX/tVr09GtDRxe3h1yov9TwuHaHYh 10 | 91RlyMejggEUMIIBEDAMBgNVHRMBAf8EAjAAMCEGA1UdIAQaMBgwCgYIKoIUAEwE 11 | gSMwCgYIKoIUAEwEgUowHQYDVR0OBBYEFK5+wVL9g8tGve6b1MdHK1xs62H7MDgG 12 | CCsGAQUFBwEBBCwwKjAoBggrBgEFBQcwAYYcaHR0cDovL2VoY2EuZ2VtYXRpay5k 13 | ZS9vY3NwLzAOBgNVHQ8BAf8EBAMCAwgwUwYFKyQIAwMESjBIMEYwRDBCMEAwMgww 14 | RS1SZXplcHQgdmVydHJhdWVuc3fDvHJkaWdlIEF1c2bDvGhydW5nc3VtZ2VidW5n 15 | MAoGCCqCFABMBIICMB8GA1UdIwQYMBaAFCjw+OapyHfMQ0Xbmq7XOoOsDg+oMAoG 16 | CCqGSM49BAMCA0cAMEQCIGZ20lLY2WEAGOTmNEFBB1EeU645fE0Iy2U9ypFHMlw4 17 | AiAVEP0HYut0Z8sKUk6WVanMmKXjfxO/qgQFzjsbq954dw== 18 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Resources.bundle/X509/GEM.KOMP-CA10-TEST-ONLY.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDGjCCAr+gAwIBAgIBFzAKBggqhkjOPQQDAjCBgTELMAkGA1UEBhMCREUxHzAd 3 | BgNVBAoMFmdlbWF0aWsgR21iSCBOT1QtVkFMSUQxNDAyBgNVBAsMK1plbnRyYWxl 4 | IFJvb3QtQ0EgZGVyIFRlbGVtYXRpa2luZnJhc3RydWt0dXIxGzAZBgNVBAMMEkdF 5 | TS5SQ0EzIFRFU1QtT05MWTAeFw0xNzA4MzAxMTM2MjJaFw0yNTA4MjgxMTM2MjFa 6 | MIGEMQswCQYDVQQGEwJERTEfMB0GA1UECgwWZ2VtYXRpayBHbWJIIE5PVC1WQUxJ 7 | RDEyMDAGA1UECwwpS29tcG9uZW50ZW4tQ0EgZGVyIFRlbGVtYXRpa2luZnJhc3Ry 8 | dWt0dXIxIDAeBgNVBAMMF0dFTS5LT01QLUNBMTAgVEVTVC1PTkxZMFowFAYHKoZI 9 | zj0CAQYJKyQDAwIIAQEHA0IABDFinQgzfsT1CN0QWwdm7e2JiaDYHocCiy1TWpOP 10 | yHwoPC54RULeUIBJeX199Qm1FFpgeIRP1E8cjbHGNsRbju6jggEgMIIBHDAdBgNV 11 | HQ4EFgQUKPD45qnId8xDRduartc6g6wOD6gwHwYDVR0jBBgwFoAUB5AzLXVTXn/4 12 | yDe/fskmV2jfONIwQgYIKwYBBQUHAQEENjA0MDIGCCsGAQUFBzABhiZodHRwOi8v 13 | b2NzcC5yb290LWNhLnRpLWRpZW5zdGUuZGUvb2NzcDASBgNVHRMBAf8ECDAGAQH/ 14 | AgEAMA4GA1UdDwEB/wQEAwIBBjAVBgNVHSAEDjAMMAoGCCqCFABMBIEjMFsGA1Ud 15 | EQRUMFKgUAYDVQQKoEkMR2dlbWF0aWsgR2VzZWxsc2NoYWZ0IGbDvHIgVGVsZW1h 16 | dGlrYW53ZW5kdW5nZW4gZGVyIEdlc3VuZGhlaXRza2FydGUgbWJIMAoGCCqGSM49 17 | BAMCA0kAMEYCIQCprLtIIRx1Y4mKHlNngOVAf6D7rkYSa723oRyX7J2qwgIhAKPi 18 | 9GSJyYp4gMTFeZkqvj8pcAqxNR9UKV7UYBlHrdxC 19 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /Config/ios-conf.patch: -------------------------------------------------------------------------------- 1 | 1747a1748,1771 2 | > "ios64sim-cross" => { 3 | > inherit_from => [ "ios-common" ], 4 | > cflags => add("-arch x86_64 -DOPENSSL_NO_ASM -mios-version-min=7.0.0 -isysroot \$(CROSS_TOP)/SDKs/\$(CROSS_SDK) -fno-common"), 5 | > module_ldflags => "-shared", 6 | > LDFLAGS => "-shared", 7 | > sys_id => "iOS", 8 | > bn_ops => "SIXTY_FOUR_BIT_LONG RC4_CHAR", 9 | > perlasm_scheme => "ios64", 10 | > }, 11 | > "ios64sim-arm64-cross" => { 12 | > inherit_from => [ "darwin-common" ], 13 | > CC => "xcrun -sdk iphonesimulator cc", 14 | > cflags => add("-arch arm64 -DOPENSSL_NO_ASM -mios-simulator-version-min=14.0.0 -isysroot \$(CROSS_TOP)/SDKs/\$(CROSS_SDK) -fno-common"), 15 | > sys_id => "iOS", 16 | > module_ldflags => "-shared", 17 | > LDFLAGS => "-shared", 18 | > bn_ops => "SIXTY_FOUR_BIT_LONG RC4_CHAR", 19 | > perlasm_scheme => "ios64", 20 | > }, 21 | > "ios64-cross-fix" => { 22 | > inherit_from => [ "ios64-cross" ], 23 | > module_ldflags => "-shared", 24 | > LDFLAGS => "-shared", 25 | > }, 26 | -------------------------------------------------------------------------------- /Sources/OpenSSL/String+CChar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | import Foundation 22 | 23 | extension String { 24 | var asCChar: UnsafeMutablePointer { 25 | let count = utf8.count + 1 26 | let result = UnsafeMutablePointer.allocate(capacity: count) 27 | withCString { baseAddress in 28 | // func initialize(from: UnsafePointer, count: Int) 29 | result.initialize(from: baseAddress, count: count) 30 | } 31 | return result 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Hash/HashTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @testable import OpenSSL 22 | import XCTest 23 | 24 | final class HashTests: XCTestCase { 25 | func testSha256() { 26 | let expectedHash = Data( 27 | [0xAF, 0xAC, 0xEC, 0xF8, 0x26, 0x4F, 0xDF, 0x05, 0x97, 0x75, 0xF3, 0x33, 0xD5, 0x0C, 0x00, 0x87, 28 | 0x31, 0xDE, 0xCD, 0x80, 0xE9, 0x77, 0x38, 0x41, 0x4C, 0x4D, 0x4D, 0x1E, 0xD4, 0x02, 0x63, 0x19] 29 | ) 30 | let hashable = "Hallotlghn" 31 | 32 | XCTAssertEqual(Hash.SHA256.hash(string: hashable), expectedHash) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Certificate/ResourceFileReader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | import Foundation 22 | 23 | class ResourceFileReader { 24 | static func readFileInResourceBundle(filePath: String, for bundle: Bundle) throws -> Data { 25 | guard let url = bundle.resourceURL? 26 | .appendingPathComponent("Resources.bundle") 27 | .appendingPathComponent(filePath) 28 | else { 29 | throw Error.fileNotFound(filePath) 30 | } 31 | 32 | return try Data(contentsOf: url, options: .mappedIfSafe) 33 | } 34 | 35 | enum Error: Swift.Error { 36 | case fileNotFound(String) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Resources.bundle/OCSP/vau-oscp-response.der.base64: -------------------------------------------------------------------------------- 1 | MIIEVQoBAKCCBE4wggRKBgkrBgEFBQcwAQEEggQ7MIIENzCCAQ2hYjBgMQswCQYDVQQGEwJERTEmMCQGA1UECgwdYXJ2YXRvIFN5c3RlbXMgR21iSCBOT1QtVkFMSUQxKTAnBgNVBAMMIEtvbXAtQ0EyOCBPQ1NQLVNpZ25lcjMgVEVTVC1PTkxZGA8yMDI1MDMwNDA3NDM1NFowgZUwgZIwOzAJBgUrDgMCGgUABBT8X79XfV64wqxpB3xpQTrtT+Z95AQUAGo4kPOZriGPUtruwYxugK1hIskCAiRkgAAYDzIwMjUwMzA0MDc0MzU0WqFAMD4wPAYFKyQIAw0EMzAxMA0GCWCGSAFlAwQCAQUABCCgLhMDx789ZyyhhqnQTlU+vsneAba3iJxV6S9+CFqnyTAKBggqhkjOPQQDAgNIADBFAiEAkA5mVKSA/KNr0VWuLI98rCMmRTa72xLyrg2JtvReni4CIBQQ0qEAfM/p/SED+084zh4K4Wj1IkYVUabOcl6yE0AkoIICzDCCAsgwggLEMIICaqADAgECAgIkeTAKBggqhkjOPQQDAjCBhDELMAkGA1UEBhMCREUxHzAdBgNVBAoMFmdlbWF0aWsgR21iSCBOT1QtVkFMSUQxMjAwBgNVBAsMKUtvbXBvbmVudGVuLUNBIGRlciBUZWxlbWF0aWtpbmZyYXN0cnVrdHVyMSAwHgYDVQQDDBdHRU0uS09NUC1DQTI4IFRFU1QtT05MWTAeFw0yNDAxMTEwOTEzMzFaFw0yNjA2MTgwOTQ5NThaMGAxCzAJBgNVBAYTAkRFMSYwJAYDVQQKDB1hcnZhdG8gU3lzdGVtcyBHbWJIIE5PVC1WQUxJRDEpMCcGA1UEAwwgS29tcC1DQTI4IE9DU1AtU2lnbmVyMyBURVNULU9OTFkwWjAUBgcqhkjOPQIBBgkrJAMDAggBAQcDQgAEhqwMPqH61aF8lamHXNXpY6wv3gZeGbdzV6ar8uQJwFp5JKfsvD2h7Ro1KI4b+q1K9I6htPuQL/Pab8UrQunMFaOB7TCB6jAdBgNVHQ4EFgQUIKPkXU//d3MA/bbfSAxhb9invPwwHwYDVR0jBBgwFoAUAGo4kPOZriGPUtruwYxugK1hIskwTQYIKwYBBQUHAQEEQTA/MD0GCCsGAQUFBzABhjFodHRwOi8vZG93bmxvYWQtdGVzdHJlZi5jcmwudGktZGllbnN0ZS5kZS9vY3NwL2VjMA4GA1UdDwEB/wQEAwIGQDAVBgNVHSAEDjAMMAoGCCqCFABMBIEjMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwkwDwYJKwYBBQUHMAEFBAIFADAKBggqhkjOPQQDAgNIADBFAiBvLUqw4S1b3aqCEd5U9bpuEheqfutJ+VlVWKvpxUyipgIhAIyaNHncCd4l4KwDolFPmJsm/wYezSBl4uBu/4yH5oPD -------------------------------------------------------------------------------- /Sources/OpenSSL/EC/BrainpoolP256r1.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | import Foundation 22 | 23 | /// The BrainpoolP256r1 Elliptic Curve. 24 | /// 25 | /// [REQ:gemSpec_Krypt:GS-A_4361] 26 | /// [REQ:gemSpec_Krypt:GS-A_4357] 27 | public enum BrainpoolP256r1 {} 28 | 29 | extension BrainpoolP256r1 { 30 | struct Curve: ECCurve { 31 | static var name: String = OpenSSLECGroup.Name.brainpoolP256r1.rawValue 32 | 33 | static var group: OpenSSLECGroup { 34 | guard let group = try? OpenSSLECGroup(curve: .brainpoolP256r1) else { 35 | preconditionFailure("BrainpoolP256r1 OpenSSL ECGroup could not be initialized") 36 | } 37 | return group 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Resources.bundle/X509/PHARMACY.ADELHEID-EC-TEST-ONLY.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID+DCCA5+gAwIBAgIHALv/lvONGDAKBggqhkjOPQQDAjCBmjELMAkGA1UEBhMC 3 | REUxHzAdBgNVBAoMFmdlbWF0aWsgR21iSCBOT1QtVkFMSUQxSDBGBgNVBAsMP0lu 4 | c3RpdHV0aW9uIGRlcyBHZXN1bmRoZWl0c3dlc2Vucy1DQSBkZXIgVGVsZW1hdGlr 5 | aW5mcmFzdHJ1a3R1cjEgMB4GA1UEAwwXR0VNLlNNQ0ItQ0E1MSBURVNULU9OTFkw 6 | HhcNMjMwMTI1MjMwMDAwWhcNMjgwMTI1MjI1OTU5WjCB+DELMAkGA1UEBhMCREUx 7 | DDAKBgNVBAgMA1VsbTEUMBIGA1UEBwwLSW5nb2xmc3RhZHQxDjAMBgNVBBEMBTEy 8 | MzQ2MRUwEwYDVQQJDAxIYXVwdHN0ci4gMTExITAfBgNVBAoMGDMtMi0yMDIzMDEy 9 | NC0xIE5PVC1WQUxJRDEgMB4GA1UEBRMXMTYuODAyNzYwMDEwMTE2OTk5MDIzMDEx 10 | FDASBgNVBAQMC1VsbWVuZG9yZmVyMREwDwYDVQQqDAhBZGVsaGVpZDEwMC4GA1UE 11 | AwwnQXBvdGhla2UgQWRlbGhlaWQgVWxtZW5kb3JmZXIgVEVTVC1PTkxZMFowFAYH 12 | KoZIzj0CAQYJKyQDAwIIAQEHA0IABIt9O6MwSMzM3FIN8+/QeLLH419hU/Z5BpkJ 13 | LVpz2o2HZ7vYATrvaAPqkRmdPDRYTcMsjl6yYQhLlTTb6QplZiejggFtMIIBaTAO 14 | BgNVHQ8BAf8EBAMCAwgwDAYDVR0TAQH/BAIwADBcBgNVHSAEVTBTMDsGCCqCFABM 15 | BIEjMC8wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cuZ2VtYXRpay5kZS9nby9wb2xp 16 | Y2llczAJBgcqghQATARMMAkGByqCFABMBGUwTgYFKyQIAwMERTBDMEEwPzA9MDsw 17 | FwwVw5ZmZmVudGxpY2hlIEFwb3RoZWtlMAkGByqCFABMBDYTFTMtMDEuMi4yMDIz 18 | MDAxLjE2LjEwMTAfBgNVHSMEGDAWgBQGmOkCVf/Jn1yjZQ7xXeIg9YT7kzAdBgNV 19 | HQ4EFgQUV1SAEWk6W6ORPAkLkl4tv+a3dgcwOAYIKwYBBQUHAQEELDAqMCgGCCsG 20 | AQUFBzABhhxodHRwOi8vZWhjYS5nZW1hdGlrLmRlL29jc3AvMCEGA1UdEQQaMBig 21 | FgYDVQQDoA8MDU9ldGtlci1HcnVwcGUwCgYIKoZIzj0EAwIDRwAwRAIgKcJKdOEt 22 | iEAb/i3tf2uVuMn+kXewNzt+9ndW5Vr9cUoCIFTgZx/sGG0GZZPLM4UqZpQ0UVU5 23 | 2x/P288I/fZiSm41 24 | -----END CERTIFICATE----- 25 | -------------------------------------------------------------------------------- /devops/Jenkinsfile_GitHubAttachToRelease: -------------------------------------------------------------------------------- 1 | import org.jenkinsci.plugins.pipeline.modeldefinition.Utils 2 | 3 | pipeline { 4 | agent { label 'IOSDEV2' } 5 | 6 | triggers { 7 | gitlab( 8 | triggerOnNoteRequest: true, 9 | noteRegex: "[jJ]enkins" 10 | ) 11 | } 12 | 13 | environment { 14 | KEYCHAIN_PASSWORD = credentials('KEYCHAIN_PASSWORD') 15 | NEXUS_CREDENTIALS = credentials('Nexus') 16 | GITHUB_API_TOKEN = credentials('GITHUB.API.Token.Publish') 17 | } 18 | 19 | parameters { 20 | string(name: 'NEXUS_ARTEFACT_PATH', defaultValue: '', description: 'Artefact path in the Nexus repository. e.g. de/gematik/OpenSSL-Swift/4.3.2/OpenSSL_6_f647ffd.xcframework.zip') 21 | string(name: 'TAG', defaultValue: '', description: 'Tag of the GitHub release (draft) to attach the xcframework to') 22 | 23 | } 24 | 25 | options { 26 | ansiColor('xterm') 27 | copyArtifactPermission('*') 28 | } 29 | 30 | stages { 31 | stage('Download xcframework from Nexus and attach to GitHub release') { 32 | steps { 33 | sh label: 'Download xcframework from Nexus and attach to GitHub release', script: '''#!/bin/bash -l 34 | if [ -f $HOME/.bash_profile ]; then source $HOME/.bash_profile; fi 35 | 36 | security -v unlock-keychain -p "${KEYCHAIN_PASSWORD}" ~/Library/Keychains/login.keychain 37 | 38 | bundle exec fastlane download_xcframework_from_nexus_and_attach_to_github_release 39 | ''' 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ---- 3 | 4 | # Installation 5 | 6 | Make sure you have the latest version of the Xcode command line tools installed: 7 | 8 | ```sh 9 | xcode-select --install 10 | ``` 11 | 12 | For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane) 13 | 14 | # Available Actions 15 | 16 | ### cibuild 17 | 18 | ```sh 19 | [bundle exec] fastlane cibuild 20 | ``` 21 | 22 | 23 | 24 | ### test 25 | 26 | ```sh 27 | [bundle exec] fastlane test 28 | ``` 29 | 30 | 31 | 32 | ### build_all 33 | 34 | ```sh 35 | [bundle exec] fastlane build_all 36 | ``` 37 | 38 | 39 | 40 | ### static_code_analysis 41 | 42 | ```sh 43 | [bundle exec] fastlane static_code_analysis 44 | ``` 45 | 46 | 47 | 48 | ### generate_documentation 49 | 50 | ```sh 51 | [bundle exec] fastlane generate_documentation 52 | ``` 53 | 54 | Lane that (auto) genarates API documentation from inline comments. 55 | 56 | ### build_xcframework 57 | 58 | ```sh 59 | [bundle exec] fastlane build_xcframework 60 | ``` 61 | 62 | 63 | 64 | ### download_xcframework_from_nexus_and_attach_to_github_release 65 | 66 | ```sh 67 | [bundle exec] fastlane download_xcframework_from_nexus_and_attach_to_github_release 68 | ``` 69 | 70 | 71 | 72 | ---- 73 | 74 | This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. 75 | 76 | More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools). 77 | 78 | The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 79 | -------------------------------------------------------------------------------- /project.yml: -------------------------------------------------------------------------------- 1 | name: OpenSSL-Swift 2 | options: 3 | bundleIdPrefix: de.gematik.ti.swift.openssl 4 | createIntermediateGroups: true 5 | deploymentTarget: 6 | macOS: 10.15 7 | iOS: 13.0 8 | settings: 9 | base: 10 | CURRENT_PROJECT_VERSION: -1 11 | CODE_SIGN_IDENTITY: "" 12 | SWIFT_VERSION: 5.0 13 | ALWAYS_SEARCH_USER_PATHS: NO 14 | FRAMEWORK_SEARCH_PATHS: "$(inherited)" 15 | BUILD_LIBRARY_FOR_DISTRIBUTION: YES 16 | configs: 17 | Release: 18 | SWIFT_COMPILATION_MODE: wholemodule 19 | Debug: 20 | SWIFT_COMPILATION_MODE: incremental 21 | targets: 22 | OpenSSL: 23 | configFiles: 24 | Debug: Sources/OpenSSL/Resources/debug_${platform}.xcconfig 25 | Release: Sources/OpenSSL/Resources/release_${platform}.xcconfig 26 | type: framework 27 | platform: [iOS,macOS] 28 | sources: 29 | - path: Sources/OpenSSL 30 | excludes: 31 | - "**/.swiftlint.yml" 32 | - path: Sources/COpenSSL 33 | - path: lib/include 34 | copyFiles: 35 | destination: productsDirectory 36 | subpath: include/openssl 37 | preBuildScripts: 38 | - name: "Install OpenSSL" 39 | script: | 40 | cd "$SRCROOT" 41 | ./scripts/install_openssl 42 | scheme: 43 | testTargets: 44 | - OpenSSLTests_${platform} 45 | gatherCoverageData: true 46 | OpenSSLTests: 47 | type: bundle.unit-test 48 | platform: [iOS,macOS] 49 | settings: 50 | INFOPLIST_FILE: Tests/OpenSSLTests/Resources/Info.plist 51 | sources: 52 | - Tests/OpenSSLTests 53 | dependencies: 54 | - target: OpenSSL_${platform} 55 | buildImplicitDependencies: true 56 | -------------------------------------------------------------------------------- /Sources/OpenSSL/KeyExchange/DiffieHellman.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @_implementationOnly import COpenSSL 22 | import Foundation 23 | 24 | /// DiffieHellman sharedSecret protocol 25 | public protocol DiffieHellman { 26 | /// The associated PublicKey type 27 | associatedtype PublicKey 28 | 29 | /// Compute a shared secret using the given public key 30 | /// 31 | /// - Note: This protocol assumes the implementation has access [or represents] the private or 32 | /// pre-shared secret information. 33 | /// 34 | /// - Parameter peerKey: the public key material to derive a shared secret in conjunction 35 | /// with its own private information. 36 | /// - Returns: The shared secret in raw bytes 37 | /// - Throws: `OpenSSLError` in case this operation was not supported 38 | func sharedSecret(with peerKey: PublicKey) throws -> Data 39 | } 40 | -------------------------------------------------------------------------------- /Sources/OpenSSL/Hash/Hash.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | import CryptoKit 22 | import Foundation 23 | 24 | /// Hashing package 25 | public enum Hash {} 26 | 27 | /// SHA256 extension on Hash 28 | public extension Hash { // swiftlint:disable:this no_extension_access_modifier 29 | /// SHA-256 Hash 30 | enum SHA256 { 31 | /// Hash the given data with SHA-256 32 | /// 33 | /// - Parameter data: hash input 34 | /// - Returns: SHA-256 hash 35 | public static func hash(data: Data) -> Data { 36 | Data(CryptoKit.SHA256.hash(data: data)) 37 | } 38 | 39 | /// Hash the given string with SHA-256 40 | /// 41 | /// - Note: the String will be UTF-8 encoded before hashing it. 42 | /// - Parameter string: hash input 43 | /// - Returns: SHA-256 hash 44 | public static func hash(string: String) -> Data { 45 | guard let data = string.data(using: .utf8) else { 46 | preconditionFailure("String could not be utf8 encoded") 47 | } 48 | return hash(data: data) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Data+Hex.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | import Foundation 22 | 23 | extension DataProtocol { 24 | var data: Data { .init(self) } 25 | var hexa: String { map { .init(format: "%02x", $0) }.joined() } 26 | } 27 | 28 | struct HexaError: Error {} 29 | 30 | extension StringProtocol { 31 | func hexa() throws -> [UInt8] { 32 | var startIndex = startIndex 33 | return try (0 ..< count / 2).map { _ in 34 | let endIndex = index(after: startIndex) 35 | defer { startIndex = index(after: endIndex) } 36 | guard let byte = UInt8(self[startIndex ... endIndex], radix: 16) 37 | else { throw HexaError() } 38 | return byte 39 | } 40 | } 41 | } 42 | 43 | extension Data { 44 | /// Helping function to init Data from `hex` String 45 | public init(hex hexString: String) throws { 46 | try self.init(hexString.hexa().data) 47 | } 48 | } 49 | 50 | extension Data { 51 | /// Helping function to output a hexadecimal representation of `self` 52 | public func hexString() -> String { 53 | hexa 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: # rule identifiers to exclude from running 2 | - trailing_comma 3 | - operator_whitespace 4 | opt_in_rules: 5 | - attributes 6 | - empty_count 7 | - force_unwrapping 8 | - unneeded_parentheses_in_closure_argument 9 | - unavailable_function 10 | - trailing_closure 11 | - strict_fileprivate 12 | - sorted_imports 13 | - sorted_first_last 14 | - single_test_class 15 | - required_enum_case 16 | - redundant_type_annotation 17 | - redundant_nil_coalescing 18 | - prohibited_super_call 19 | - override_in_extension 20 | - overridden_super_call 21 | - operator_usage_whitespace 22 | - no_extension_access_modifier 23 | - multiline_function_chains 24 | - multiline_arguments 25 | - modifier_order 26 | - missing_docs 27 | - lower_acl_than_parent 28 | - literal_expression_end_indentation 29 | - first_where 30 | - file_name 31 | - fatal_error_message 32 | - explicit_init 33 | - empty_xctest_method 34 | - empty_string 35 | - discouraged_optional_collection 36 | - closure_end_indentation 37 | excluded: # paths to ignore during linting. Takes precedence over `included`. 38 | - Package.swift 39 | - .build/ 40 | - vendor/ 41 | - Carthage/ 42 | - DerivedData/ 43 | nesting: 44 | type_level: 3 45 | line_length: 120 46 | file_length: 47 | warning: 500 48 | error: 1200 49 | type_name: 50 | min_length: 3 51 | max_length: 52 | warning: 40 53 | error: 50 54 | allowed_symbols: ["_Previews"] 55 | custom_rules: 56 | nimble_fail_with_description: 57 | included: ".*Test\\.swift" 58 | name: "Fail with description" 59 | regex: "(Nimble.fail\\(\\))" 60 | message: "Failures need a description" 61 | severity: warning 62 | must_not_contain_author: 63 | included: 64 | - ".*Test\\.swift" 65 | - ".*Sources\\.swift" 66 | name: "must not contain author" 67 | regex: "(\/\/[[:space:]]*Created by)" 68 | message: "Source must not contain author" 69 | severity: warning 70 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Resources.bundle/X509/PHARMACY.ADELHEID-RSA-TEST-ONLY.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFJTCCBA2gAwIBAgIHAy3++TwYvTANBgkqhkiG9w0BAQsFADCBmjELMAkGA1UE 3 | BhMCREUxHzAdBgNVBAoMFmdlbWF0aWsgR21iSCBOT1QtVkFMSUQxSDBGBgNVBAsM 4 | P0luc3RpdHV0aW9uIGRlcyBHZXN1bmRoZWl0c3dlc2Vucy1DQSBkZXIgVGVsZW1h 5 | dGlraW5mcmFzdHJ1a3R1cjEgMB4GA1UEAwwXR0VNLlNNQ0ItQ0E0MSBURVNULU9O 6 | TFkwHhcNMjMwMTI1MjMwMDAwWhcNMjgwMTI1MjI1OTU5WjCB+DELMAkGA1UEBhMC 7 | REUxDDAKBgNVBAgMA1VsbTEUMBIGA1UEBwwLSW5nb2xmc3RhZHQxDjAMBgNVBBEM 8 | BTEyMzQ2MRUwEwYDVQQJDAxIYXVwdHN0ci4gMTExITAfBgNVBAoMGDMtMi0yMDIz 9 | MDEyNC0xIE5PVC1WQUxJRDEgMB4GA1UEBRMXMTYuODAyNzYwMDEwMTE2OTk5MDIz 10 | MDExFDASBgNVBAQMC1VsbWVuZG9yZmVyMREwDwYDVQQqDAhBZGVsaGVpZDEwMC4G 11 | A1UEAwwnQXBvdGhla2UgQWRlbGhlaWQgVWxtZW5kb3JmZXIgVEVTVC1PTkxZMIIB 12 | IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoldSTGb4p9vPAMh8xObq7I1Y 13 | m5kwi0e7/elMKCqxv6ZWQUoBVy1j5XpApLsS5V8foDghv0CszT9ro9g8DQ5JLKhy 14 | jBdiFp8aHxXENx4Mf/c83sVNmvDNi5XaertVp7yGFWqQQ5OdzhN0T1IHzfHTkX/m 15 | qpygIkiTbhlr4TxoZkb8L+BFqjIg4kG8J2Dupe2AAEdpEmqqFBywF8Zce5st13Fv 16 | 2GIBTNcEXzxsgyHZLLL7k4rZ74ia1Z7oiVC10sxA8EnjXFBAL78WqI3RDGT/SmR4 17 | hYFUDcNMtvYw4CsvFr+RLP/+DG2T1ziqQLn2dCb5ulr0rGt/H4Rzu30VBKo2xwID 18 | AQABo4IBDjCCAQowHwYDVR0jBBgwFoAUuvg6WBH2huoEom3Eeuu5s/pLb8IwHQYD 19 | VR0OBBYEFBEnAhX7lSpGcNurxYxIz6eEPKLEMAwGA1UdEwEB/wQCMAAwOAYIKwYB 20 | BQUHAQEELDAqMCgGCCsGAQUFBzABhhxodHRwOi8vZWhjYS5nZW1hdGlrLmRlL29j 21 | c3AvME4GBSskCAMDBEUwQzBBMD8wPTA7MBcMFcOWZmZlbnRsaWNoZSBBcG90aGVr 22 | ZTAJBgcqghQATAQ2ExUzLTAxLjIuMjAyMzAwMS4xNi4xMDEwDgYDVR0PAQH/BAQD 23 | AgQwMCAGA1UdIAQZMBcwCgYIKoIUAEwEgSMwCQYHKoIUAEwETDANBgkqhkiG9w0B 24 | AQsFAAOCAQEAPr/1uepXo+PgTFnbnrhFvXAvsLIBbrQe10cK6Y0Do0ZmWjqKNpvx 25 | iVqGnPPyKjbM2QxIx0tGcXpXqyPH5lBt6tsFyBa3Nu8tiR9I8JWTXeLEq+4wMjzk 26 | kqE8bNsE+eozZNRyZlUvS4obXJAYCwvgEBMVR9IQ6pizGAVzyq3uSdRNwC4e15At 27 | gg4ZqUCL9wq8B4Ih80qWHX3Zf+86rJA6SFi09Yj5sZvb14x/ikc4hI/9TVTVTGsW 28 | l4ZZh2OE5zzPgTFXbdt9AeOSr9bhgYEsf6x7WODcRGZQ4RbamOoa0urjgP9WKIdK 29 | qzX8yC1xrEbAAjAhKvznz91JzAcYLGicSg== 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.7 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | // 5 | // Copyright (Change Date see Readme), gematik GmbH 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | // ******* 20 | // 21 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 22 | // 23 | 24 | import PackageDescription 25 | 26 | let package = Package( 27 | name: "OpenSSL-Swift", 28 | platforms: [ 29 | .iOS(.v13), .macOS(.v12) 30 | ], 31 | products: [ 32 | // Products define the executables and libraries a package produces, and make them visible to other packages. 33 | .library( 34 | name: "OpenSSL-Swift", 35 | targets: ["OpenSSL"]), 36 | ], 37 | dependencies: [ 38 | // Dependencies declare other packages that this package depends on. 39 | ], 40 | targets: [ 41 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 42 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 43 | .binaryTarget( 44 | name: "OpenSSL", 45 | url: "https://github.com/gematik/OpenSSL-Swift/releases/download/4.6.0/OpenSSL.xcframework.zip", 46 | checksum: "d587e0f790d0a80616e77d88e1feaa33c50bfb46156c2a7bbb6f96c7eea952a6" 47 | ) 48 | ] 49 | ) 50 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Signature/ECDSASignatureTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | import Foundation 22 | @testable import OpenSSL 23 | import XCTest 24 | 25 | final class ECDSASignatureTest: XCTestCase { 26 | func testECDSASignatureRawRepresentation() { 27 | let rawSignature = 28 | try! Data( 29 | hex: "1ead1d24d3966a388bd64c31ce1a9ba86393767ab8b937302a26f0f68049c024607ac0358c5ace66bc6c8c5e3eeead5a7456d0a8e046c1642e3e68e6957e84a5" 30 | ) 31 | let signature = try! ECDSASignature(rawRepresentation: rawSignature) 32 | XCTAssertEqual(rawSignature, signature.rawBytes) 33 | } 34 | 35 | func testECDSASignatureDER() { 36 | let derSignature = try! Data( 37 | hex: "3046022100A893CCFB5530AEDB9173378B3AC912DD765CFFA5CA1B249B4821DB7E82F60DEA0221009842AC2C38E85A60E6A85824076BD484BDC377DBB31BC293D137D7CB8D5CAB05" 38 | ) 39 | let signature = try! ECDSASignature(derRepresentation: derSignature) 40 | let expectedRaw = try! Data(hex: 41 | "A893CCFB5530AEDB9173378B3AC912DD765CFFA5CA1B249B4821DB7E82F60DEA9842AC2C38E85A60E6A85824076BD484BDC377DBB31BC293D137D7CB8D5CAB05") 42 | XCTAssertEqual(expectedRaw, signature.rawBytes) 43 | XCTAssertEqual(derSignature, signature.derBytes) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ReleaseNotes.md: -------------------------------------------------------------------------------- 1 | # Release 4.6.0 2 | 3 | - Change license from EUPL to Apache License 2.0 4 | - Upgrade Xcode version to 16.4 5 | - Fix code verification in attach-binary-to-github lane 6 | - Align xcframework build and artifact naming with SPM requirements 7 | 8 | # Release 4.5.0 9 | 10 | - Introduce time injection in X509 and OCSPResponse verification checks 11 | 12 | # Release 4.4.1 13 | 14 | - Re-sign xcframework 15 | 16 | # Release 4.4.0 17 | 18 | - Update OpenSSL to 3.4.0 19 | - Fix some outdated test data 20 | - Add Jenkinsfile for attaching an asset to GitHub releases 21 | 22 | # Release 4.3.1 23 | 24 | - Fix an unit test 25 | - Re-sign xcframework 26 | 27 | # Release 4.3.0 28 | 29 | - Add PrivacyInfo.xcprivacy 30 | - Sign xcframework with Apple Distribution 31 | 32 | # Release 4.2.0 33 | 34 | - Upgrade OpenSSL version to 3.2.1 35 | - Remove dependency on DataKit 36 | 37 | # Release 4.1.0 38 | 39 | - Upgrade OpenSSL version to 3.1.0 40 | - Introduce Package.swift for importing 41 | 42 | # Release 4.0.0 43 | 44 | - Modify ECPublicKey's export API 45 | - Change ECPrivateKeyImpl's underlying data type to EVP_PKEY 46 | - Remove all usage of deprecated low level OpenSSL-API 47 | - Make EllipticCurvePoint generic in a Curve type 48 | - Upgrade OpenSSL version to 3.0.7 49 | - Change Xcode version to 14.0 50 | 51 | # Release 3.0.3 52 | 53 | - Fix CMAC method `aes128cbc(key:, data:) throws -> Data` 54 | - Change Xcode version to 13.3.1 55 | - Upgrade OpenSSL version to 3.0.3 56 | 57 | # Release 3.0.2 58 | 59 | # OpenSSL 3.0.2 60 | 61 | - Upgrade to OpenSSL 3.0.2 62 | - Fix some of the introduced deprecations 63 | - Add support for CMSContentInfo Encryption 64 | 65 | # Release 1.0.1 66 | 67 | - SDK 15 68 | - update OpenSSL to 1.1.1n 69 | 70 | # Release 1.0.0 71 | Initial release with support/wrappers for: 72 | 73 | - X.509 Certificate 74 | - OCSP Response 75 | - Key Management 76 | - Generate Private Key (KeyPair) 77 | - ECDH Shared Secret computation 78 | - Verify BrainpoolP256r1 signature 79 | - PACE protocol map Nonce for Shared Secret derivation 80 | - MAC calculation 81 | 82 | 83 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Resources.bundle/OCSP/vau-ocsp-response-no-signing-ca.der.base64: -------------------------------------------------------------------------------- 1 | MIIHBQoBAKCCBv4wggb6BgkrBgEFBQcwAQEEggbrMIIG5zCCASShgYEwfzELMAkGA1UEBhMCREUxJjAkBgNVBAoMHWdlbWF0aWsgVEVTVC1PTkxZIC0gTk9ULVZBTElEMSIwIAYDVQQDDBlpbXYwMS5vcnMxLnRlbGVtYXRpay10ZXN0MSQwIgYDVQQFExsxMDAwMC1HT1JJeElNVnhUTFN4Q3gwMDB4MDAYDzIwMjEwMzI1MTI1NDMxWjBoMGYwPjAHBgUrDgMCGgQUXI3/vEvbfD7eUzpI1js5mj5OUC4EFCjw+OapyHfMQ0Xbmq7XOoOsDg+oAgcBPCti7yC3gAAYDzIwMjEwMzI1MTI1NDMxWqARGA8yMDIxMDMyNTEyNTQzMVqhIzAhMB8GCSsGAQUFBzABAgQSBBAyiaTFc4ZALNjCGFo2CXWxMA0GCSqGSIb3DQEBBQUAA4IBAQChZb3eRpmaqs/TACgNKD1cqtjOcY21Sm5PruRufbb68lE1eIVtNMS6Hkx1TK+mSm0Vbvd7GG9owfsxhbY/O+hH+ICp0jFHxmBhuEEsaltQaMMehs0jzDjj4s+b11t/bah8iU9SGThFhvIO8JxgNjx/0c8JdRCChJk+7/HC5F/ahZ4fPnW0OHofd6bINFd65mJkZ3xrecM+gP7TT0AuNNYc/s9wJgzLKimq+caunYhZCBZLKuKhlQKF8KOi7/BwHtOV4qbaLeb3cPnocycTBDMONjKHUV4nt71mIpt9F/n9xBEPCYWzWlw3mBXK7FV+2Km+pkh3mIujpoqqFBBnNuHpoIIEpzCCBKMwggSfMIIDh6ADAgECAgMB0Z0wDQYJKoZIhvcNAQELBQAwaDELMAkGA1UEBhMCREUxHzAdBgNVBAoMFmdlbWF0aWsgR21iSCBOT1QtVkFMSUQxFDASBgNVBAsMC0tvbXBvbmVudGVuMSIwIAYDVQQDDBlDLkdFTS5LT01QLUNBMDEgVEVTVC1PTkxZMB4XDTE0MDIwMzE0MjQ1OVoXDTE2MDIwMzE0MjQ1OVowfzELMAkGA1UEBhMCREUxJjAkBgNVBAoMHWdlbWF0aWsgVEVTVC1PTkxZIC0gTk9ULVZBTElEMSIwIAYDVQQDDBlpbXYwMS5vcnMxLnRlbGVtYXRpay10ZXN0MSQwIgYDVQQFExsxMDAwMC1HT1JJeElNVnhUTFN4Q3gwMDB4MDAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDG3QzOTVDGuYpJwH3wOTn0y4rJJ9XZdYLnZ+MxrbDsXEEhJqcmPbbwoksWPOo3G2GJuzjV4JdTbjOxZYOQyilZu/BwVAFokoPyLfhUFfoDSDutgSacHjzn2M8qOmE9Qd29zbvTJoc0Nl1fyqmxWWBMOWZSucjm5Ocsl2ui0+WgrzUvSYNgmDh3irWgVpKuKkkHQ3BPGE+7NEmt0+MKbxY6oRoQMQEx+LWEUJ/Rbw8H8ku+C9nImRjsaIR4bPI2jgDA4L01bUSI9V3wWendx+uUvNT8aNb1vd2/9bVChYY2l2YLS4jW3H9N/U80VccTFCWtXTK+ZtzipepnT2+/azKFAgMBAAGjggE5MIIBNTAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMBEGA1UdDgQKBAhCxeAORGKLXjAhBgNVHSAEGjAYMAoGCCqCFABMBIEoMAoGCCqCFABMBIEjMDQGBSskCAMDBCswKTAnMCUwIzAhMBMMEUludGVybWVkacOkciBWU0RNMAoGCCqCFABMBIEfMBMGA1UdIwQMMAqACEJVIH3uhsRUMEsGCCsGAQUFBwEBBD8wPTA7BggrBgEFBQcwAYYvaHR0cDovL29jc3AucGtpLnRlbGVtYXRpay10ZXN0OjgwODAvQ01PQ1NQL09DU1AwDgYDVR0PAQH/BAQDAgWgMDIGA1UdHwQrMCkwJ6AloCOGIWh0dHA6Ly9vY3NwLnBraS50ZWxlbWF0aWstdGVzdDo4MjANBgkqhkiG9w0BAQsFAAOCAQEAZsYVlrrHe4GRHl65hVe9ftQB0c6uideLcgR/Zy/90sFhM02j5yimJdiELkW7nnrVk1XE8PTCzC9IzG/2bmAETEZnsMt/yWlKHsEuOz3cSLdPKjkkizumHxYIeATCvKovYKUSAXJcdRdJ6raI4BaCeFPgeEPazwmWw8Zq9o35e2DUahQf2KeyZFhYEZ4hO1OEMqErq82ktuWcYFGC+X8M8bMGQUE4PEh7SYDrtMjueoNbuJaEwwK7q9aeUUUMez5mm4X0gJCRvZAdoac9G8zXFKeeBB8UmwUXoTG+GYY2et8RwHpsV5djmiwBU7D+ZILpcB0KVjWulK4ZcSkPAO5GlA== -------------------------------------------------------------------------------- /Tests/OpenSSLTests/KeyExchange/BrainpoolP256r1ExtPACETests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @testable import OpenSSL 22 | import XCTest 23 | 24 | final class BrainpoolP256r1ExtPACETests: XCTestCase { 25 | func testPaceMapNonceBrainpoolP256r1() throws { 26 | // given 27 | let nonce = try Data(hex: "A44248628B8E8B94072EF3843C56E844") 28 | let ownPrivateKey1Raw = try Data(hex: "0D7DFFAC3558C4C3C075A0479F4C3A4864DBD8E686CDB154DD0BDD0BA7CE4D51") 29 | let ownPrivateKey1 = try ECPrivateKeyImpl(raw: ownPrivateKey1Raw) 30 | let peerKeyRaw = 31 | try Data( 32 | hex: "045CAC41779F548CBE714A08CBCEB40F616B5EFDD59DD3345802027DCB0C3FB02B20DC7A458B7744102DE98D350D4399FEC0F8CC5CCE50317A2CEE3CB418A4DA41" // swiftlint:disable:this line_length 33 | ) 34 | let peerKey1 = try ECPublicKeyImpl(x962: peerKeyRaw) 35 | 36 | let ownPrivateKey2Raw = try Data(hex: "4C164B01D17B7C097B3640AF1EBCE0C88ED4B57738803872EEC3261EBB9A89E7") 37 | let keyPairGen = { try ECPrivateKeyImpl(raw: ownPrivateKey2Raw) } 38 | 39 | // when 40 | let (ownPubKey2, keyPair2) = try ownPrivateKey1 41 | .paceMapNonce(nonce: nonce, peerKey1: peerKey1, keyPair: keyPairGen) 42 | 43 | // then 44 | let expectedPubKey2Raw = 45 | try Data( 46 | hex: "04A1D37688F62647E4B7CCEB64881142EEEC48FCF148BA2B518E3246166EF8495C81D0644A59DD6927E7492A4BD52926957450BEDED208B2E616D03D9504F9FE12" // swiftlint:disable:this line_length 47 | ) 48 | XCTAssertEqual(try ownPubKey2.rawValue(), expectedPubKey2Raw) 49 | XCTAssertEqual(try keyPair2.publicKey.rawValue(), try keyPairGen().publicKey.rawValue()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/.DS_Store 2 | /.build 3 | /Packages 4 | lib/* 5 | !lib/include 6 | lib/include/* 7 | !lib/include/.gitkeep 8 | tmp/ 9 | openssl_*/ 10 | .swiftpm 11 | DownLoads/ 12 | 13 | # Temporarily downloaded file 14 | openssl-*.tar.gz 15 | 16 | # Generated documentation 17 | docs/ 18 | 19 | # AppCode project files 20 | .idea/ 21 | 22 | .vscode/ 23 | 24 | # Third party files 25 | vendor/ 26 | .bundle/ 27 | 28 | # Xcode 29 | # 30 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 31 | 32 | ## Build generated 33 | build/ 34 | DerivedData/ 35 | 36 | ## Various settings 37 | *.pbxuser 38 | !default.pbxuser 39 | *.mode1v3 40 | !default.mode1v3 41 | *.mode2v3 42 | !default.mode2v3 43 | *.perspectivev3 44 | !default.perspectivev3 45 | xcuserdata/ 46 | 47 | ## Other 48 | *.moved-aside 49 | *.xccheckout 50 | *.xcscmblueprint 51 | 52 | ## Obj-C/Swift specific 53 | *.hmap 54 | *.ipa 55 | *.dSYM.zip 56 | *.dSYM 57 | 58 | ## Playgrounds 59 | timeline.xctimeline 60 | playground.xcworkspace 61 | 62 | # Swift Package Manager 63 | # 64 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 65 | # Packages/ 66 | # Package.pins 67 | # Package.resolved 68 | .build/ 69 | 70 | # CocoaPods 71 | # 72 | # We recommend against adding the Pods directory to your .gitignore. However 73 | # you should judge for yourself, the pros and cons are mentioned at: 74 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 75 | # 76 | # Pods/ 77 | # 78 | # Add this line if you want to avoid checking in source code from the Xcode workspace 79 | # *.xcworkspace 80 | 81 | # Carthage 82 | # 83 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 84 | Carthage/Checkouts 85 | Carthage/Build 86 | 87 | # fastlane 88 | # 89 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 90 | # screenshots whenever they are needed. 91 | # For more information about the recommended setup visit: 92 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 93 | 94 | fastlane/report.xml 95 | fastlane/Preview.html 96 | fastlane/screenshots/**/*.png 97 | fastlane/test_output 98 | artifacts/ 99 | 100 | # Code Injection 101 | # 102 | # After new code Injection tools there's a generated folder /iOSInjectionProject 103 | # https://github.com/johnno1962/injectionforxcode 104 | 105 | iOSInjectionProject/ 106 | 107 | # R.swift generated 108 | *.generated.swift 109 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/EC/ECPublicKeyTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @testable import OpenSSL 22 | import XCTest 23 | 24 | final class ECPublicKeyTests: XCTestCase { 25 | func testBrainpoolP256r1PublicKey_InitData() throws { 26 | // given 27 | let pubKeyX962 = 28 | try Data( 29 | hex: "048634212830DAD457CA05305E6687134166B9C21A65FFEBF555F4E75DFB04888866E4B6843624CBDA43C97EA89968BC41FD53576F82C03EFA7D601B9FACAC2B29" // swiftlint:disable:this line_length 30 | ) 31 | 32 | // when 33 | let sut: ECPublicKey = try ECPublicKeyImpl(data: pubKeyX962) 34 | 35 | // then 36 | XCTAssertNotNil(sut) 37 | XCTAssertEqual(try sut.rawValue(), pubKeyX962) 38 | 39 | let expectedCompactKey = try Data(hex: "038634212830DAD457CA05305E6687134166B9C21A65FFEBF555F4E75DFB048888") 40 | XCTAssertEqual(try sut.compactValue(), expectedCompactKey) 41 | } 42 | 43 | func testBrainpoolP256r1PublicKey_InitCompact() throws { 44 | // given 45 | let compactKey = try Data(hex: "038634212830DAD457CA05305E6687134166B9C21A65FFEBF555F4E75DFB048888") 46 | 47 | // when 48 | let sut = try ECPublicKeyImpl(compact: compactKey) 49 | 50 | // then 51 | XCTAssertNotNil(sut) 52 | XCTAssertEqual(try sut.compactValue(), compactKey) 53 | 54 | let expectedPubKeyX962 = 55 | try Data( 56 | hex: "048634212830DAD457CA05305E6687134166B9C21A65FFEBF555F4E75DFB04888866E4B6843624CBDA43C97EA89968BC41FD53576F82C03EFA7D601B9FACAC2B29" // swiftlint:disable:this line_length 57 | ) 58 | XCTAssertEqual(try sut.rawValue(), expectedPubKeyX962) 59 | } 60 | 61 | func testBrainpoolP256r1_ThrowsInitEmpty() throws { 62 | XCTAssertThrowsError(try ECPublicKeyImpl(x962: Data())) 63 | XCTAssertThrowsError(try ECPublicKeyImpl(compact: Data())) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sources/OpenSSL/EC/OpenSSLECGroup.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @_implementationOnly import COpenSSL 22 | import Foundation 23 | 24 | /// Elliptic-Curve protocol 25 | public protocol ECCurve { 26 | /// Name of the ECCurve (group) in OpenSSL (e.g. "brainpoolP256r1", "P-256", etc ...) 27 | static var name: String { get } 28 | /// Elliptic-Curve group information 29 | static var group: OpenSSLECGroup { get } 30 | } 31 | 32 | /// Elliptic-Curve information in accordance with OpenSSL 33 | public class OpenSSLECGroup { 34 | @usableFromInline let curve: OpaquePointer 35 | 36 | /// Initialize a Curve by `OpenSSLECGroup.Name` 37 | /// 38 | /// - Parameter curve: curve name 39 | /// - Throws: `OpenSSLError` when the curve could not be found by the underlying OpenSSL implementation 40 | public init(curve: Name) throws { 41 | guard let opensslGroup = EC_GROUP_new_by_curve_name(curve.nid) else { 42 | throw OpenSSLError(name: "EC Curve not found: [\(curve.nid)]") 43 | } 44 | self.curve = opensslGroup 45 | } 46 | 47 | /// De-init OpenSSLECGroup 48 | deinit { 49 | EC_GROUP_free(curve) 50 | } 51 | 52 | @inlinable 53 | func withUnsafeGroupPointer(_ body: (OpaquePointer) throws -> T) rethrows -> T { 54 | try body(curve) 55 | } 56 | } 57 | 58 | extension OpenSSLECGroup { 59 | /// Curves 60 | public enum Name: String { 61 | /// BrainpoolP256r1 as defined in https://tools.ietf.org/html/rfc5639 62 | case brainpoolP256r1 63 | 64 | var nid: CInt { 65 | switch self { 66 | // [REQ:gemSpec_Krypt:GS-A_4361] (For now only) BrainpoolP256r1 curve support 67 | // [REQ:gemSpec_Krypt:GS-A_4357] (For now only) BrainpoolP256r1 curve support 68 | case .brainpoolP256r1: return NID_brainpoolP256r1 69 | } 70 | } 71 | } 72 | } 73 | 74 | extension OpenSSLECGroup { 75 | @usableFromInline var coordinateByteCount: Int { 76 | (Int(EC_GROUP_get_degree(curve)) + 7) / 8 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Sources/OpenSSL/Signature/Signature.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | import Foundation 22 | 23 | /// Typealias for Digest 24 | public typealias Digest = Data 25 | 26 | /// Protocol for message signer 27 | public protocol Signer { 28 | /// Type signature type 29 | associatedtype Signature 30 | /// Sign a message 31 | /// 32 | /// - Parameter message: message to hash and then sign 33 | /// - Returns: the signature 34 | /// - Throws: `OpenSSLError` 35 | func sign(message: Data) throws -> Signature 36 | } 37 | 38 | /// Protocol for digest signers 39 | public protocol DigestSigner { 40 | /// Signature type 41 | associatedtype Signature 42 | /// Sign a digest 43 | /// 44 | /// - Parameter digest: the hash to be signed/encrypted 45 | /// - Returns: the signature 46 | /// - Throws: `OpenSSLError` 47 | func sign(digest: Digest) throws -> Signature 48 | } 49 | 50 | /// Protocol for message verifiers 51 | public protocol SignatureVerifier { 52 | /// The signature type 53 | associatedtype Signature 54 | /// Verify a signature for the given message 55 | /// 56 | /// - Note: message will be hashed by the function before verification 57 | /// 58 | /// - Parameters: 59 | /// - signature: the signature for the given message 60 | /// - message: the message to verify 61 | /// - Returns: true when the message is signed with given signature 62 | /// - Throws: `OpenSSLError` 63 | func verify(signature: Signature, message: Data) throws -> Bool 64 | } 65 | 66 | /// Protocol for EC Signature types 67 | public protocol ECSignature { 68 | /// Initialize from rawRepresentation 69 | /// 70 | /// - Parameter rawRepresentation: raw signature value 71 | /// - Throws: `OpenSSLError` 72 | init(rawRepresentation: Data) throws 73 | /// Initialize from DER encoded data 74 | /// 75 | /// - Parameter derRepresentation: ASN.1 DER encoded signature 76 | /// - Throws: `OpenSSLError` 77 | init(derRepresentation: Data) throws 78 | /// ASN.1 DER encoded signature 79 | var derRepresentation: Data { get } 80 | /// Raw signature data 81 | var rawRepresentation: Data { get } 82 | } 83 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/KeyExchange/BrainpoolP256r1ExtDiffieHellmanTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @testable import OpenSSL 22 | import XCTest 23 | 24 | final class BrainpoolP256r1ExtDiffieHellmanTests: XCTestCase { 25 | func testDeriveSharedSecretFromBrainpoolP256r1() throws { 26 | let pubKeyRaw = 27 | try Data( 28 | hex: "048634212830DAD457CA05305E6687134166B9C21A65FFEBF555F4E75DFB04888866E4B6843624CBDA43C97EA89968BC41FD53576F82C03EFA7D601B9FACAC2B29" // swiftlint:disable:this line_length 29 | ) 30 | let privateKeyRaw = try Data(hex: "83456D98DEA3435C166385A4E644EBCA588E8A0AA7C811F51FCC736368630206") 31 | let expectedSharedSecret = try Data(hex: "29AAA0349B1A3AA99501EA0D087E05F0E7A51C356693BE5BA010CA615C7BB5FD") 32 | let pubKey = try BrainpoolP256r1.KeyExchange.PublicKey(x962: pubKeyRaw) 33 | let privateKey = try BrainpoolP256r1.KeyExchange.PrivateKey(raw: privateKeyRaw) 34 | let sharedSecretData = try privateKey.sharedSecret(with: pubKey) 35 | 36 | XCTAssertNotEqual(try privateKey.publicKey.rawValue(), try pubKey.rawValue()) 37 | XCTAssertEqual(expectedSharedSecret, sharedSecretData) 38 | } 39 | 40 | func testDeriveSharedSecretFromBrainpoolP256r1_2() throws { 41 | let pubKeyRaw = 42 | try Data( 43 | hex: "048634212830DAD457CA05305E6687134166B9C21A65FFEBF555F4E75DFB04888866E4B6843624CBDA43C97EA89968BC41FD53576F82C03EFA7D601B9FACAC2B29" // swiftlint:disable:this line_length 44 | ) 45 | let privateKeyRaw = try Data(hex: "5bbba34d47502bd588ed680dfa2309ca375eb7a35ddbbd67cc7f8b6b687a1c1d") 46 | let expectedSharedSecret = try Data(hex: "9656c2b4b3da81d0385f6a1ee60e93b91828fd90231c923d53ce7bbbcd58ceaa") 47 | let pubKey = try BrainpoolP256r1.KeyExchange.PublicKey(x962: pubKeyRaw) 48 | let privateKey = try BrainpoolP256r1.KeyExchange.PrivateKey(raw: privateKeyRaw) 49 | let sharedSecretData = try privateKey.sharedSecret(with: pubKey) 50 | 51 | XCTAssertNotEqual(try privateKey.publicKey.rawValue(), try pubKey.rawValue()) 52 | XCTAssertEqual(expectedSharedSecret, sharedSecretData) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/EC/ECPrivateKeyTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @testable import OpenSSL 22 | import XCTest 23 | 24 | final class ECPrivateKeyTests: XCTestCase { 25 | func testGenerateKey() throws { 26 | // when 27 | let sut = try ECPrivateKeyImpl() 28 | 29 | // then 30 | XCTAssertNotNil(sut) 31 | XCTAssertEqual(try sut.publicKey.x962Value().count, 65) 32 | XCTAssertEqual(try sut.publicKey.compactValue().count, 33) 33 | } 34 | 35 | func testBrainpoolP256r1_InitX962() throws { 36 | // given 37 | let keyX962 = 38 | try Data( 39 | hex: "040AB68E9435DCA456983930A62770461AC7F0C5E5DFC6D93032702E3213168248A21E1DF599CCD1832037101DEF5926069DE865EE48BBC3ED92DA273EFE935CC783456D98DEA3435C166385A4E644EBCA588E8A0AA7C811F51FCC736368630206" // swiftlint:disable:this line_length 40 | ) 41 | 42 | // when 43 | let sut = try ECPrivateKeyImpl(x962: keyX962) 44 | 45 | // then 46 | XCTAssertNotNil(sut) 47 | 48 | let expectedRawPrivateKeyData = 49 | try Data(hex: "83456D98DEA3435C166385A4E644EBCA588E8A0AA7C811F51FCC736368630206") 50 | XCTAssertEqual(try sut.rawPrivateKeyData(), expectedRawPrivateKeyData) 51 | 52 | let expectCompactPublicKey = try Data(hex: "030AB68E9435DCA456983930A62770461AC7F0C5E5DFC6D93032702E3213168248") 53 | XCTAssertEqual(try sut.publicKey.compactValue(), expectCompactPublicKey) 54 | } 55 | 56 | func testBrainpoolP256r1_InitRaw() throws { 57 | // given 58 | let rawKey = try Data(hex: "5BBBA34D47502BD588ED680DFA2309CA375EB7A35DDBBD67CC7F8B6B687A1C1D") 59 | 60 | // when 61 | let sut = try ECPrivateKeyImpl(raw: rawKey) 62 | 63 | // then 64 | XCTAssertNotNil(sut) 65 | 66 | let expectedPubKeyX962 = 67 | try Data( 68 | hex: "04754E548941E5CD073FED6D734578A484BE9F0BBFA1B6FA3168ED7FFB22878F0F9AEF9BBD932A020D8828367BD080A3E72B36C41EE40C87253F9B1B0BEB8371BF" // swiftlint:disable:this line_length 69 | ) 70 | XCTAssertEqual(try sut.publicKey.x962Value(), expectedPubKeyX962) 71 | } 72 | 73 | func testBrainpoolP256r1_ThrowsInitEmpty() throws { 74 | XCTAssertThrowsError(try ECPrivateKeyImpl(x962: Data())) 75 | XCTAssertThrowsError(try ECPrivateKeyImpl(raw: Data())) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Sources/OpenSSL/FiniteBigNumberFieldArithmeticContext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @_implementationOnly import COpenSSL 22 | import Foundation 23 | 24 | /// A context for performing mathematical operations on BigNumber over a finite field. 25 | /// 26 | /// A common part of elliptic curve mathematics is to perform arithmetic operations over a finite field. These require 27 | /// performing modular arithmetic, and cannot be processed in the same way as regular math on these integers. 28 | /// 29 | /// Most operations we perform over finite fields are part of repeated, larger arithmetic operations, so this object 30 | /// also manages the lifetime of a `BN_CTX`. While `BN_CTX` is a silly data type, it does still have the effect of 31 | /// caching existing `BIGNUM`s, so it's not a terrible idea to use it here. 32 | @usableFromInline 33 | class FiniteBigNumberFieldArithmeticContext { 34 | private var fieldSize: BigNumber 35 | private var bnCtx: OpaquePointer 36 | 37 | @usableFromInline 38 | init(fieldSize: BigNumber) throws { 39 | self.fieldSize = fieldSize 40 | guard let bnCtx = BN_CTX_new() else { 41 | throw OpenSSLError(name: "Unable to allocate BN_CTX_new") 42 | } 43 | BN_CTX_start(bnCtx) 44 | self.bnCtx = bnCtx 45 | } 46 | 47 | deinit { 48 | BN_CTX_end(bnCtx) 49 | BN_CTX_free(bnCtx) 50 | } 51 | } 52 | 53 | // MARK: - Arithmetic operations 54 | 55 | extension FiniteBigNumberFieldArithmeticContext { 56 | @usableFromInline 57 | func subtract(_ x: BigNumber, from y: BigNumber) -> BigNumber? { // swiftlint:disable:this identifier_name 58 | let output = BigNumber() 59 | 60 | let resultCode = x.withUnsafeBignumPointer { xPointer in 61 | y.withUnsafeBignumPointer { yPointer in 62 | self.fieldSize.withUnsafeBignumPointer { fieldSizePointer in 63 | output.withUnsafeBignumPointer { outputPointer in 64 | // Note the order of y and x. 65 | BN_mod_sub( 66 | outputPointer, 67 | yPointer, 68 | xPointer, 69 | fieldSizePointer, 70 | self.bnCtx 71 | ) 72 | } 73 | } 74 | } 75 | } 76 | 77 | guard resultCode == 1 else { 78 | return nil 79 | } 80 | 81 | return output 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /devops/Jenkinsfile: -------------------------------------------------------------------------------- 1 | import org.jenkinsci.plugins.pipeline.modeldefinition.Utils 2 | 3 | pipeline { 4 | agent { label 'IOSDEV2' } 5 | 6 | triggers { 7 | gitlab( 8 | triggerOnNoteRequest: true, 9 | noteRegex: "[jJ]enkins" 10 | ) 11 | } 12 | 13 | environment { 14 | KEYCHAIN_PASSWORD = credentials('KEYCHAIN_PASSWORD') 15 | NEXUS_CREDENTIALS = credentials('Nexus') 16 | 17 | MATCH_PASSWORD = credentials('MATCH_PASSWORD') 18 | MATCH_USERNAME = credentials('MATCH_USERNAME') 19 | MATCH_GIT_URL = credentials('MATCH_GIT_URL') 20 | } 21 | 22 | parameters { 23 | booleanParam(name: 'BUILD_AND_UPLOAD_XCFRAMEWORK', defaultValue: false, description: 'Manual trigger for building xcframework and uploading to Nexus') 24 | } 25 | 26 | options { 27 | ansiColor('xterm') 28 | copyArtifactPermission('*') 29 | } 30 | 31 | stages { 32 | stage('Fastlane cibuild') { 33 | steps { 34 | sh label: 'starting ios test run', script: '''#!/bin/bash -l 35 | if [ -f $HOME/.bash_profile ]; then source $HOME/.bash_profile; fi 36 | 37 | security -v unlock-keychain -p "${KEYCHAIN_PASSWORD}" ~/Library/Keychains/login.keychain 38 | 39 | make cibuild 40 | ''' 41 | } 42 | } 43 | 44 | stage('Build xcframework and upload to Nexus') { 45 | when { 46 | anyOf { 47 | branch pattern: "xcframework/.*", comparator: "REGEXP"; 48 | expression { 49 | return params.BUILD_AND_UPLOAD_XCFRAMEWORK 50 | } 51 | } 52 | } 53 | 54 | steps { 55 | sh label: 'Build xcframework', script: '''#!/bin/bash -l 56 | if [ -f $HOME/.bash_profile ]; then source $HOME/.bash_profile; fi 57 | 58 | security -v unlock-keychain -p "${KEYCHAIN_PASSWORD}" ~/Library/Keychains/login.keychain 59 | 60 | bundle exec fastlane build_xcframework 61 | ''' 62 | 63 | sh label: 'Upload xcframework', script: '''#!/bin/bash -l 64 | if [ -f $HOME/.bash_profile ]; then source $HOME/.bash_profile; fi 65 | 66 | set -ev 67 | 68 | # At this point we expect a branch name like xcframework/1.29.0 69 | # Remove everything before the last "/" (if exists) from BRANCH_NAME 70 | VERSION=$(echo "$BRANCH_NAME" | sed 's|.*/||') 71 | GIT_HASH=$(git rev-parse --short HEAD) 72 | curl -u "${NEXUS_CREDENTIALS}" --http1.1 --upload-file "artifacts/xcframework/OpenSSL_${BUILD_NUMBER}.xcframework.zip" "https://nexus.prod.ccs.gematik.solutions/repository/Apps/de/gematik/OpenSSL-Swift/${VERSION}/OpenSSL_${BUILD_NUMBER}_${GIT_HASH}.xcframework.zip" 73 | curl -u "${NEXUS_CREDENTIALS}" --http1.1 --upload-file "artifacts/xcframework/OpenSSL_${BUILD_NUMBER}.xcframework.zip.checksum" "https://nexus.prod.ccs.gematik.solutions/repository/Apps/de/gematik/OpenSSL-Swift/${VERSION}/OpenSSL_${BUILD_NUMBER}_${GIT_HASH}.xcframework.zip.checksum" 74 | 75 | ''' 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Mac/CMACTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @testable import OpenSSL 22 | import XCTest 23 | 24 | final class CMACTests: XCTestCase { 25 | func testAes128CbcCmacEmptyMessage() throws { 26 | // given 27 | let key = try Data(hex: "2b7e151628aed2a6abf7158809cf4f3c") 28 | let message = try Data(hex: "") 29 | 30 | // when 31 | let cmac = try CMAC.aes128cbc(key: key, data: message) 32 | let cmacOneShot = try CMAC.aes128cbc(key: key, data: message) 33 | 34 | // then 35 | let expectedCmac = try Data(hex: "bb1d6929e95937287fa37d129b756746") 36 | XCTAssertEqual(cmac, expectedCmac) 37 | XCTAssertEqual(cmacOneShot, expectedCmac) 38 | } 39 | 40 | func testAes128CbcCmacMessageLength16() throws { 41 | // given 42 | let key = try Data(hex: "2b7e151628aed2a6abf7158809cf4f3c") 43 | let message = try Data(hex: "6bc1bee22e409f96e93d7e117393172a") 44 | 45 | // when 46 | let cmac = try CMAC.aes128cbc(key: key, data: message) 47 | let cmacOneShot = try CMAC.aes128cbc(key: key, data: message) 48 | 49 | // then 50 | let expectedCmac = try Data(hex: "070a16b46b4d4144f79bdd9dd04a287c") 51 | XCTAssertEqual(cmac, expectedCmac) 52 | XCTAssertEqual(cmacOneShot, expectedCmac) 53 | } 54 | 55 | func testAes128CbcCmacMessageLength64() throws { 56 | // given 57 | let key = try Data(hex: "2b7e151628aed2a6abf7158809cf4f3c") 58 | let message = 59 | try Data( 60 | hex: 61 | "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710" // swiftlint:disable:this line_length 62 | ) 63 | 64 | // when 65 | let cmac = try CMAC.aes128cbc(key: key, data: message) 66 | let cmacOneShot = try CMAC.aes128cbc(key: key, data: message) 67 | 68 | // then 69 | let expectedCmac = try Data(hex: "51f0bebf7e3b9d92fc49741779363cfe") 70 | XCTAssertEqual(cmac, expectedCmac) 71 | XCTAssertEqual(cmacOneShot, expectedCmac) 72 | } 73 | 74 | func testAes128CbcCmacInvalidKey() throws { 75 | // given 76 | let message = try Data(hex: "") 77 | 78 | // when 79 | let invalidKeyShort = try Data(hex: "2b7e151628aed2a6abf7158809cf4f") 80 | let invalidKeyLong = try Data(hex: "2b7e151628aed2a6abf7158809cf4f3c00") 81 | 82 | // then 83 | XCTAssertThrowsError(try CMAC.aes128cbc(key: invalidKeyShort, data: message)) 84 | XCTAssertThrowsError(try CMAC.aes128cbc(key: invalidKeyLong, data: message)) 85 | XCTAssertThrowsError(try CMAC.aes128cbc(key: invalidKeyShort, data: message)) 86 | XCTAssertThrowsError(try CMAC.aes128cbc(key: invalidKeyLong, data: message)) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Sources/OpenSSL/BigNumber.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @_implementationOnly import COpenSSL 22 | import Foundation 23 | 24 | typealias BIGNUM = OpaquePointer 25 | 26 | @usableFromInline 27 | class BigNumber { 28 | private let backing: BIGNUM 29 | 30 | init() { 31 | backing = BN_new() 32 | } 33 | 34 | init(owningNoCopy original: BIGNUM) { 35 | backing = original 36 | } 37 | 38 | deinit { 39 | BN_clear_free(backing) 40 | } 41 | 42 | var rawBytes: Data { 43 | withUnsafeBignumPointer { bignum in 44 | let size = (BN_num_bits(bignum) + 7) / 8 45 | var bytes = [UInt8](repeating: 0x0, count: Int(size)) 46 | _ = BN_bn2bin(bignum, &bytes) 47 | return Data(bytes) 48 | } 49 | } 50 | } 51 | 52 | extension BigNumber { 53 | convenience init(bytes: ContiguousBytes) throws { 54 | self.init() 55 | 56 | let number: BIGNUM? = bytes.withUnsafeBytes { (bytesPointer: UnsafeRawBufferPointer) -> BIGNUM? in 57 | BN_bin2bn(bytesPointer.baseAddress?.assumingMemoryBound(to: UInt8.self), CInt(bytesPointer.count), backing) 58 | } 59 | guard number != nil else { 60 | throw OpenSSLError(name: "BIGNUM initialization error") 61 | } 62 | } 63 | 64 | convenience init(copying original: BIGNUM) { 65 | self.init() 66 | 67 | _ = withUnsafeBignumPointer { ptr in 68 | BN_copy(ptr, original) 69 | } 70 | } 71 | } 72 | 73 | extension BigNumber { 74 | func withUnsafeBignumPointer(_ body: (BIGNUM) throws -> T) rethrows -> T { 75 | try body(backing) 76 | } 77 | } 78 | 79 | // MARK: - Equatable 80 | 81 | extension BigNumber: Equatable { 82 | @inlinable 83 | static func ==(lhs: BigNumber, rhs: BigNumber) -> Bool { 84 | compare(lhs: lhs, rhs: rhs) == 0 85 | } 86 | } 87 | 88 | // MARK: - Comparable 89 | 90 | extension BigNumber: Comparable { 91 | @inlinable 92 | static func <(lhs: BigNumber, rhs: BigNumber) -> Bool { 93 | compare(lhs: lhs, rhs: rhs) < 0 94 | } 95 | 96 | @inlinable 97 | static func <=(lhs: BigNumber, rhs: BigNumber) -> Bool { 98 | compare(lhs: lhs, rhs: rhs) <= 0 99 | } 100 | 101 | @inlinable 102 | static func >(lhs: BigNumber, rhs: BigNumber) -> Bool { 103 | compare(lhs: lhs, rhs: rhs) > 0 104 | } 105 | 106 | @inlinable 107 | static func >=(lhs: BigNumber, rhs: BigNumber) -> Bool { 108 | compare(lhs: lhs, rhs: rhs) >= 0 109 | } 110 | } 111 | 112 | extension BigNumber { 113 | @usableFromInline 114 | static func compare(lhs: BigNumber, rhs: BigNumber) -> CInt { 115 | lhs.withUnsafeBignumPointer { lhsPtr in 116 | rhs.withUnsafeBignumPointer { rhsPtr in 117 | BN_cmp(lhsPtr, rhsPtr) 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Sources/OpenSSL/KeyExchange/BrainpoolP256r1+DiffieHellman.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | import Foundation 22 | 23 | /// Key exchange type for BrainpoolP256r1 24 | public extension BrainpoolP256r1 { // swiftlint:disable:this no_extension_access_modifier 25 | /// KeyExchange types for the BrainpoolP256r1 Elliptic Curve 26 | enum KeyExchange { 27 | /// The BrainpoolP256r1 Public Key for ECDH KeyExchange 28 | public struct PublicKey: ECPublicKey { 29 | let pubKey: ECPublicKeyImpl 30 | 31 | internal init(impl: ECPublicKeyImpl) { 32 | pubKey = impl 33 | } 34 | 35 | public init(compact: Data) throws { 36 | pubKey = try ECPublicKeyImpl(compact: compact) 37 | } 38 | 39 | public init(x962: Data) throws { 40 | pubKey = try ECPublicKeyImpl(x962: x962) 41 | } 42 | 43 | public func rawValue() throws -> Data { 44 | try pubKey.rawValue() 45 | } 46 | 47 | public func x962Value() throws -> Data { 48 | try pubKey.x962Value() 49 | } 50 | 51 | public func compactValue() throws -> Data { 52 | try pubKey.compactValue() 53 | } 54 | } 55 | 56 | /// The BrainpoolP256r1 Private Key for ECDH KeyExchange 57 | public struct PrivateKey: ECPrivateKey, DiffieHellman, PACE { 58 | private let key: ECPrivateKeyImpl 59 | 60 | init(key: ECPrivateKeyImpl) { 61 | self.key = key 62 | } 63 | 64 | public init(raw: Data) throws { 65 | key = try ECPrivateKeyImpl(raw: raw) 66 | } 67 | 68 | public init(x962: Data) throws { 69 | key = try ECPrivateKeyImpl(x962: x962) 70 | } 71 | 72 | public var publicKey: KeyExchange.PublicKey { 73 | KeyExchange.PublicKey(impl: key.publicKey) 74 | } 75 | 76 | public func sharedSecret(with peerKey: KeyExchange.PublicKey) throws -> Data { 77 | try key.sharedSecret(with: peerKey.pubKey) 78 | } 79 | 80 | public func paceMapNonce(nonce: Data, peerKey1: PublicKey) throws -> (PublicKey, PrivateKey) { 81 | let (pub, priv) = try key.paceMapNonce(nonce: nonce, peerKey1: peerKey1.pubKey) 82 | return (PublicKey(impl: pub), PrivateKey(key: priv)) 83 | } 84 | 85 | public static func generateKey() throws -> PrivateKey { 86 | try Self(key: ECPrivateKeyImpl.generateKey()) 87 | } 88 | } 89 | 90 | /// Generate a key 91 | /// 92 | /// - Returns: the generated key pair 93 | /// - Throws: when no key pair could be generated 94 | public static func generateKey() throws -> PrivateKey { 95 | try KeyExchange.PrivateKey.generateKey() 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /OpenSSL-Swift.xcodeproj/xcshareddata/xcschemes/OpenSSL_iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 38 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 64 | 70 | 71 | 72 | 73 | 79 | 81 | 87 | 88 | 89 | 90 | 92 | 93 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /OpenSSL-Swift.xcodeproj/xcshareddata/xcschemes/OpenSSL_macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 38 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 64 | 70 | 71 | 72 | 73 | 79 | 81 | 87 | 88 | 89 | 90 | 92 | 93 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /Sources/OpenSSL/KeyExchange/PACE.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @_implementationOnly import COpenSSL 22 | import Foundation 23 | 24 | /// PACE (Password Authenticated Connection Establishment) protocol 25 | public protocol PACE: DiffieHellman { 26 | /// The associated own key PrivateKey type 27 | associatedtype PrivateKey 28 | 29 | /// The PACE protocol sets up a SecureMessaging channel with strong session keys based on a shared password, 30 | /// possibly of low entropy. 31 | /// see: BSI TR-03110 https://www.bsi.bund.de/EN/Publications/TechnicalGuidelines/TR03110/BSITR03110.html 32 | /// 33 | /// - Parameters: 34 | /// - nonce: Plain nonce queried from the peer entity 35 | /// - peerKey1: First ephemeral public key received from the peer entity 36 | /// - Returns: Derived own `PublicKey` and the second (generated) `PrivateKey` (pair) 37 | /// - Throws: when no ephemeral public key could be generated 38 | func paceMapNonce(nonce: Data, peerKey1: PublicKey) throws -> (PublicKey, PrivateKey) 39 | } 40 | 41 | /// This implementation follows PACE protocol conformance as specified in gemSpec_COS_V3.11.0 (N085.064) 42 | /// for establishing a secure channel communication with German health cards. 43 | /// 44 | /// First calculate an ephemeral generator g~ = nonce * g + ownKey1.priv * peerKey1 45 | /// Then perform an ECDH key agreement using the new group generator g~ together with a second private key (pair). 46 | /// Return the derived second own public key and the second (generated) private key (pair). 47 | extension ECPrivateKeyImpl: PACE { 48 | public func paceMapNonce( 49 | nonce: Data, 50 | peerKey1: ECPublicKeyImpl 51 | ) throws -> (ECPublicKeyImpl, ECPrivateKeyImpl) { 52 | try paceMapNonce(nonce: nonce, peerKey1: peerKey1) { 53 | try Self.generateKey() 54 | } 55 | } 56 | 57 | func paceMapNonce( 58 | nonce: Data, 59 | peerKey1: ECPublicKeyImpl, 60 | keyPair generator: () throws -> PrivateKey 61 | ) throws -> (ECPublicKeyImpl, ECPrivateKeyImpl) { 62 | let gTilde = try paceMapNoncePart1(nonce: nonce, peerKey1: peerKey1) 63 | return try paceMapNoncePart2(gTilde: gTilde, keyPair: generator) 64 | } 65 | 66 | // Part 1: 67 | // Calculate ephemeral shared secret Point g~ = nonce * g + ownKey1.priv * peerKey1 68 | private func paceMapNoncePart1(nonce: Data, peerKey1: ECPublicKeyImpl) throws -> EllipticCurvePoint { 69 | // summand1 = nonce * G 70 | let nonce = try BigNumber(bytes: nonce) 71 | let summand1 = try EllipticCurvePoint(multiplyWithBasePoint: nonce) 72 | 73 | // summand2 = ownKey1.priv * peerKey1 74 | let summand2 = try multiply(with: EllipticCurvePoint(raw: peerKey1.rawValue())) 75 | 76 | // g~ = summand1 + summand2 77 | return try summand1.add(summand2) 78 | } 79 | 80 | // Part2: 81 | // Generate a second private key (pair) and return derived ownPubKey2 = privKey2 * g~ and keyPair2 82 | private func paceMapNoncePart2( 83 | gTilde: EllipticCurvePoint, 84 | keyPair generator: () throws -> PrivateKey 85 | ) throws -> (ECPublicKeyImpl, ECPrivateKeyImpl) { 86 | let privateKey2 = try generator() 87 | let ownPubKeyPoint2: EllipticCurvePoint = try privateKey2.multiply(with: gTilde) 88 | 89 | let ownPubKeyX962 = try ownPubKeyPoint2.export(pointConversion: .uncompressed) 90 | let ownPubKey = try ECPublicKeyImpl(x962: ownPubKeyX962) 91 | 92 | return (ownPubKey, privateKey2) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Sources/OpenSSL/Signature/BrainpoolP256r1+ECDSA.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | import Foundation 22 | 23 | /// Validation and signing with BrainpoolP256r1 for ECDSA 24 | public extension BrainpoolP256r1 { // swiftlint:disable:this no_extension_access_modifier 25 | /// Verify signatures for the BrainpoolP256r1 Elliptic Curve 26 | enum Verify { 27 | /// The BrainpoolP256r1 Public Key for ECDSA verification 28 | public struct PublicKey: ECPublicKey, SignatureVerifier { 29 | let pubKey: ECPublicKeyImpl 30 | 31 | internal init(impl: ECPublicKeyImpl) { 32 | pubKey = impl 33 | } 34 | 35 | public init(compact: Data) throws { 36 | pubKey = try ECPublicKeyImpl(compact: compact) 37 | } 38 | 39 | public init(x962: Data) throws { 40 | pubKey = try ECPublicKeyImpl(x962: x962) 41 | } 42 | 43 | public func rawValue() throws -> Data { 44 | try pubKey.rawValue() 45 | } 46 | 47 | public func x962Value() throws -> Data { 48 | try pubKey.x962Value() 49 | } 50 | 51 | public func compactValue() throws -> Data { 52 | try pubKey.compactValue() 53 | } 54 | 55 | public func verify(signature: Signature, message: Data) throws -> Bool { 56 | try pubKey.verify( 57 | signature: ECDSASignature(signature: signature), 58 | digest: Hash.SHA256.hash(data: message) 59 | ) 60 | } 61 | } 62 | 63 | /// The BrainpoolP256r1 Private Key for ECDSA signing 64 | public struct PrivateKey: ECPrivateKey, Signer { 65 | private let key: ECPrivateKeyImpl 66 | 67 | init(key: ECPrivateKeyImpl) { 68 | self.key = key 69 | } 70 | 71 | public init(raw: Data) throws { 72 | key = try ECPrivateKeyImpl(raw: raw) 73 | } 74 | 75 | public init(x962: Data) throws { 76 | key = try ECPrivateKeyImpl(x962: x962) 77 | } 78 | 79 | public var publicKey: Verify.PublicKey { 80 | Verify.PublicKey(impl: key.publicKey) 81 | } 82 | 83 | public func sign(message: Data) throws -> Signature { 84 | try Signature(signature: key.sign(digest: Hash.SHA256.hash(data: message))) 85 | } 86 | 87 | public static func generateKey() throws -> PrivateKey { 88 | try Self(key: ECPrivateKeyImpl.generateKey()) 89 | } 90 | } 91 | 92 | public struct Signature: ECSignature { 93 | private let signature: ECDSASignature 94 | 95 | internal init(signature: ECDSASignature) { 96 | self.signature = signature 97 | } 98 | 99 | public init(rawRepresentation: Data) throws { 100 | signature = try ECDSASignature(rawRepresentation: rawRepresentation) 101 | } 102 | 103 | public init(derRepresentation: Data) throws { 104 | signature = try ECDSASignature(derRepresentation: derRepresentation) 105 | } 106 | 107 | public var derRepresentation: Data { 108 | signature.derBytes 109 | } 110 | 111 | public var rawRepresentation: Data { 112 | signature.rawBytes 113 | } 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Sources/OpenSSL/Signature/ECDSASignature.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @_implementationOnly import COpenSSL 22 | import Foundation 23 | 24 | typealias ECDSA_SIG = OpaquePointer 25 | 26 | class ECDSASignature { 27 | private let sig: ECDSA_SIG 28 | 29 | internal init(owningNoCopy signature: ECDSA_SIG) { 30 | sig = signature 31 | } 32 | 33 | convenience init(signature: S) throws { 34 | try self.init(derRepresentation: signature.derRepresentation) 35 | } 36 | 37 | init(derRepresentation: Data) throws { 38 | sig = try derRepresentation.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in 39 | var bytesPtr = buffer.bindMemory(to: UInt8.self).baseAddress 40 | 41 | guard let rawSig = d2i_ECDSA_SIG(nil, &bytesPtr, buffer.count) else { 42 | throw OpenSSLError(name: "Signature not DER encoded") 43 | } 44 | return rawSig 45 | } 46 | } 47 | 48 | init(rawRepresentation: Data) throws { 49 | let half = rawRepresentation.count / 2 50 | let r = try BigNumber(bytes: rawRepresentation.prefix(half)) // swiftlint:disable:this identifier_name 51 | let s = try BigNumber(bytes: rawRepresentation.suffix(half)) // swiftlint:disable:this identifier_name 52 | guard let sig = ECDSA_SIG_new() else { 53 | throw OpenSSLError(name: "Signature could not be initialized") 54 | } 55 | try r.withUnsafeBignumPointer { rPtr in 56 | try s.withUnsafeBignumPointer { sPtr in 57 | // ECDSA_SIG_set0 takes ownership without copying the backing BIGNUM 58 | // Therefore we need to duplicate/copy them before passing them to `sig` 59 | // see: https://www.openssl.org/docs/man1.1.0/man3/ECDSA_SIG_set0.html 60 | guard let rCopy = BN_dup(rPtr) else { 61 | ECDSA_SIG_free(sig) 62 | throw OpenSSLError(name: "Could not copy [r]") 63 | } 64 | guard let sCopy = BN_dup(sPtr) else { 65 | // free rCopy before throwing 66 | BN_free(rCopy) 67 | ECDSA_SIG_free(sig) 68 | throw OpenSSLError(name: "Could not copy [s]") 69 | } 70 | 71 | if ECDSA_SIG_set0(sig, rCopy, sCopy) == 0 { 72 | // Error. We still own the BIGNUMs, and must free them. 73 | BN_free(rCopy) 74 | BN_free(sCopy) 75 | ECDSA_SIG_free(sig) 76 | throw OpenSSLError(name: "Signature initialization failed") 77 | } 78 | // Success. We've passed BIGNUMs ownership successfully, so we don't free them. 79 | } 80 | } 81 | self.sig = sig 82 | } 83 | 84 | deinit { 85 | ECDSA_SIG_free(sig) 86 | } 87 | 88 | var derBytes: Data { 89 | var dataPtr: UnsafeMutablePointer? 90 | let length = i2d_ECDSA_SIG(sig, &dataPtr) 91 | guard length > 0, let safeDataPtr = dataPtr else { 92 | return Data() 93 | } 94 | 95 | return Data(bytesNoCopy: safeDataPtr, count: Int(length), deallocator: .free) 96 | } 97 | 98 | var rawBytes: Data { 99 | var r, s: BIGNUM? // swiftlint:disable:this identifier_name 100 | /* Accessor for r and s fields of ECDSA_SIG 101 | * \param sig pointer to ECDSA_SIG pointer 102 | * \param pr pointer to BIGNUM pointer for r (may be NULL) 103 | * \param ps pointer to BIGNUM pointer for s (may be NULL) 104 | */ 105 | ECDSA_SIG_get0(sig, &r, &s) 106 | guard let safeR = r, let safeS = s else { 107 | return Data() 108 | } 109 | return BigNumber(copying: safeR).rawBytes + BigNumber(copying: safeS).rawBytes 110 | } 111 | 112 | func withUnsafeSignaturePointer(_ body: (OpaquePointer) throws -> T) rethrows -> T { 113 | try body(sig) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Sources/OpenSSL/CMS/CMSContentInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @_implementationOnly import COpenSSL 22 | import Foundation 23 | 24 | /// CMSContentInfo 25 | public class CMSContentInfo { 26 | let cms: OpaquePointer 27 | 28 | init() { 29 | cms = CMS_ContentInfo_new() 30 | } 31 | 32 | required init(owningNoCopy cms: OpaquePointer) { 33 | self.cms = cms 34 | } 35 | 36 | /// De-initialize 37 | deinit { 38 | CMS_ContentInfo_free(cms) 39 | } 40 | 41 | /// Get the DER byte representation as `Data` 42 | public var derBytes: Data? { 43 | var dataPtr: UnsafeMutablePointer? 44 | let length = i2d_CMS_ContentInfo(cms, &dataPtr) 45 | guard length > 0, let safeDataPtr = dataPtr else { 46 | return nil 47 | } 48 | 49 | return Data(bytesNoCopy: safeDataPtr, count: Int(length), deallocator: .free) 50 | } 51 | 52 | /// Entry step for the `encryptPartial`, `addRecipients`, `final` (as in `init`, `update`, `final`) cycle. 53 | /// 54 | /// - Important: Encryption is done by `aes_256_gcm` 55 | /// - Parameter data: Data (e.g. a message) to be encrypted 56 | /// - Returns: A (partially) initialized `CMSContentInfo` 57 | /// - Throws: `OpenSSLError` 58 | public static func encryptPartial(data: Data) throws -> Self { 59 | let flagPartial = UInt32(CMS_PARTIAL) // don't call `finalize` immediately 60 | let flags = flagPartial 61 | 62 | let cms: OpaquePointer! 63 | cms = try data.withUnsafeBytes { unsafeRawBufferPointer in 64 | let bytesPtr = unsafeRawBufferPointer.bindMemory(to: UInt8.self).baseAddress 65 | let dataBio = BIO_new_mem_buf(bytesPtr, Int32(unsafeRawBufferPointer.count)) 66 | defer { BIO_free(dataBio) } 67 | 68 | guard let ret = CMS_encrypt(nil, dataBio, EVP_aes_256_gcm(), flags) else { 69 | throw OpenSSLError(name: "Error calling CMS_encrypt()") 70 | } 71 | return ret 72 | } 73 | return .init(owningNoCopy: cms) 74 | } 75 | 76 | /// Update step for the `encryptPartial`, `addRecipients`, `final` (as in `init`, `update`, `final`) cycle. 77 | /// 78 | /// - Important: This will only work with `X509` certificates that contain a RSA public key! 79 | /// - Parameter recipients: 80 | /// - Throws: `OpenSSLError` 81 | public func addRecipientsRSAOnly(_ recipients: [X509]) throws { 82 | let flagPartial = UInt32(CMS_PARTIAL) // don't call `finalize` immediately 83 | let flags = flagPartial 84 | 85 | for recipient in recipients { 86 | var ri: OpaquePointer! // swiftlint:disable:this identifier_name 87 | var pctx: OpaquePointer! 88 | ri = CMS_add1_recipient_cert(cms, recipient.x509, flags | UInt32(CMS_KEY_PARAM)) 89 | pctx = CMS_RecipientInfo_get0_pkey_ctx(ri) 90 | guard EVP_PKEY_CTX_set_rsa_oaep_md(pctx, EVP_sha256()) == 1 else { 91 | throw OpenSSLError(name: "Error calling EVP_PKEY_CTX_set_rsa_oaep_md()") 92 | } 93 | guard EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, EVP_sha256()) == 1 else { 94 | throw OpenSSLError(name: "Error calling EVP_PKEY_CTX_set_rsa_mgf1_md()") 95 | } 96 | guard EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_OAEP_PADDING) == 1 else { 97 | throw OpenSSLError(name: "Error calling EVP_PKEY_CTX_set_rsa_padding()") 98 | } 99 | } 100 | } 101 | 102 | /// Final step for the `encryptPartial`, `addRecipients`, `final` (as in `init`, `update`, `final`) cycle. 103 | /// 104 | /// - Parameter data: Data (e.g. a message) to be encrypted 105 | /// - Throws: `OpenSSLError` 106 | public func final(data: Data) throws { 107 | let flagPartial = UInt32(CMS_PARTIAL) // don't call `finalize` immediately 108 | let flags = flagPartial 109 | 110 | try data.withUnsafeBytes { unsafeRawBufferPointer in 111 | let bytesPtr = unsafeRawBufferPointer.bindMemory(to: UInt8.self).baseAddress 112 | let dataBio = BIO_new_mem_buf(bytesPtr, Int32(unsafeRawBufferPointer.count)) 113 | defer { 114 | BIO_free(dataBio) 115 | } 116 | guard CMS_final(cms, dataBio, nil, flags) == 1 else { 117 | throw OpenSSLError(name: "Error calling CMS_final()") 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Sources/OpenSSL/Mac/CMAC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @_implementationOnly import COpenSSL 22 | import Foundation 23 | 24 | /// CMAC calculation package 25 | public enum CMAC {} 26 | 27 | extension CMAC { 28 | /// Stateless, one-shot AES 128 CBC CMAC function 29 | /// see: https://tools.ietf.org/html/rfc4493 30 | /// 31 | /// - Parameters: 32 | /// - key: raw key `Data` - must be 16 bytes 33 | /// - data: raw message `Data` 34 | /// - Throws: `OpenSSLError` 35 | /// - Returns: calculated MAC as `Data` 36 | public static func aes128cbc_bySteps(key: Data, data: Data) throws -> Data { 37 | let byteCount = 16 38 | guard key.count == byteCount else { 39 | throw OpenSSLError(name: "Key length invalid for CMAC calculation") 40 | } 41 | var macT = [UInt8](repeating: 0x0, count: byteCount) 42 | var outLen = 0 43 | let macTCount = byteCount 44 | 45 | let mac = EVP_MAC_fetch(nil, "cmac", nil) 46 | defer { 47 | EVP_MAC_free(mac) 48 | } 49 | let ctx: OpaquePointer = EVP_MAC_CTX_new(mac) 50 | defer { 51 | EVP_MAC_CTX_free(ctx) 52 | } 53 | var cipher = "aes-128-cbc".cString(using: .utf8)! // swiftlint:disable:this force_unwrapping 54 | let cipherTag = "cipher".cString(using: .utf8)! // swiftlint:disable:this force_unwrapping 55 | let subalgParam0 = OSSL_PARAM_construct_utf8_string(cipherTag, &cipher, 0) 56 | let subalgParam1 = OSSL_PARAM_construct_end() 57 | let subalgParams = [subalgParam0, subalgParam1] 58 | 59 | guard EVP_MAC_CTX_set_params(ctx, subalgParams) == 1 else { 60 | throw OpenSSLError(name: "Could not add OSSL_PARAM") 61 | } 62 | 63 | try key.withUnsafeBytes { (keyPtr: UnsafeRawBufferPointer) in 64 | try data.withUnsafeBytes { (msgPtr: UnsafeRawBufferPointer) in 65 | guard let keyPtrBaseAddress = keyPtr.bindMemory(to: UInt8.self).baseAddress, 66 | let msgPtrBaseAddress = msgPtr.bindMemory(to: UInt8.self).baseAddress else { 67 | throw OpenSSLError(name: "Error deriving CMAC") 68 | } 69 | 70 | guard EVP_MAC_init(ctx, keyPtrBaseAddress, byteCount, nil) == 1 71 | else { 72 | throw OpenSSLError(name: "Error EVP_MAC life cycle begin") 73 | } 74 | guard EVP_MAC_update(ctx, msgPtrBaseAddress, msgPtr.count) == 1 75 | else { 76 | throw OpenSSLError(name: "Error EVP_MAC life cycle update") 77 | } 78 | guard EVP_MAC_final(ctx, &macT, &outLen, macTCount) == 1 79 | else { 80 | throw OpenSSLError(name: "Error EVP_MAC life cycle final") 81 | } 82 | } 83 | } 84 | return Data(macT) 85 | } 86 | 87 | /// Stateless, one-shot AES 128 CBC CMAC function 88 | /// see: https://tools.ietf.org/html/rfc4493 89 | /// 90 | /// - Parameters: 91 | /// - key: raw key `Data` - must be 16 bytes 92 | /// - data: raw message `Data` 93 | /// - Throws: `OpenSSLError` 94 | /// - Returns: calculated MAC as `Data` 95 | public static func aes128cbc(key: Data, data: Data) throws -> Data { 96 | let byteCount = 16 97 | guard key.count == byteCount else { 98 | throw OpenSSLError(name: "Key length invalid for CMAC calculation") 99 | } 100 | var macT = [UInt8](repeating: 0x0, count: byteCount) 101 | var outLen = 0 102 | let macTCount = byteCount 103 | 104 | try key.withUnsafeBytes { (keyPtr: UnsafeRawBufferPointer) in 105 | try data.withUnsafeBytes { (msgPtr: UnsafeRawBufferPointer) in 106 | guard let keyPtrBaseAddress = keyPtr.bindMemory(to: UInt8.self).baseAddress, 107 | let msgPtrBaseAddress = msgPtr.bindMemory(to: UInt8.self).baseAddress else { 108 | throw OpenSSLError(name: "Error deriving CMAC") 109 | } 110 | EVP_Q_mac( 111 | nil, 112 | "cmac", 113 | nil, 114 | "aes-128-cbc", 115 | nil, 116 | keyPtrBaseAddress, 117 | byteCount, 118 | msgPtrBaseAddress, 119 | msgPtr.count, 120 | &macT, 121 | macTCount, 122 | &outLen 123 | ) 124 | } 125 | } 126 | return Data(macT) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Certificate/OCSPResponseTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | import Foundation 22 | @testable import OpenSSL 23 | import XCTest 24 | 25 | // swiftlint:disable force_try 26 | final class OCSPResponseTests: XCTestCase { 27 | let bundle = Bundle(for: OCSPResponseTests.self) 28 | lazy var vauOcspResponse: OCSPResponse = { 29 | let fileName = "OCSP/vau-oscp-response.der.base64" 30 | let base64 = try! ResourceFileReader.readFileInResourceBundle(filePath: fileName, for: bundle) 31 | return try! OCSPResponse(der: Data(base64Encoded: base64)!) 32 | }() 33 | 34 | lazy var vauOcspResponseNoKnownSignerCa: OCSPResponse = { 35 | let fileName = "OCSP/vau-ocsp-response-no-signing-ca.der.base64" 36 | let base64 = try! ResourceFileReader.readFileInResourceBundle(filePath: fileName, for: bundle) 37 | return try! OCSPResponse(der: Data(base64Encoded: base64)!) 38 | }() 39 | 40 | func testOCSPResponseStatus() { 41 | XCTAssertEqual(vauOcspResponseNoKnownSignerCa.status(), .successful) 42 | } 43 | 44 | func testProducedAt() throws { 45 | // when 46 | let producedAt = try vauOcspResponseNoKnownSignerCa.producedAt() 47 | 48 | // then 49 | let expectedProducedAt = ISO8601DateFormatter().date(from: "2021-03-25T12:54:31Z") 50 | XCTAssertEqual(producedAt, expectedProducedAt) 51 | } 52 | 53 | func testCertificateStatus() throws { 54 | // given 55 | let issuer: X509 = { 56 | let filename = "X509/GEM.KOMP-CA10-TEST-ONLY.pem" 57 | return try! X509(pem: ResourceFileReader.readFileInResourceBundle(filePath: filename, for: bundle)) 58 | }() 59 | let eeCert: X509 = { 60 | let filename = "X509/c.fd.enc-erp-erpserverReferenz.pem" 61 | return try! X509(pem: ResourceFileReader.readFileInResourceBundle(filePath: filename, for: bundle)) 62 | }() 63 | 64 | // when 65 | let certificateStatus = try vauOcspResponseNoKnownSignerCa.certificateStatus(for: eeCert, issuer: issuer) 66 | 67 | // then 68 | XCTAssertEqual(certificateStatus, .good) 69 | } 70 | 71 | func testGetSignerCertificate() throws { 72 | // when 73 | let signerCertificate = try vauOcspResponseNoKnownSignerCa.getSigner() 74 | 75 | // expect 76 | XCTAssertEqual(try signerCertificate?.serialNumber(), "119197") 77 | } 78 | 79 | func testOcspBasicVerify_pathValidation() throws { 80 | // given 81 | let ocspSignerCa: X509 = { 82 | let fileName = "X509/GEM.KOMP-CA28 TEST-ONLY.der.base64" 83 | let base64 = try! ResourceFileReader.readFileInResourceBundle(filePath: fileName, for: bundle) 84 | return try! X509(der: Data(base64Encoded: base64)!) 85 | }() 86 | let rootCa: X509 = { 87 | let filename = "X509/GEM.RCA3-TEST-ONLY.pem" 88 | return try! X509(pem: ResourceFileReader.readFileInResourceBundle(filePath: filename, for: bundle)) 89 | }() 90 | let trustedStore = [ocspSignerCa, rootCa] 91 | 92 | // then 93 | /* 94 | Note: With the updated test data the basicVerifyWith check passes now without setting the flag options. 95 | The code remains in here for reference. 96 | 97 | // Signer certificate does not meet the OCSP issuer criteria including potential delegation 98 | XCTAssertFalse(try vauOcspResponse.basicVerifyWith(trustedStore: trustedStore)) 99 | 100 | // After successful path validation the function returns success if the OCSP_NOCHECKS flag is set. 101 | // Note: Path validation could possibly fail in the future when certificates expire. 102 | let options: OCSPResponse.BasicVerifyOptions = [.noChecks] 103 | XCTAssertTrue(try vauOcspResponse.basicVerifyWith(trustedStore: trustedStore, options: options)) 104 | */ 105 | 106 | // Use a specific validation time to prevent test failures when certificates expire 107 | let validationTime = Date(timeIntervalSince1970: 1_736_329_189) // Wed Jan 08 2025 09:39:49 GMT+0000 108 | XCTAssertTrue(try vauOcspResponse.basicVerifyWith(trustedStore: trustedStore, validationTime: validationTime)) 109 | 110 | // Test that validation fails when using a date after certificate expiration 111 | // GEM.KOMP-CA28 TEST-ONLY expires on Jun 18 2026, so use a date after that 112 | let expiredValidationTime = Date(timeIntervalSince1970: 1_782_259_200) // Mon Jun 22 2026 00:00:00 GMT+0000 113 | XCTAssertFalse(try vauOcspResponse 114 | .basicVerifyWith(trustedStore: trustedStore, validationTime: expiredValidationTime)) 115 | } 116 | } 117 | 118 | // swiftlint:enable force_try 119 | -------------------------------------------------------------------------------- /Sources/OpenSSL/EC/EllipticCurvePoint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @_implementationOnly import COpenSSL 22 | import Foundation 23 | 24 | @usableFromInline 25 | class EllipticCurvePoint { 26 | let point: OpaquePointer // internally EC_POINT is used 27 | 28 | init(point: OpaquePointer) { 29 | self.point = point 30 | } 31 | 32 | /// Initialize an `EllipticCurvePoint` from raw data in un-/compressed encoding. 33 | /// 34 | /// - Parameter raw: Can be either in compressed (0x02 / 0x03) or uncompressed (0x04) encoding. 35 | /// - Throws: OpenSSLError 36 | /// - Note: No check whether the encoded point actually lies on the curve is employed during initialisation. 37 | init(raw: Data) throws { 38 | point = try raw.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) -> OpaquePointer in 39 | let group = Curve.group 40 | let point = EC_POINT_new(group.curve) 41 | guard let bufferPointer = buffer.bindMemory(to: UInt8.self).baseAddress else { 42 | throw OpenSSLError(name: "EC_POINT data unavailable") 43 | } 44 | 45 | guard 46 | EC_POINT_oct2point(group.curve, point, bufferPointer, buffer.count, nil) == 1, 47 | let point = point 48 | else { 49 | throw OpenSSLError(name: "Incorrect EC_POINT encoding") 50 | } 51 | return point 52 | } 53 | } 54 | 55 | var group: OpenSSLECGroup { 56 | Curve.group 57 | } 58 | 59 | deinit { 60 | EC_POINT_free(point) 61 | } 62 | 63 | enum PointConversion { 64 | case uncompressed 65 | case compressed 66 | } 67 | 68 | func export(pointConversion: PointConversion) throws -> Data { 69 | let group = Curve.group 70 | var buffer: UnsafeMutablePointer? 71 | let pointConversionForm: point_conversion_form_t 72 | switch pointConversion { 73 | case .uncompressed: pointConversionForm = POINT_CONVERSION_UNCOMPRESSED 74 | case .compressed: pointConversionForm = POINT_CONVERSION_COMPRESSED 75 | } 76 | let size = EC_POINT_point2buf(group.curve, point, pointConversionForm, &buffer, nil) 77 | guard size > 0, let safeBuffer = buffer else { 78 | throw OpenSSLError(name: "Error exporting encoded data from EllipticCurvePoint") 79 | } 80 | return Data(bytesNoCopy: safeBuffer, count: size, deallocator: .free) 81 | } 82 | 83 | /// Perform a point addition with the two points on the given curve 84 | /// 85 | /// - Parameter other: the `EllipticCurvePoint` to be added 86 | /// - Returns: Sum of the addition as `EllipticCurvePoint` 87 | /// - Throws: `OpenSSLError` 88 | func add(_ other: EllipticCurvePoint) throws -> EllipticCurvePoint { 89 | let sumPoint: OpaquePointer = try group.withUnsafeGroupPointer { groupPtr in 90 | guard let pointPtr = EC_POINT_new(groupPtr) else { 91 | throw OpenSSLError(name: "EC_POINT internal error") 92 | } 93 | return pointPtr 94 | } 95 | 96 | try group.withUnsafeGroupPointer { groupPtr in 97 | try other.withPointPointer { otherPtr in 98 | guard 99 | EC_POINT_add(groupPtr, sumPoint, point, otherPtr, nil) == 1 100 | else { 101 | EC_POINT_free(sumPoint) 102 | throw OpenSSLError(name: "EC_POINT internal error") 103 | } 104 | } 105 | } 106 | return EllipticCurvePoint(point: sumPoint) 107 | } 108 | 109 | /// Create an `EllipticCurvePoint` by multiplying a scalar with a curve's base point. 110 | /// 111 | /// - Parameters: 112 | /// - scalar: `BigNumber` to multiply the base point with 113 | /// - Throws: `OpenSSLError` 114 | @usableFromInline 115 | init(multiplyWithBasePoint scalar: BigNumber) throws { 116 | let group = Curve.group 117 | let productPoint: OpaquePointer = try group.withUnsafeGroupPointer { groupPtr in 118 | guard let pointPtr = EC_POINT_new(groupPtr) else { 119 | throw OpenSSLError(name: "EC_POINT internal error") 120 | } 121 | return pointPtr 122 | } 123 | 124 | try group.withUnsafeGroupPointer { groupPtr in 125 | try scalar.withUnsafeBignumPointer { bigNumPtr in 126 | guard 127 | EC_POINT_mul(groupPtr, productPoint, bigNumPtr, nil, nil, nil) != 0 128 | else { 129 | EC_POINT_free(productPoint) 130 | throw OpenSSLError(name: "EC_POINT internal error") 131 | } 132 | } 133 | } 134 | point = productPoint 135 | } 136 | 137 | /// Multiply a scalar with this point. 138 | /// 139 | /// - Parameter scalar: `BigNumber` to multiply the other point with 140 | /// - Returns: Product of the multiplication as `EllipticCurvePoint` 141 | /// - Throws: `OpenSSLError` 142 | func multiply(_ scalar: BigNumber) throws -> EllipticCurvePoint { 143 | let productPoint: OpaquePointer = try group.withUnsafeGroupPointer { groupPtr in 144 | guard let pointPtr = EC_POINT_new(groupPtr) else { 145 | throw OpenSSLError(name: "EC_POINT internal error") 146 | } 147 | return pointPtr 148 | } 149 | 150 | try group.withUnsafeGroupPointer { groupPtr in 151 | try scalar.withUnsafeBignumPointer { bigNumPtr in 152 | guard 153 | EC_POINT_mul(groupPtr, productPoint, nil, point, bigNumPtr, nil) != 0 154 | else { 155 | EC_POINT_free(productPoint) 156 | throw OpenSSLError(name: "EC_POINT internal error") 157 | } 158 | } 159 | } 160 | return EllipticCurvePoint(point: productPoint) 161 | } 162 | } 163 | 164 | extension EllipticCurvePoint { 165 | @usableFromInline 166 | func withPointPointer(_ body: (OpaquePointer) throws -> T) rethrows -> T { 167 | try body(point) 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | fastlane_version "2.210.1" 2 | 3 | xcodes( 4 | version: ENV["FL_XCODE_VERSION"] || "16.4.0", 5 | select_for_current_build_only: true, 6 | update_list: false 7 | ) 8 | 9 | lane :cibuild do 10 | test 11 | end 12 | 13 | lane :test do 14 | scan( 15 | scheme: "OpenSSL_macOS" 16 | ) 17 | scan( 18 | scheme: "OpenSSL_iOS", 19 | device: "iPhone 11" 20 | ) 21 | end 22 | 23 | lane :build_all do 24 | scan( 25 | scheme: "OpenSSL_macOS", 26 | build_for_testing: true 27 | ) 28 | scan( 29 | scheme: "OpenSSL_iOS", 30 | device: "iPhone 11", 31 | build_for_testing: true 32 | ) 33 | end 34 | 35 | lane :static_code_analysis do 36 | swiftlint_mint = "mint run swiftlint" 37 | swiftlint( 38 | executable: swiftlint_mint, 39 | mode: :lint, 40 | ignore_exit_status: false, 41 | quiet: true, 42 | strict: true 43 | ) 44 | sh "cd .. && swiftformat . --config .swiftformat --lint --quiet; cd -" 45 | end 46 | 47 | desc "Lane that (auto) genarates API documentation from inline comments." 48 | lane :generate_documentation do 49 | jazzy( 50 | config: ".jazzy.yml" 51 | ) 52 | end 53 | 54 | lane :build_xcframework do 55 | # Build OpenSSL xcframework for iOS and macOS 56 | sh "pushd ..; mint run carthage build --platform iOS,Mac --configuration Release --cache-builds --use-xcframeworks --no-skip-current; popd" 57 | 58 | # Move it to artifacts directory 59 | sh "pushd ..; mkdir -p artifacts/xcframework && rm -rf artifacts/xcframework/* && mv Carthage/Build/OpenSSL.xcframework artifacts/xcframework/; popd" 60 | 61 | # Load the Distribution certificate and private key into the keychain 62 | match(type: "appstore", skip_provisioning_profiles: true) 63 | 64 | # Codesign the xcframework 65 | sh "pushd ../artifacts/xcframework; xcrun codesign --timestamp -s \"Apple Distribution\" --keychain \"#{keychain_path}\" OpenSSL.xcframework; popd" 66 | 67 | # Was it actually signed? A unsuccessful attempt might not have exited with an error 68 | # A directory "_CodeSignature" should be present in the xcframework directory 69 | sh "pushd ../artifacts/xcframework; if [ -d OpenSSL.xcframework/_CodeSignature ]; then echo 'Code signature directory exists'; else echo 'Code signature directory does not exist'; exit 1; fi; popd" 70 | sh "pushd ../artifacts/xcframework; codesign --verify OpenSSL.xcframework; popd" # Returns code 1 if the signature is invalid or the xcframework is not signed at all 71 | 72 | # Zip it with build version in the filename for tracking 73 | sh "pushd ../artifacts/xcframework; zip -ry OpenSSL_#{build_version}.xcframework.zip OpenSSL.xcframework; popd" 74 | 75 | # Compute checksum 76 | sh "pushd ../artifacts/xcframework; swift package compute-checksum OpenSSL_#{build_version}.xcframework.zip > OpenSSL_#{build_version}.xcframework.zip.checksum; popd" 77 | 78 | end 79 | 80 | def build_version() 81 | ENV['BUILD_NUMBER'] || 'LOCAL_BUILD' 82 | end 83 | 84 | lane :download_xcframework_from_nexus_and_attach_to_github_release do |options| 85 | artefact_path = options[:artefact_path] || ENV['NEXUS_ARTEFACT_PATH'] 86 | 87 | unless artefact_path 88 | UI.user_error!("artefact_path is required but was not provided.") 89 | end 90 | 91 | asset_file = "./Downloads/#{File.basename(artefact_path)}" 92 | 93 | # Download the xcframework.zip from Nexus 94 | nexus_file_download( 95 | artefact_path: artefact_path, 96 | target_file_path: asset_file, 97 | repository: "Apps", 98 | nexus_url: "https://nexus.prod.ccs.gematik.solutions/" 99 | ) 100 | 101 | # Check if the downloaded xcframework file has the same checksum stated in Package.swift 102 | nexus_file_download( 103 | artefact_path: "#{artefact_path}.checksum", 104 | target_file_path: "#{asset_file}.checksum", 105 | repository: "Apps", 106 | nexus_url: "https://nexus.prod.ccs.gematik.solutions/" 107 | ) 108 | checksum_file_path = "../#{asset_file}.checksum" 109 | package_swift_contains_checksum = match_first_line(checksum_file_path, "../Package.swift") 110 | UI.user_error!("Checksum in Package.swift does not match the checksum of the downloaded file.") unless package_swift_contains_checksum 111 | 112 | # Check if the downloaded xcframework file is signed and the signature is valid 113 | # Extract the zip file to verify the signature 114 | sh "unzip -q #{asset_file} -d ./Downloads/" 115 | 116 | # Find the .xcframework directory in the extracted content 117 | xcframework_path = Dir.glob("./Downloads/*.xcframework").first 118 | UI.user_error!("No .xcframework found in the extracted zip file.") unless xcframework_path 119 | 120 | # Verify the code signature 121 | sh "codesign --verify #{xcframework_path}" # Returns code 1 if the signature is invalid or the xcframework is not signed at all 122 | 123 | # Upload the xcframework.zip to GitHub release 124 | asset_file = "./Downloads/#{File.basename(artefact_path)}" 125 | upload_github_release_asset( 126 | github_project_name: "OpenSSL-Swift", 127 | asset_file: asset_file, 128 | asset_name: "OpenSSL.xcframework.zip" 129 | ) 130 | end 131 | 132 | def match_first_line(file1_path, file2_path) 133 | # Open both files 134 | File.open(file1_path) do |file1| 135 | File.open(file2_path) do |file2| 136 | # Read the first line from file1 137 | first_line = file1.readline.strip 138 | 139 | # Check if the first line is not empty 140 | if first_line && !first_line.empty? 141 | UI.message("First line of #{file1_path}: #{first_line}") 142 | 143 | # Check if the contents of the first line matches any part of file2 144 | file2.each_line do |line| 145 | return true if line.include?(first_line) 146 | end 147 | UI.message("No match found in #{file2_path}") 148 | return false 149 | else 150 | UI.message("First line of #{file1_path} is empty") 151 | return false 152 | end 153 | end 154 | rescue Errno::ENOENT => e 155 | UI.error("Error: File not found - #{e.message}") 156 | return false 157 | rescue Errno::EACCES => e 158 | UI.error("Error: Permission denied - #{e.message}") 159 | return false 160 | rescue IOError => e 161 | UI.error("Error: IO error occurred - #{e.message}") 162 | return false 163 | end 164 | end 165 | 166 | # Keychain (containing the Distribution certificate's corresponding private key) 167 | # is needed for signing the xcframework 168 | 169 | def keychain_path 170 | File.expand_path("~/Library/Keychains/gematik-db") 171 | end 172 | 173 | before_all do |lane, options| 174 | load_keychain 175 | end 176 | 177 | after_all do |lane, options| 178 | remove_keychain 179 | end 180 | 181 | error do |lane, exception, options| 182 | remove_keychain 183 | end 184 | 185 | def load_keychain 186 | remove_keychain 187 | 188 | create_keychain( 189 | name: "gematik", 190 | password: "gematikpassword", 191 | unlock: true, 192 | timeout: 0 193 | ) 194 | end 195 | 196 | def remove_keychain 197 | if File.exist? keychain_path 198 | delete_keychain(name: "gematik") 199 | end 200 | end -------------------------------------------------------------------------------- /Tests/OpenSSLTests/EC/EllipticCurvePointTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @_implementationOnly import COpenSSL 22 | @testable import OpenSSL 23 | import XCTest 24 | 25 | final class EllipticCurvePointTests: XCTestCase { 26 | func testInitAndExportFromUncompressedRawData() throws { 27 | // given 28 | let pointUncompressedRawData = 29 | try Data( 30 | hex: "0435AAD48B06E2B1B10EE09DBCE9C80ABCDDD139F9BDC35EFD7C7832085B87F7744A2B7A13149AC71A252DB814214483EBB4EB6635FC375A7361A5D5C2E14DBC8F" // swiftlint:disable:this line_length 31 | ) 32 | let pointCompressedRawData = 33 | try Data( 34 | hex: "0335AAD48B06E2B1B10EE09DBCE9C80ABCDDD139F9BDC35EFD7C7832085B87F774" // swiftlint:disable:this line_length 35 | ) 36 | 37 | // when 38 | let ecPoint = try EllipticCurvePoint(raw: pointUncompressedRawData) 39 | let exportUncompressed = try ecPoint.export(pointConversion: .uncompressed) 40 | let exportCompressed = try ecPoint.export(pointConversion: .compressed) 41 | 42 | // then 43 | XCTAssertEqual(exportUncompressed, pointUncompressedRawData) 44 | XCTAssertEqual(exportCompressed, pointCompressedRawData) 45 | } 46 | 47 | func testInitFromRawData_compressed() throws { 48 | // given 49 | let pointUncompressedRawData = 50 | try Data( 51 | hex: "0435AAD48B06E2B1B10EE09DBCE9C80ABCDDD139F9BDC35EFD7C7832085B87F7744A2B7A13149AC71A252DB814214483EBB4EB6635FC375A7361A5D5C2E14DBC8F" // swiftlint:disable:this line_length 52 | ) 53 | let pointCompressedRawData = 54 | try Data( 55 | hex: "0335AAD48B06E2B1B10EE09DBCE9C80ABCDDD139F9BDC35EFD7C7832085B87F774" // swiftlint:disable:this line_length 56 | ) 57 | 58 | // when 59 | let ecPoint = try EllipticCurvePoint(raw: pointCompressedRawData) 60 | let exportUncompressed = try ecPoint.export(pointConversion: .uncompressed) 61 | let exportCompressed = try ecPoint.export(pointConversion: .compressed) 62 | 63 | // then 64 | XCTAssertEqual(exportUncompressed, pointUncompressedRawData) 65 | XCTAssertEqual(exportCompressed, pointCompressedRawData) 66 | } 67 | 68 | func testAddAnotherPoint() throws { 69 | // given 70 | let point1RawData = 71 | try Data( 72 | hex: "0435AAD48B06E2B1B10EE09DBCE9C80ABCDDD139F9BDC35EFD7C7832085B87F7744A2B7A13149AC71A252DB814214483EBB4EB6635FC375A7361A5D5C2E14DBC8F" // swiftlint:disable:this line_length 73 | ) 74 | let point2RawData = 75 | try Data( 76 | hex: "04941DDF52912A31313BD2A9E88A10691403D7B5CB810EA3AF2D8393C3EC380D3F385F7F3E6CF7B5A7CC0CA7AA9E573FA1849B664F32E417C5A4EA1D10BF5F5B8A" // swiftlint:disable:this line_length 77 | ) 78 | let point1 = try EllipticCurvePoint(raw: point1RawData) 79 | let point2 = try EllipticCurvePoint(raw: point2RawData) 80 | 81 | // when 82 | let point = try point1.add(point2) 83 | let export = try point.export(pointConversion: .uncompressed) 84 | 85 | // then 86 | let expected = 87 | try Data( 88 | hex: "047F6F0BB04F8CE6E67B93DB8B61929CB211C0FEF9602D9CAC431B0DFF081F89BC3BE2DD1F03BB1D46947CDB2F11F77DDC9E9C7211502334A3450591EC57900A86" // swiftlint:disable:this line_length 89 | ) 90 | XCTAssertEqual(export, expected) 91 | } 92 | 93 | func testInitViaMultiplyScalarWithBasePoint() throws { 94 | // given 95 | let scalar = try BigNumber(bytes: try Data(hex: "6B4C371D20A352D570183879FE1EEB63")) 96 | 97 | // when 98 | let point = try EllipticCurvePoint(multiplyWithBasePoint: scalar) 99 | let export = try point.export(pointConversion: .uncompressed) 100 | 101 | // then 102 | let expected = 103 | try Data( 104 | hex: 105 | "04821EADCCD4EFAB8D2C6608ED4E0303DC5FF1074D688EA4A7D3BD6B9D2B20D5C87965BF640347DE95E7CC467BA230F0ACEDF507068692A2F415C4DC0ABA69CFFB" // swiftlint:disable:this line_length 106 | ) 107 | XCTAssertEqual(export, expected) 108 | } 109 | 110 | func testMultiplicationWithEllipticCurvePoint() throws { 111 | // given 112 | let scalar = 113 | try BigNumber(bytes: try Data(hex: "0162AD399C4603B47A878BFAFB81CA17317D6649FEA0B3FE079329514BC6319FE4")) 114 | let point1Raw = 115 | try Data( 116 | hex: "04662553C7EBD0466473FB3AF925EC89CE4F4EEB89FFB8AECA4CB1BD6B55460CBBA6DF467A24DF394AAA230B7B630E35B9E89350F3E78D24E40F91F29B8E16D47C" // swiftlint:disable:this line_length 117 | ) 118 | let point1 = try EllipticCurvePoint(raw: point1Raw) 119 | 120 | // when 121 | let product = try point1.multiply(scalar) 122 | let export = try product.export(pointConversion: .uncompressed) 123 | 124 | // then 125 | let expected = 126 | try Data( 127 | hex: "044241F43161DB2400704509015D66EDB85FA8236157C4D11BDB4CA258000434B98FE93ABCD6F172B1678933D7CC7B17C698664EF36AA527E158979C1A4D3B191B" // swiftlint:disable:this line_length 128 | ) 129 | XCTAssertEqual(export, expected) 130 | } 131 | 132 | func testMultiplyWithScalar() throws { 133 | // given 134 | let scalar = 135 | try BigNumber(bytes: try Data(hex: "0162AD399C4603B47A878BFAFB81CA17317D6649FEA0B3FE079329514BC6319FE4")) 136 | let point1Raw = 137 | try Data( 138 | hex: "04662553C7EBD0466473FB3AF925EC89CE4F4EEB89FFB8AECA4CB1BD6B55460CBBA6DF467A24DF394AAA230B7B630E35B9E89350F3E78D24E40F91F29B8E16D47C" // swiftlint:disable:this line_length 139 | ) 140 | let point1 = try EllipticCurvePoint(raw: point1Raw) 141 | 142 | // when 143 | let product = try point1.multiply(scalar) 144 | let export = try product.export(pointConversion: .uncompressed) 145 | 146 | // then 147 | let expected = 148 | try Data( 149 | hex: "044241F43161DB2400704509015D66EDB85FA8236157C4D11BDB4CA258000434B98FE93ABCD6F172B1678933D7CC7B17C698664EF36AA527E158979C1A4D3B191B" // swiftlint:disable:this line_length 150 | ) 151 | XCTAssertEqual(export, expected) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Signature/BrainpoolP256r1ExtECDSATests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | import Foundation 22 | @testable import OpenSSL 23 | import XCTest 24 | 25 | final class BrainpoolP256r1ExtECDSATests: XCTestCase { 26 | func testVerifyBP256r1Signature() throws { 27 | // swiftlint:disable line_length 28 | let pubkeyraw = 29 | try! Data( 30 | hex: "049650ac6d4d5b1201de4cffe99db3a2396426a377bc95d9dc466727a2574d7c39643159e578f05a6b607e89afdd5395eeacc8e72714489cac3160c4bb79aa45c6" 31 | ) 32 | let pubKey = try BrainpoolP256r1.Verify.PublicKey(x962: pubkeyraw) 33 | let message = 34 | "eyJhbGciOiJCUDI1NlIxIiwieDVjIjpbIk1JSUNzVENDQWxpZ0F3SUJBZ0lIQTYxSTVBQ1VqVEFLQmdncWhrak9QUVFEQWpDQmhERUxNQWtHQTFVRUJoTUNSRVV4SHpBZEJnTlZCQW9NRm1kbGJXRjBhV3NnUjIxaVNDQk9UMVF0VmtGTVNVUXhNakF3QmdOVkJBc01LVXR2YlhCdmJtVnVkR1Z1TFVOQklHUmxjaUJVWld4bGJXRjBhV3RwYm1aeVlYTjBjblZyZEhWeU1TQXdIZ1lEVlFRRERCZEhSVTB1UzA5TlVDMURRVEV3SUZSRlUxUXRUMDVNV1RBZUZ3MHlNREE0TURRd01EQXdNREJhRncweU5UQTRNRFF5TXpVNU5UbGFNRWt4Q3pBSkJnTlZCQVlUQWtSRk1TWXdKQVlEVlFRS0RCMW5aVzFoZEdscklGUkZVMVF0VDA1TVdTQXRJRTVQVkMxV1FVeEpSREVTTUJBR0ExVUVBd3dKU1VSUUlGTnBaeUF4TUZvd0ZBWUhLb1pJemowQ0FRWUpLeVFEQXdJSUFRRUhBMElBQkpaUXJHMU5XeElCM2t6LzZaMnpvamxrSnFOM3ZKWFozRVpuSjZKWFRYdzVaREZaNVhqd1dtdGdmb212M1ZPVjdxekk1eWNVU0p5c01XREV1M21xUmNhamdlMHdnZW93SFFZRFZSME9CQllFRko4RFZMQVpXVCtCbG9qVEQ0TVQvTmErRVM4WU1EZ0dDQ3NHQVFVRkJ3RUJCQ3d3S2pBb0JnZ3JCZ0VGQlFjd0FZWWNhSFIwY0RvdkwyVm9ZMkV1WjJWdFlYUnBheTVrWlM5dlkzTndMekFNQmdOVkhSTUJBZjhFQWpBQU1DRUdBMVVkSUFRYU1CZ3dDZ1lJS29JVUFFd0VnVXN3Q2dZSUtvSVVBRXdFZ1NNd0h3WURWUjBqQkJnd0ZvQVVLUEQ0NXFuSWQ4eERSZHVhcnRjNmc2d09ENmd3TFFZRkt5UUlBd01FSkRBaU1DQXdIakFjTUJvd0RBd0tTVVJRTFVScFpXNXpkREFLQmdncWdoUUFUQVNDQkRBT0JnTlZIUThCQWY4RUJBTUNCNEF3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnVkJQaEF3eVg4SEFWSDBPMGIzK1ZhenBCQVdrUU5qa0VWUmt2K0VZWDFlOENJRmRuNE8rbml2TStYVmk5eGlLSzRkVzFSN01EMzM0T3BPUFRGamVFaElWViJdfQ.eyJhdXRob3JpemF0aW9uX2VuZHBvaW50IjoiaHR0cDovL2dzdG9wZGgyLnRvcC5sb2NhbDo4NTgwL2F1dGhvcml6YXRpb24iLCJ0b2tlbl9lbmRwb2ludCI6Imh0dHA6Ly9nc3RvcGRoMi50b3AubG9jYWw6ODU4MC90b2tlbiIsImlzc3VlciI6Imh0dHA6Ly9nc3RvcGRoMi50b3AubG9jYWw6ODU4MC9hdXRoL3JlYWxtcy9pZHAiLCJqd2tzX3VyaSI6Imh0dHA6Ly9nc3RvcGRoMi50b3AubG9jYWw6ODU4MC9qd2tzIiwicHVrX3VyaV9hdXRoIjoiaHR0cDovL2dzdG9wZGgyLnRvcC5sb2NhbDo4NTgwL2F1dGhLZXkvandrcy5qc29uIiwicHVrX3VyaV90b2tlbiI6Imh0dHA6Ly9nc3RvcGRoMi50b3AubG9jYWw6ODU4MC90b2tlbktleS9qd2tzLmpzb24iLCJwdWtfdXJpX2Rpc2MiOiJodHRwOi8vZ3N0b3BkaDIudG9wLmxvY2FsOjg1ODAvZGlzY0tleS9qd2tzLmpzb24iLCJzdWJqZWN0X3R5cGVzX3N1cHBvcnRlZCI6WyJwYWlyd2lzZSJdLCJpZF90b2tlbl9zaWduaW5nX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIkJQMjU2UjEiXSwicmVzcG9uc2VfdHlwZXNfc3VwcG9ydGVkIjpbImNvZGUiXSwic2NvcGVzX3N1cHBvcnRlZCI6WyJvcGVuaWQiLCJlLXJlemVwdCJdLCJyZXNwb25zZV9tb2Rlc19zdXBwb3J0ZWQiOlsicXVlcnkiXSwiZ3JhbnRfdHlwZXNfc3VwcG9ydGVkIjpbImF1dGhvcml6YXRpb25fY29kZSJdLCJhY3JfdmFsdWVzX3N1cHBvcnRlZCI6WyJ1cm46ZWlkYXM6bG9hOmhpZ2giXSwidG9rZW5fZW5kcG9pbnRfYXV0aF9tZXRob2RzX3N1cHBvcnRlZCI6WyJub25lIl19" 35 | 36 | let rawSignature = 37 | try! Data( 38 | hex: "1ead1d24d3966a388bd64c31ce1a9ba86393767ab8b937302a26f0f68049c024607ac0358c5ace66bc6c8c5e3eeead5a7456d0a8e046c1642e3e68e6957e84a5" 39 | ) 40 | let rawSignature1 = 41 | try! Data( 42 | hex: "16ad3a9445293ec8c5fedec64f431a1c20ef6a1ec13a5b517124cac92191474977909364a96c22369b8806300f666735e4ea7eff04af73d8e28ffc136f32c8e9" 43 | ) 44 | let rawSignature2 = 45 | try! Data( 46 | hex: "3b466e3e5fccf62587d86d46d80ce328dab44fbb3c41da44b631be88a863379871270106f26a1e6f1fd4b786522d869a7a15c306a8c41fd60ea25ce950864c64" 47 | ) 48 | let invalidRawSignature = 49 | try! Data( 50 | hex: "b3466e3e5fccf62587d86d46d80ce328dab44fbb3c41da44b631be88a863379871270106f26a1e6f1fd4b786522d869a7a15c306a8c41fd60ea25ce950864c64" 51 | ) 52 | // swiftlint:enable line_length 53 | 54 | let signature = try! BrainpoolP256r1.Verify.Signature(rawRepresentation: rawSignature) 55 | let signature1 = try! BrainpoolP256r1.Verify.Signature(rawRepresentation: rawSignature1) 56 | let signature2 = try! BrainpoolP256r1.Verify.Signature(rawRepresentation: rawSignature2) 57 | let invalidSignature = try! BrainpoolP256r1.Verify.Signature(rawRepresentation: invalidRawSignature) 58 | 59 | XCTAssertTrue(try pubKey.verify(signature: signature, message: message.data(using: .utf8)!)) 60 | XCTAssertTrue(try pubKey.verify(signature: signature1, message: message.data(using: .utf8)!)) 61 | XCTAssertTrue(try pubKey.verify(signature: signature2, message: message.data(using: .utf8)!)) 62 | XCTAssertFalse(try pubKey.verify(signature: invalidSignature, message: message.data(using: .utf8)!)) 63 | XCTAssertFalse(try pubKey.verify(signature: signature, message: "wrong message".data(using: .utf8)!)) 64 | } 65 | 66 | func testSignBP256r1() throws { 67 | let key = try BrainpoolP256r1.Verify.PrivateKey.generateKey() 68 | let message = 69 | "eyJhbGciOiJCUDI1NlIxIiwieDVjIjpbIk1JSUNzVENDQWxpZ0F3SUJBZ0lIQTYxSTVBQ1VqVEFLQmdncWhrak9QUVFEQWpDQmhERUxNQWtHQTFVRUJoTUNSRVV4SHpBZEJnTlZCQW9NRm1kbGJXRjBhV3NnUjIxaVNDQk9UMVF0VmtGTVNVUXhNakF3QmdOVkJBc01LVXR2YlhCdmJtVnVkR1Z1TFVOQklHUmxjaUJVWld4bGJXRjBhV3RwYm1aeVlYTjBjblZyZEhWeU1TQXdIZ1lEVlFRRERCZEhSVTB1UzA5TlVDMURRVEV3SUZSRlUxUXRUMDVNV1RBZUZ3MHlNREE0TURRd01EQXdNREJhRncweU5UQTRNRFF5TXpVNU5UbGFNRWt4Q3pBSkJnTlZCQVlUQWtSRk1TWXdKQVlEVlFRS0RCMW5aVzFoZEdscklGUkZVMVF0VDA1TVdTQXRJRTVQVkMxV1FVeEpSREVTTUJBR0ExVUVBd3dKU1VSUUlGTnBaeUF4TUZvd0ZBWUhLb1pJemowQ0FRWUpLeVFEQXdJSUFRRUhBMElBQkpaUXJHMU5XeElCM2t6LzZaMnpvamxrSnFOM3ZKWFozRVpuSjZKWFRYdzVaREZaNVhqd1dtdGdmb212M1ZPVjdxekk1eWNVU0p5c01XREV1M21xUmNhamdlMHdnZW93SFFZRFZSME9CQllFRko4RFZMQVpXVCtCbG9qVEQ0TVQvTmErRVM4WU1EZ0dDQ3NHQVFVRkJ3RUJCQ3d3S2pBb0JnZ3JCZ0VGQlFjd0FZWWNhSFIwY0RvdkwyVm9ZMkV1WjJWdFlYUnBheTVrWlM5dlkzTndMekFNQmdOVkhSTUJBZjhFQWpBQU1DRUdBMVVkSUFRYU1CZ3dDZ1lJS29JVUFFd0VnVXN3Q2dZSUtvSVVBRXdFZ1NNd0h3WURWUjBqQkJnd0ZvQVVLUEQ0NXFuSWQ4eERSZHVhcnRjNmc2d09ENmd3TFFZRkt5UUlBd01FSkRBaU1DQXdIakFjTUJvd0RBd0tTVVJRTFVScFpXNXpkREFLQmdncWdoUUFUQVNDQkRBT0JnTlZIUThCQWY4RUJBTUNCNEF3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnVkJQaEF3eVg4SEFWSDBPMGIzK1ZhenBCQVdrUU5qa0VWUmt2K0VZWDFlOENJRmRuNE8rbml2TStYVmk5eGlLSzRkVzFSN01EMzM0T3BPUFRGamVFaElWViJdfQ.eyJhdXRob3JpemF0aW9uX2VuZHBvaW50IjoiaHR0cDovL2dzdG9wZGgyLnRvcC5sb2NhbDo4NTgwL2F1dGhvcml6YXRpb24iLCJ0b2tlbl9lbmRwb2ludCI6Imh0dHA6Ly9nc3RvcGRoMi50b3AubG9jYWw6ODU4MC90b2tlbiIsImlzc3VlciI6Imh0dHA6Ly9nc3RvcGRoMi50b3AubG9jYWw6ODU4MC9hdXRoL3JlYWxtcy9pZHAiLCJqd2tzX3VyaSI6Imh0dHA6Ly9nc3RvcGRoMi50b3AubG9jYWw6ODU4MC9qd2tzIiwicHVrX3VyaV9hdXRoIjoiaHR0cDovL2dzdG9wZGgyLnRvcC5sb2NhbDo4NTgwL2F1dGhLZXkvandrcy5qc29uIiwicHVrX3VyaV90b2tlbiI6Imh0dHA6Ly9nc3RvcGRoMi50b3AubG9jYWw6ODU4MC90b2tlbktleS9qd2tzLmpzb24iLCJwdWtfdXJpX2Rpc2MiOiJodHRwOi8vZ3N0b3BkaDIudG9wLmxvY2FsOjg1ODAvZGlzY0tleS9qd2tzLmpzb24iLCJzdWJqZWN0X3R5cGVzX3N1cHBvcnRlZCI6WyJwYWlyd2lzZSJdLCJpZF90b2tlbl9zaWduaW5nX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIkJQMjU2UjEiXSwicmVzcG9uc2VfdHlwZXNfc3VwcG9ydGVkIjpbImNvZGUiXSwic2NvcGVzX3N1cHBvcnRlZCI6WyJvcGVuaWQiLCJlLXJlemVwdCJdLCJyZXNwb25zZV9tb2Rlc19zdXBwb3J0ZWQiOlsicXVlcnkiXSwiZ3JhbnRfdHlwZXNfc3VwcG9ydGVkIjpbImF1dGhvcml6YXRpb25fY29kZSJdLCJhY3JfdmFsdWVzX3N1cHBvcnRlZCI6WyJ1cm46ZWlkYXM6bG9hOmhpZ2giXSwidG9rZW5fZW5kcG9pbnRfYXV0aF9tZXRob2RzX3N1cHBvcnRlZCI6WyJub25lIl19" // swiftlint:disable:this line_length 70 | .data(using: .ascii)! 71 | let signature = try! key.sign(message: message) 72 | XCTAssertTrue(try! key.publicKey.verify(signature: signature, message: message)) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Sources/OpenSSL/EC/ECPublicKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @_implementationOnly import COpenSSL 22 | import Foundation 23 | 24 | /// Elliptic Curve public key 25 | public protocol ECPublicKey { 26 | /// Initialize a public key from compressed representation 27 | /// 28 | /// - Parameter compact: key material 29 | /// - Throws: `OpenSSLError` when the key could not be initialized 30 | init(compact: Data) throws 31 | 32 | /// Initialize a public key For an elliptic curve public key, 33 | /// the format follows the ANSI X9.62 standard using a byte string of 04 || X || Y. 34 | /// 35 | /// - Parameter x962: ANSI X9.62 key material 36 | /// - Throws: `OpenSSLError` when the key could not be initialized 37 | init(x962: Data) throws 38 | 39 | /// The raw EC Public Key representation in Octet-String 40 | func rawValue() throws -> Data 41 | 42 | /// The raw EC Public Key representation using ANSI X9.62 standard [04 || X || Y]. (uncompressed) 43 | func x962Value() throws -> Data 44 | 45 | /// The raw EC Public Key compressed representation 46 | func compactValue() throws -> Data 47 | } 48 | 49 | final class ECPublicKeyImpl: ECPublicKey { 50 | let pkey: EVP_PKEY 51 | 52 | /// Owning Non-copying initializer for EC Public Key 53 | /// 54 | /// - Parameter pkey: pointer to the EVP_PKEY to own and manage 55 | init(pkey: EVP_PKEY) { 56 | self.pkey = pkey 57 | } 58 | 59 | /// Initialize a public key from data (regardless whether in compressed or uncompressed representation. 60 | /// 61 | /// - Parameter data: Data encoding the public key 62 | /// - Throws: OpenSSLError 63 | convenience init(data: Data) throws { 64 | try self.init(data: data, pointFormatUncompressed: true) 65 | } 66 | 67 | // swiftlint:disable:next function_body_length 68 | private init(data: Data, pointFormatUncompressed: Bool = true) throws { 69 | var pkey = EVP_PKEY_new() 70 | 71 | // set up OSSL_PARAMs 72 | let osslParamBuilder = OSSL_PARAM_BLD_new() 73 | defer { OSSL_PARAM_BLD_free(osslParamBuilder) } 74 | guard OSSL_PARAM_BLD_push_utf8_string( 75 | osslParamBuilder, 76 | OSSL_PKEY_PARAM_GROUP_NAME_W, 77 | Curve.name.asCChar, 78 | 0 79 | ) == 1 80 | else { 81 | throw OpenSSLError(name: "Error setting up OSSL_PARAM") 82 | } 83 | 84 | let pointFormat = pointFormatUncompressed ? 85 | OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_UNCOMPRESSED_W : OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_COMPRESSED_W 86 | 87 | guard OSSL_PARAM_BLD_push_utf8_string( 88 | osslParamBuilder, 89 | OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT_W, 90 | pointFormat, 91 | 0 92 | ) == 1 93 | else { 94 | throw OpenSSLError(name: "Error setting up OSSL_PARAM") 95 | } 96 | 97 | try data.withUnsafeBytes { buffer in 98 | guard let bufferPointer = buffer.bindMemory(to: UInt8.self).baseAddress, 99 | OSSL_PARAM_BLD_push_octet_string( 100 | osslParamBuilder, 101 | OSSL_PKEY_PARAM_PUB_KEY_W, 102 | bufferPointer, 103 | buffer.count 104 | ) == 1 105 | else { 106 | throw OpenSSLError(name: "Error setting up OSSL_PARAM") 107 | } 108 | } 109 | let osslParams = OSSL_PARAM_BLD_to_param(osslParamBuilder) 110 | defer { OSSL_PARAM_free(osslParams) } 111 | 112 | // write OSSL_PARAMs to EVP_PKEY 113 | let ctx = EVP_PKEY_CTX_new_from_name(nil, EVP_PKEY_CTX_NAME_EC, nil) 114 | defer { EVP_PKEY_CTX_free(ctx) } 115 | guard 116 | EVP_PKEY_fromdata_init(ctx) == 1, 117 | EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY_W, osslParams) == 1, 118 | let pkey = pkey 119 | else { 120 | throw OpenSSLError(name: "Error reading data to pkey") 121 | } 122 | 123 | let validationContext = EVP_PKEY_CTX_new_from_pkey(nil, pkey, nil) 124 | defer { EVP_PKEY_CTX_free(validationContext) } 125 | // NOTE: Functions return -2 if the operation is not supported for the specific algorithm. Not an issue now. 126 | guard 127 | EVP_PKEY_param_check(validationContext) > 0, 128 | EVP_PKEY_public_check(validationContext) > 0 129 | else { 130 | throw OpenSSLError(name: "Error when validating the components of the key (bad data?)") 131 | } 132 | 133 | self.pkey = pkey 134 | } 135 | 136 | convenience init(x962: Data) throws { 137 | let group = Curve.group 138 | let length = x962.count 139 | guard length == (group.coordinateByteCount * 2) + 1 else { 140 | throw OpenSSLError(name: "incorrectParameterSize") 141 | } 142 | try self.init(data: x962) 143 | } 144 | 145 | convenience init(compact: Data) throws { 146 | let group = Curve.group 147 | let length = compact.count 148 | guard length == group.coordinateByteCount + 1 else { 149 | throw OpenSSLError(name: "incorrectParameterSize") 150 | } 151 | try self.init(data: compact) 152 | } 153 | 154 | init(pem _: Data) throws { 155 | throw OpenSSLError(name: "Not implemented") 156 | } 157 | 158 | deinit { 159 | EVP_PKEY_free(pkey) 160 | } 161 | 162 | func rawValue() throws -> Data { 163 | var buffer: UnsafeMutablePointer? 164 | let size = i2d_PublicKey(pkey, &buffer) 165 | guard size > 0, let safeBuffer = buffer else { 166 | throw OpenSSLError(name: "Unable to retrieve public key data") 167 | } 168 | return Data(bytesNoCopy: safeBuffer, count: Int(size), deallocator: .free) 169 | } 170 | 171 | func x962Value() throws -> Data { 172 | try rawValue() 173 | } 174 | 175 | func compactValue() throws -> Data { 176 | try Self(data: rawValue(), pointFormatUncompressed: false).rawValue() 177 | } 178 | } 179 | 180 | extension ECPublicKeyImpl { 181 | func verify(signature: ECDSASignature, digest: Digest) throws -> Bool { 182 | let success = try signature.derBytes.withUnsafeBytes { (signaturePtr: UnsafeRawBufferPointer) in 183 | try digest.withUnsafeBytes { (digestPtr: UnsafeRawBufferPointer) -> Int32 in 184 | let verificationContext = EVP_PKEY_CTX_new_from_pkey(nil, pkey, nil) 185 | defer { EVP_PKEY_CTX_free(verificationContext) } 186 | guard EVP_PKEY_verify_init(verificationContext) == 1 else { 187 | throw OpenSSLError(name: "Error setting up the signature verification context") 188 | } 189 | 190 | let result = EVP_PKEY_verify( 191 | verificationContext, 192 | signaturePtr.bindMemory(to: UInt8.self).baseAddress, 193 | signaturePtr.count, 194 | digestPtr.bindMemory(to: UInt8.self).baseAddress, 195 | digestPtr.count 196 | ) 197 | guard result >= 0 else { 198 | throw OpenSSLError(name: "Serious error when verifying signature (e.g. bad data format, etc.)") 199 | } 200 | return result 201 | } 202 | } 203 | return success == 1 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/CMS/CMSContentInfoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | import Foundation 22 | @testable import OpenSSL 23 | import XCTest 24 | 25 | // swiftlint:disable line_length force_try 26 | final class CMSContentInfoTests: XCTestCase { 27 | func testEncryptRSAOnly() throws { 28 | // given 29 | let x509rsa1 = try X509(pem: x509rsa1.data(using: .utf8)!) 30 | let x509rsa2 = try X509(der: Data(base64Encoded: x509rsa2)!) 31 | let recipients = [x509rsa1, x509rsa2] 32 | let data = message.data(using: .utf8)! 33 | 34 | // when 35 | let cms = try CMSContentInfo.encryptPartial(data: data) 36 | try cms.addRecipientsRSAOnly(recipients) 37 | try cms.final(data: data) 38 | 39 | // then expect sut to contain a byte sequence that represents 40 | // SEQUENCE (2 elem) 41 | // OBJECT IDENTIFIER 1.2.840.113549.1.1.7 rsaOAEP (PKCS #1) 42 | // SEQUENCE (2 elem) 43 | // [0] (1 elem) 44 | // SEQUENCE (1 elem) 45 | // OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1 sha-256 (NIST Algorithm) 46 | // [1] (1 elem) 47 | // SEQUENCE (2 elem) 48 | // OBJECT IDENTIFIER 1.2.840.113549.1.1.8 pkcs1-MGF (PKCS #1) 49 | // SEQUENCE (1 elem) 50 | // OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1 sha-256 (NIST Algorithm) 51 | XCTAssertTrue( 52 | cms.derBytes?.hexString().uppercased().contains( 53 | "303806092A864886F70D010107302BA00D300B0609608648016503040201A11A301806092A864886F70D010108300B0609608648016503040201" 54 | ) ?? false 55 | ) 56 | } 57 | 58 | let message = 59 | """ 60 | { 61 | \t"version": "2", 62 | \t"supplyOptionsType": "delivery", 63 | \t"name": "Dr. Maximilian von Muster", 64 | \t"address": [ 65 | \t\t"Bundesallee", 66 | \t\t"312", 67 | \t\t"12345", 68 | \t\t"Berlin" 69 | \t], 70 | \t"hint": "Bitte im Morsecode klingeln: -.-.", 71 | \t"text": "123456", 72 | \t"phone": "004916094858168", 73 | \t"mail": "max@musterfrau.de", 74 | \t"transaction": "ee63e415-9a99-4051-ab07-257632faf985", 75 | \t"taskID": "160.123.456.789.123.58", 76 | \t"accessCode": "777bea0e13cc9c42ceec14aec3ddee2263325dc2c6c699db115f58fe423607ea" 77 | } 78 | """ 79 | 80 | let x509rsa1 = 81 | """ 82 | -----BEGIN CERTIFICATE----- 83 | MIIFSTCCBDGgAwIBAgIHAXLewUJXxjANBgkqhkiG9w0BAQsFADCBmjELMAkGA1UE 84 | BhMCREUxHzAdBgNVBAoMFmdlbWF0aWsgR21iSCBOT1QtVkFMSUQxSDBGBgNVBAsM 85 | P0luc3RpdHV0aW9uIGRlcyBHZXN1bmRoZWl0c3dlc2Vucy1DQSBkZXIgVGVsZW1h 86 | dGlraW5mcmFzdHJ1a3R1cjEgMB4GA1UEAwwXR0VNLlNNQ0ItQ0EyNCBURVNULU9O 87 | TFkwHhcNMjAwMTI0MDAwMDAwWhcNMjQxMjExMjM1OTU5WjCB5TELMAkGA1UEBhMC 88 | REUxEDAOBgNVBAcMB0hhbWJ1cmcxDjAMBgNVBBEMBTIyNDUzMRgwFgYDVQQJDA9I 89 | ZXNlbHN0w7xja2VuIDkxKjAoBgNVBAoMITMtU01DLUItVGVzdGthcnRlLTg4MzEx 90 | MDAwMDExNjg3MzEdMBsGA1UEBRMUODAyNzY4ODMxMTAwMDAxMTY4NzMxEjAQBgNV 91 | BAQMCVNjaHJhw59lcjESMBAGA1UEKgwJU2llZ2ZyaWVkMScwJQYDVQQDDB5BcG90 92 | aGVrZSBhbSBGbHVnaGFmZW5URVNULU9OTFkwggEiMA0GCSqGSIb3DQEBAQUAA4IB 93 | DwAwggEKAoIBAQCZ9ihWMq2T1C9OEoXpbWJWjALF/X6pbRmzmln2gdRxW7k/BS59 94 | YpONamWX3Wmjc7ELpmiU+5atOpSrFhS7QCQomTyCbnuIYOB6WVaYgDREceZ7bu29 95 | QxD04aHGGrOwaU/55i4f3JTa88QtyMOqPEA/YW3XoCKdPwouiVEP8AXJ+8dRiYCS 96 | SzPUKOOy+R53sMhrTmpkwGNfOmq9Kg1uX8NRDg0Lamv41O9XbsfJTuzVa4EcKALx 97 | HEMprsUokV9WaGVK0nHCyU0TTi6V9EqslVoK1iyMgUUl2nfx1/aRtUViFbXtd6DR 98 | 6SeUhcqIzFOVBnl9EY4alAnHfR/qE8iBe6bbAgMBAAGjggFFMIIBQTAdBgNVHQ4E 99 | FgQUGRLcBNLvAKTcCYYIS+HLzaac0EAwDAYDVR0TAQH/BAIwADA4BggrBgEFBQcB 100 | AQQsMCowKAYIKwYBBQUHMAGGHGh0dHA6Ly9laGNhLmdlbWF0aWsuZGUvb2NzcC8w 101 | DgYDVR0PAQH/BAQDAgQwMB8GA1UdIwQYMBaAFHrp4W/qFFkWBe4D6dP9Iave6dme 102 | MCAGA1UdIAQZMBcwCgYIKoIUAEwEgSMwCQYHKoIUAEwETDCBhAYFKyQIAwMEezB5 103 | pCgwJjELMAkGA1UEBhMCREUxFzAVBgNVBAoMDmdlbWF0aWsgQmVybGluME0wSzBJ 104 | MEcwFwwVw5ZmZmVudGxpY2hlIEFwb3RoZWtlMAkGByqCFABMBDYTITMtU01DLUIt 105 | VGVzdGthcnRlLTg4MzExMDAwMDExNjg3MzANBgkqhkiG9w0BAQsFAAOCAQEALmkJ 106 | S6sCvx0cyDcFMRFiCJ7Po3H6jAPGgVmuQsldo+AHcjN7YAuM/7JwOBulvycZOEBi 107 | Mf+NYkjfzQRM16h9SHspjFsr8yD78u0JfdKJEYWnpTUEDTl0C0ssv++obWLyw/lj 108 | 1623pjn5Kb0x5yjEbzSGo3kk5S050Bnwf39JGVzv2M1j31y9CQQSAxT3EKl937Gj 109 | 306acGmt6vjDDd0GB8P6nPreulTYh1M0Tlli53gfP7o987q2Pq/jIK13ExF6t5WN 110 | PCqpN2JbFY8waA6PzoT57zKdT6sB/w26rA2Gnc9eGp9pZ9DH11Qw+x+SArCs1eEh 111 | 0jqYhPIqIs2gJPl3hw== 112 | -----END CERTIFICATE----- 113 | """ 114 | 115 | let x509rsa2 = 116 | "MIIE4TCCA8mgAwIBAgIDD0vlMA0GCSqGSIb3DQEBCwUAMIGuMQswCQYDVQQGEwJERTEzMDEGA1UECgwqQXRvcyBJbmZvcm1hdGlvbiBUZWNobm9sb2d5IEdtYkggTk9ULVZBTElEMUgwRgYDVQQLDD9JbnN0aXR1dGlvbiBkZXMgR2VzdW5kaGVpdHN3ZXNlbnMtQ0EgZGVyIFRlbGVtYXRpa2luZnJhc3RydWt0dXIxIDAeBgNVBAMMF0FUT1MuU01DQi1DQTMgVEVTVC1PTkxZMB4XDTE5MDkxNzEyMzYxNloXDTI0MDkxNzEyMzYxNlowXDELMAkGA1UEBhMCREUxIDAeBgNVBAoMFzEtMjExMjM0NTY3ODkgTk9ULVZBTElEMSswKQYDVQQDDCJBcnp0cHJheGlzIERyLiBBxJ9hb8SfbHUgVEVTVC1PTkxZMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmdmUeBLB6UDh4u8FAvi7B3hpAhJYXBlx+IJXLiSrhgCu/T/L5vVlCQb+1gYybWhHT5YlxafTJpOcXSfcixJbFWGxn+iQLqo+LCp/ljLBz5JoU+IXIxRKZCi5SZ9APeglGs4R0/xpPBtsJzihFXVu+B8qGm2oqmvVV91u+MoJ5asC6C+rVOecLxqy/OdmeKfaNSgH2NxVzNc19VmFUkFDGUFJjG4ZgatW4V6AuAhiPnDkEg8gfXr5L7ycQRZUNlEGMmDhh+noHU/doxSU2cgBaiTZNmu17FJLXlBLRISpWcQitcjOkjrJDt4Z0Yta64yZe13+a5dANh32Zeeg5jDQRQIDAQABo4IBVzCCAVMwHQYDVR0OBBYEFF/uDhGziRKzsUC9Nkat5xQojOUZMA4GA1UdDwEB/wQEAwIEMDAMBgNVHRMBAf8EAjAAMCAGA1UdIAQZMBcwCQYHKoIUAEwETDAKBggqghQATASBIzBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLXNtY2IuZWdrLXRlc3QtdHNwLmRlL0FUT1MuU01DQi1DQTNfVEVTVC1PTkxZLmNybDA8BggrBgEFBQcBAQQwMC4wLAYIKwYBBQUHMAGGIGh0dHA6Ly9vY3NwLXNtY2IuZWdrLXRlc3QtdHNwLmRlMB8GA1UdIwQYMBaAFD+eHl4mKtYMlaF4nqrz1drzQaf8MEUGBSskCAMDBDwwOjA4MDYwNDAyMBYMFEJldHJpZWJzc3TDpHR0ZSBBcnp0MAkGByqCFABMBDITDTEtMjExMjM0NTY3ODkwDQYJKoZIhvcNAQELBQADggEBACUnL3MxjyoEyUBRxcBAjl7FdePW0O1/UCeDAbH2b4ob9GjMGjL5OoBmhj9GsUORg/K4cIiqTot2TcPtdooKCI5a5Jupp0nYoAuzdrNlvGYEm0S/cvlyYJXjfhrEIHmlDY0/hpJX3S/hYgkniJ1Wg70MfLLcib05+31OijZmEzpChioIm4KmumEKU4ODsLWr/4OEw9KCYfuNpjiSyyAEd2pMgnGU8MKCJhrR/ZKSteAxAPKTXVtNTKndbptvcsaEZPp//vNdbBh+k8P642P2DHYfeDoUgivEYXdE5ABixtG9sk1Q2DPfTXoS+CKv45ae0vejBnRjuA28lmkmuIp+f+s=" 117 | 118 | let x509eccPem = 119 | """ 120 | -----BEGIN CERTIFICATE----- 121 | MIIDvDCCA2OgAwIBAgIHAN6t0nKF8TAKBggqhkjOPQQDAjCBmTELMAkGA1UEBhMC 122 | REUxHzAdBgNVBAoMFmdlbWF0aWsgR21iSCBOT1QtVkFMSUQxSDBGBgNVBAsMP0lu 123 | c3RpdHV0aW9uIGRlcyBHZXN1bmRoZWl0c3dlc2Vucy1DQSBkZXIgVGVsZW1hdGlr 124 | aW5mcmFzdHJ1a3R1cjEfMB0GA1UEAwwWR0VNLlNNQ0ItQ0E5IFRFU1QtT05MWTAe 125 | Fw0yMDAxMjQwMDAwMDBaFw0yNDEyMTEyMzU5NTlaMIHlMQswCQYDVQQGEwJERTEQ 126 | MA4GA1UEBwwHSGFtYnVyZzEOMAwGA1UEEQwFMjI0NTMxGDAWBgNVBAkMD0hlc2Vs 127 | c3TDvGNrZW4gOTEqMCgGA1UECgwhMy1TTUMtQi1UZXN0a2FydGUtODgzMTEwMDAw 128 | MTE2ODczMR0wGwYDVQQFExQ4MDI3Njg4MzExMDAwMDExNjg3MzESMBAGA1UEBAwJ 129 | U2NocmHDn2VyMRIwEAYDVQQqDAlTaWVnZnJpZWQxJzAlBgNVBAMMHkFwb3RoZWtl 130 | IGFtIEZsdWdoYWZlblRFU1QtT05MWTBaMBQGByqGSM49AgEGCSskAwMCCAEBBwNC 131 | AASQgPBIpSefm85uPe99rvPzdr/FjR8BjDf+o2Z6vaRpO2ACx7ehSNuDHW+OtOZ5 132 | hvm7ONskNAOoWasDt1wJ4t1eo4IBRTCCAUEwHQYDVR0OBBYEFDbdoSwPnHLJr04A 133 | LfE7RkGmYy46MA4GA1UdDwEB/wQEAwIDCDAgBgNVHSAEGTAXMAoGCCqCFABMBIEj 134 | MAkGByqCFABMBEwwHwYDVR0jBBgwFoAUYoiaxN78o/OTOcufkOcTmj2JzHUwOAYI 135 | KwYBBQUHAQEELDAqMCgGCCsGAQUFBzABhhxodHRwOi8vZWhjYS5nZW1hdGlrLmRl 136 | L29jc3AvMAwGA1UdEwEB/wQCMAAwgYQGBSskCAMDBHsweaQoMCYxCzAJBgNVBAYT 137 | AkRFMRcwFQYDVQQKDA5nZW1hdGlrIEJlcmxpbjBNMEswSTBHMBcMFcOWZmZlbnRs 138 | aWNoZSBBcG90aGVrZTAJBgcqghQATAQ2EyEzLVNNQy1CLVRlc3RrYXJ0ZS04ODMx 139 | MTAwMDAxMTY4NzMwCgYIKoZIzj0EAwIDRwAwRAIgOOxowEl8laLh5qRTy5prce49 140 | kxKocntxvCCcjtSHwlYCIG3AuFFTWw/LVvopPTrjv6neLca0kKFfIs3Nw1bPw/VG 141 | -----END CERTIFICATE----- 142 | """ 143 | } 144 | 145 | // swiftlint:enable line_length force_try 146 | -------------------------------------------------------------------------------- /scripts/install_openssl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # scripts/install_openssl: Download, verify and unpack OpenSSL 4 | # $ ./scripts/install_openssl {VERSION:1.1.0j} {SHA256:31...46} 5 | 6 | set -o pipefail 7 | 8 | # The install script uses the PLATFORM_NAME environment variable to decide wether to build for iOS or MacOS 9 | # Xcodebuild preBuildPhase scripts get this variable injected automatically and will therefore 10 | # be there. Only when building manually from command line this variable needs to be set beforehand. 11 | : ${PLATFORM_NAME?"Need to set PLATFORM_NAME (e.g. macosx, iphoneos)"} 12 | echo "PLATFORM_NAME: $PLATFORM_NAME" 13 | echo "ARCHS: $ARCHS" 14 | 15 | cd "$(dirname $0)/.." 16 | DIRNAME=$(pwd) 17 | 18 | VERSION=${1-"3.4.0"} 19 | VERSION_SHA256_CHECKSUM=${2-"e15dda82fe2fe8139dc2ac21a36d4ca01d5313c75f99f46c4e8a27709b7294bf"} 20 | 21 | #################################### 22 | OPENSSL_IMAGE="${DIRNAME}/openssl-${VERSION}.tar.gz" 23 | 24 | function deleteWhenExists() { 25 | [ ! -d "$1" ] || { 26 | echo "==> Delete (-Rf) [$1]" 27 | rm -Rf "$1" &2>/dev/null 28 | } 29 | [ ! -f "$1" ] || { 30 | echo "==> Delete (file) [$1]" 31 | rm -f "$1" &2>/dev/null 32 | } 33 | [ ! -L "$1" ] || { 34 | echo "==> Delete (symlink) [$1]" 35 | rm -f "$1" &2>/dev/null 36 | } 37 | } 38 | 39 | # Build for a given configuration 40 | function build_for() 41 | { 42 | PLATFORM=$1 43 | ARCH=$2 44 | CROSS_TOP_ENV=CROSS_TOP_$3 45 | CROSS_SDK_ENV=CROSS_SDK_$3 46 | PLATFORM_DIR="openssl_${PLATFORM}_${VERSION}" 47 | 48 | TARGET_DIR="${DIRNAME}/tmp/${PLATFORM_DIR}" 49 | deleteWhenExists "$TARGET_DIR" 50 | 51 | # Unzip into platform folder 52 | cd "$DIRNAME" 53 | 54 | OPENSSL_SRC_DIR_VERSION="openssl-$VERSION" 55 | deleteWhenExists "$OPENSSL_SRC_DIR_VERSION" 56 | deleteWhenExists "$PLATFORM_DIR" 57 | 58 | tar -xzf "${OPENSSL_IMAGE}" 59 | mv "$OPENSSL_SRC_DIR_VERSION" "$PLATFORM_DIR" 60 | 61 | pushd "$PLATFORM_DIR" 62 | patch "Configurations/10-main.conf" < "${DIRNAME}/Config/ios-conf.patch" 63 | if [ ${PLATFORM_NAME} != "macosx" ]; then 64 | patch "Configurations/shared-info.pl" < "${DIRNAME}/Config/shared-info.pl.patch" 65 | fi 66 | export MACOSX_DEPLOYMENT_TARGET="10.4" 67 | 68 | if [ ! -z $3 ]; then 69 | echo "Setting CROSS_TOP and CROSS_SDK" 70 | export CROSS_TOP="${!CROSS_TOP_ENV}" 71 | export CROSS_SDK="${!CROSS_SDK_ENV}" 72 | fi 73 | echo "./Configure $PLATFORM \"-arch $ARCH -fembed-bitcode\" no-asm no-ssl3 no-comp no-engine no-async --prefix=\"${TARGET_DIR}\"" 74 | if [ ${PLATFORM_NAME} == "macosx" ]; then 75 | ./Configure $PLATFORM "-arch $ARCH" no-asm no-ssl3 no-comp no-engine no-async --prefix="${TARGET_DIR}" || exit 1 76 | else 77 | ./Configure $PLATFORM "-arch $ARCH -fembed-bitcode" no-asm no-ssl3 no-comp no-engine no-async --prefix="${TARGET_DIR}" || exit 1 78 | fi 79 | # problem of concurrent build; make -j8 80 | echo "Building $PLATFORM:$ARCH static library..." 81 | make >> /dev/null 2>&1 || exit 1 82 | make install_sw >> /dev/null 2>&1 || exit 1 83 | unset CROSS_TOP 84 | unset CROSS_SDK 85 | 86 | popd 87 | deleteWhenExists "$PLATFORM_DIR" 88 | } 89 | 90 | function createLibInputFilePaths() { 91 | DIRNAME=$1 92 | LIBNAME=$2 93 | VERSION=$3 94 | 95 | IFS=',' read -ra PLATFORM_NAMES <<< "$4" 96 | for NAME in "${PLATFORM_NAMES[@]}"; 97 | do 98 | echo "${DIRNAME}/tmp/openssl_${NAME}_${VERSION}/lib/lib${LIBNAME}.a " 99 | done 100 | } 101 | 102 | # Create FAT binaries for a given lib and put them into out dir 103 | function pack_for() 104 | { 105 | LIBNAME=$1 106 | OUT_DIR=$2 107 | PLATFORMS=$3 108 | PLATFORM_SUFFIX=$4 109 | 110 | CREATE_ARGUMENT=$(createLibInputFilePaths $DIRNAME $LIBNAME $VERSION $PLATFORMS) 111 | 112 | ${DEVROOT}/usr/bin/lipo -create ${CREATE_ARGUMENT} \ 113 | -output "${OUT_DIR}/lib${LIBNAME}_release_${VERSION}_${PLATFORM_SUFFIX}.a" 114 | 115 | PLATFORM=$(echo $PLATFORMS | sed 's/,.*//') 116 | cp -r "${DIRNAME}/tmp/openssl_${PLATFORM}_${VERSION}/include" "${OUT_DIR}/" 117 | } 118 | 119 | OPENSSL_CRYPTO_LIBNAME="crypto" 120 | OPENSSL_SSL_LIBNAME="ssl" 121 | OPENSSL_LIB_FOLDER="$DIRNAME/lib" 122 | TARGET_OPENSSL_CRYPTO_LIB_IOS="${OPENSSL_LIB_FOLDER}/lib${OPENSSL_CRYPTO_LIBNAME}_release_${VERSION}_iphoneos.a" 123 | TARGET_OPENSSL_SSL_LIB_IOS="${OPENSSL_LIB_FOLDER}/lib${OPENSSL_SSL_LIBNAME}_release_${VERSION}_iphoneos.a" 124 | TARGET_OPENSSL_CRYPTO_LIB_SIMULATOR="${OPENSSL_LIB_FOLDER}/lib${OPENSSL_CRYPTO_LIBNAME}_release_${VERSION}_iphonesimulator.a" 125 | TARGET_OPENSSL_SSL_LIB_SIMULATOR="${OPENSSL_LIB_FOLDER}/lib${OPENSSL_SSL_LIBNAME}_release_${VERSION}_iphonesimulator.a" 126 | TARGET_OPENSSL_CRYPTO_LIB_MAC="${OPENSSL_LIB_FOLDER}/lib${OPENSSL_CRYPTO_LIBNAME}_release_${VERSION}_macOS.a" 127 | TARGET_OPENSSL_SSL_LIB_MAC="${OPENSSL_LIB_FOLDER}/lib${OPENSSL_SSL_LIBNAME}_release_${VERSION}_macOS.a" 128 | 129 | SKIP_BUILD=1 130 | if [ ${PLATFORM_NAME} == "macosx" ]; then 131 | if [[ ! -f ${TARGET_OPENSSL_CRYPTO_LIB_MAC} || ! -f ${TARGET_OPENSSL_SSL_LIB_MAC} ]]; then 132 | SKIP_BUILD=0 133 | fi 134 | else 135 | # iphoneos missing 136 | if [[ ! -f ${TARGET_OPENSSL_CRYPTO_LIB_IOS} || ! -f ${TARGET_OPENSSL_SSL_LIB_IOS} ]]; then 137 | SKIP_BUILD=0 138 | fi 139 | # iphonesimulator missing 140 | if [[ ! -f ${TARGET_OPENSSL_CRYPTO_LIB_SIMULATOR} || ! -f ${TARGET_OPENSSL_SSL_LIB_SIMULATOR} ]]; then 141 | SKIP_BUILD=0 142 | fi 143 | fi 144 | 145 | if [ $SKIP_BUILD != 1 ]; then 146 | # Download when needed 147 | echo "Download OpenSSL when needed..." 148 | if [ ! -f ${OPENSSL_IMAGE} ]; then 149 | curl -SsLO "https://www.openssl.org/source/openssl-$VERSION.tar.gz" 150 | else 151 | echo "Skipping download..." 152 | fi 153 | 154 | # Run a checksum to ensure this file wasn't tampered with 155 | FILE_CHECKSUM=$(shasum -a 256 "${OPENSSL_IMAGE}" | awk '{print $1; exit}') 156 | if [ "$FILE_CHECKSUM" != "$VERSION_SHA256_CHECKSUM" ]; then 157 | echo "FILE_CHECKSUM: $FILE_CHECKSUM" 158 | echo "OpenSSL v$VERSION failed checksum. Please ensure that you are on a trusted network." 159 | exit 1 160 | fi 161 | 162 | if [ ${PLATFORM_NAME} == "macosx" ]; then 163 | # Build openssl_x86_64 164 | build_for darwin64-x86_64-cc x86_64 || exit 1 165 | build_for darwin64-arm64-cc arm64 || exit 1 166 | else 167 | # Build for Simulator 168 | CROSS_TOP_SIM="`xcode-select --print-path`/Platforms/iPhoneSimulator.platform/Developer" 169 | CROSS_SDK_SIM="iPhoneSimulator.sdk" 170 | # Build for iOS 171 | CROSS_TOP_IOS="`xcode-select --print-path`/Platforms/iPhoneOS.platform/Developer" 172 | CROSS_SDK_IOS="iPhoneOS.sdk" 173 | 174 | export CROSS_COMPILE=`xcode-select --print-path`/Toolchains/XcodeDefault.xctoolchain/usr/bin/ 175 | 176 | build_for ios64sim-cross x86_64 SIM || exit 1 177 | build_for ios64-cross-fix arm64 IOS || exit 1 178 | 179 | export CROSS_COMPILE="" # set via `CC` within ios-conf.patch 180 | 181 | build_for ios64sim-arm64-cross arm64 SIM || exit 1 182 | 183 | unset CROSS_COMPILE 184 | fi 185 | 186 | # Copy include and License into main OpenSSL folder (done after configure so can be generated) 187 | #cp "${DIRNAME}/openssl_ios64-cross-fix_${VERSION}/LICENSE" "${OPENSSL_LIB_FOLDER}" # Copy License 188 | 189 | # Link 190 | echo "Linking..." 191 | if [ ${PLATFORM_NAME} == "macosx" ]; then 192 | # macOS 193 | pack_for $OPENSSL_SSL_LIBNAME "$OPENSSL_LIB_FOLDER" darwin64-x86_64-cc,darwin64-arm64-cc macOS || exit 1 194 | pack_for $OPENSSL_CRYPTO_LIBNAME "$OPENSSL_LIB_FOLDER" darwin64-x86_64-cc,darwin64-arm64-cc macOS || exit 1 195 | else 196 | # iOS 197 | pack_for $OPENSSL_SSL_LIBNAME "$OPENSSL_LIB_FOLDER" ios64-cross-fix iphoneos || exit 1 198 | pack_for $OPENSSL_CRYPTO_LIBNAME "$OPENSSL_LIB_FOLDER" ios64-cross-fix iphoneos || exit 1 199 | pack_for $OPENSSL_SSL_LIBNAME "$OPENSSL_LIB_FOLDER" ios64sim-cross,ios64sim-arm64-cross iphonesimulator || exit 1 200 | pack_for $OPENSSL_CRYPTO_LIBNAME "$OPENSSL_LIB_FOLDER" ios64sim-cross,ios64sim-arm64-cross iphonesimulator || exit 1 201 | fi 202 | else 203 | echo "Skipping build..." 204 | fi 205 | 206 | pushd "$OPENSSL_LIB_FOLDER" 207 | if [ ${PLATFORM_NAME} == "macosx" ]; then 208 | ln -sf "lib${OPENSSL_CRYPTO_LIBNAME}_release_${VERSION}_macOS.a" "lib${OPENSSL_CRYPTO_LIBNAME}_release_macOS.a" 209 | ln -sf "lib${OPENSSL_SSL_LIBNAME}_release_${VERSION}_macOS.a" "lib${OPENSSL_SSL_LIBNAME}_release_macOS.a" 210 | else 211 | if [ ${PLATFORM_NAME} == "iphoneos" ]; then 212 | ln -sf "lib${OPENSSL_CRYPTO_LIBNAME}_release_${VERSION}_iphoneos.a" "lib${OPENSSL_CRYPTO_LIBNAME}_release_iphoneos.a" 213 | ln -sf "lib${OPENSSL_SSL_LIBNAME}_release_${VERSION}_iphoneos.a" "lib${OPENSSL_SSL_LIBNAME}_release_iphoneos.a" 214 | else 215 | ln -sf "lib${OPENSSL_CRYPTO_LIBNAME}_release_${VERSION}_iphonesimulator.a" "lib${OPENSSL_CRYPTO_LIBNAME}_release_iphonesimulator.a" 216 | ln -sf "lib${OPENSSL_SSL_LIBNAME}_release_${VERSION}_iphonesimulator.a" "lib${OPENSSL_SSL_LIBNAME}_release_iphonesimulator.a" 217 | fi 218 | fi 219 | 220 | rm "${OPENSSL_LIB_FOLDER}/include/openssl/asn1_mac.h" &2>/dev/null 221 | 222 | echo "# OpenSSL info - $(date "+%H:%M:%S") 223 | # VERSION=\"${VERSION}\" 224 | # VERSION_SHA256_CHECKSUM=\"${VERSION_SHA256_CHECKSUM}\" \ 225 | " > hash.txt 226 | 227 | popd 228 | 229 | # Cleanup 230 | echo "Cleaning..." 231 | rm "${OPENSSL_IMAGE}" 232 | rm -Rf "${DIRNAME}/tmp" 233 | 234 | echo "Finished OpenSSL generation script." 235 | -------------------------------------------------------------------------------- /Tests/OpenSSLTests/Certificate/X509Tests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | import Foundation 22 | @testable import OpenSSL 23 | import XCTest 24 | 25 | // swiftlint:disable line_length force_try 26 | final class X509Tests: XCTestCase { 27 | let bundle = Bundle(for: X509Tests.self) 28 | lazy var discoveryDocument: X509 = { 29 | let filename = "X509/GEM.DISCOVERY-DOC-TEST-ONLY.pem" 30 | return try! X509(pem: ResourceFileReader.readFileInResourceBundle(filePath: filename, for: bundle)) 31 | }() 32 | 33 | lazy var caCertificate: X509 = { 34 | let filename = "X509/GEM.KOMP-CA10-TEST-ONLY.pem" 35 | return try! X509(pem: ResourceFileReader.readFileInResourceBundle(filePath: filename, for: bundle)) 36 | }() 37 | 38 | lazy var rootCertificate: X509 = { 39 | let filename = "X509/GEM.RCA3-TEST-ONLY.pem" 40 | return try! X509(pem: ResourceFileReader.readFileInResourceBundle(filePath: filename, for: bundle)) 41 | }() 42 | 43 | lazy var pharmacyAdelheidRsaPubKeyCertificate: X509 = { 44 | let filename = "X509/PHARMACY.ADELHEID-RSA-TEST-ONLY.pem" 45 | return try! X509(pem: ResourceFileReader.readFileInResourceBundle(filePath: filename, for: bundle)) 46 | }() 47 | 48 | lazy var pharmacyAdelheidEcPubKeyCertificate: X509 = { 49 | let filename = "X509/PHARMACY.ADELHEID-EC-TEST-ONLY.pem" 50 | return try! X509(pem: ResourceFileReader.readFileInResourceBundle(filePath: filename, for: bundle)) 51 | }() 52 | 53 | func testDerBytes() throws { 54 | // when 55 | let derBytes = discoveryDocument.derBytes 56 | 57 | // then 58 | let base64 = 59 | "MIICsTCCAligAwIBAgIHA61I5ACUjTAKBggqhkjOPQQDAjCBhDELMAkGA1UEBhMCREUxHzAdBgNVBAoMFmdlbWF0aWsgR21iSCBOT1QtVkFMSUQxMjAwBgNVBAsMKUtvbXBvbmVudGVuLUNBIGRlciBUZWxlbWF0aWtpbmZyYXN0cnVrdHVyMSAwHgYDVQQDDBdHRU0uS09NUC1DQTEwIFRFU1QtT05MWTAeFw0yMDA4MDQwMDAwMDBaFw0yNTA4MDQyMzU5NTlaMEkxCzAJBgNVBAYTAkRFMSYwJAYDVQQKDB1nZW1hdGlrIFRFU1QtT05MWSAtIE5PVC1WQUxJRDESMBAGA1UEAwwJSURQIFNpZyAxMFowFAYHKoZIzj0CAQYJKyQDAwIIAQEHA0IABJZQrG1NWxIB3kz/6Z2zojlkJqN3vJXZ3EZnJ6JXTXw5ZDFZ5XjwWmtgfomv3VOV7qzI5ycUSJysMWDEu3mqRcajge0wgeowHQYDVR0OBBYEFJ8DVLAZWT+BlojTD4MT/Na+ES8YMDgGCCsGAQUFBwEBBCwwKjAoBggrBgEFBQcwAYYcaHR0cDovL2VoY2EuZ2VtYXRpay5kZS9vY3NwLzAMBgNVHRMBAf8EAjAAMCEGA1UdIAQaMBgwCgYIKoIUAEwEgUswCgYIKoIUAEwEgSMwHwYDVR0jBBgwFoAUKPD45qnId8xDRduartc6g6wOD6gwLQYFKyQIAwMEJDAiMCAwHjAcMBowDAwKSURQLURpZW5zdDAKBggqghQATASCBDAOBgNVHQ8BAf8EBAMCB4AwCgYIKoZIzj0EAwIDRwAwRAIgVBPhAwyX8HAVH0O0b3+VazpBAWkQNjkEVRkv+EYX1e8CIFdn4O+nivM+XVi9xiKK4dW1R7MD334OpOPTFjeEhIVV" 60 | let expected = Data(base64Encoded: base64, options: .ignoreUnknownCharacters) 61 | XCTAssertEqual(derBytes, expected) 62 | } 63 | 64 | func testX509CertificateSerialNumber() throws { 65 | // when 66 | let serialNumber = try discoveryDocument.serialNumber() 67 | 68 | // then 69 | XCTAssertEqual(serialNumber, "1034953504625805") 70 | } 71 | 72 | func testX509CertificatePublicKeyAlgorithm() { 73 | XCTAssertEqual(pharmacyAdelheidRsaPubKeyCertificate.publicKeyAlgorithm(), .rsaEncryption) 74 | 75 | XCTAssertEqual(pharmacyAdelheidEcPubKeyCertificate.publicKeyAlgorithm(), .ellipticCurve) 76 | } 77 | 78 | func testIssuer() throws { 79 | // when 80 | let issuerData = discoveryDocument.issuerX500PrincipalDEREncoded() 81 | 82 | // then 83 | XCTAssertEqual( 84 | issuerData?.hexString().uppercased(), 85 | "308184310B3009060355040613024445311F301D060355040A0C1667656D6174696B20476D6248204E4F542D56414C494431323030060355040B0C294B6F6D706F6E656E74656E2D4341206465722054656C656D6174696B696E667261737472756B7475723120301E06035504030C1747454D2E4B4F4D502D4341313020544553542D4F4E4C59" 86 | ) 87 | 88 | if #available(iOS 16.0, macOS 13.0, *) { 89 | XCTAssertTrue(issuerData!.contains("GEM.KOMP-CA10 TEST-ONLY".data(using: .ascii)!)) 90 | } 91 | } 92 | 93 | func testSubject() { 94 | // when 95 | let subjectData = discoveryDocument.subjectX500PrincipalDEREncoded() 96 | 97 | // then 98 | XCTAssertEqual( 99 | subjectData?.hexString().uppercased(), 100 | "3049310B300906035504061302444531263024060355040A0C1D67656D6174696B20544553542D4F4E4C59202D204E4F542D56414C49443112301006035504030C09494450205369672031" 101 | ) 102 | if #available(iOS 16.0, macOS 13.0, *) { 103 | XCTAssertTrue(subjectData!.contains("IDP Sig 1".data(using: .ascii)!)) 104 | } 105 | } 106 | 107 | func testX509CertificateNotBeforeAfter() throws { 108 | // when 109 | let notBefore = try discoveryDocument.notBefore() 110 | let notAfter = try discoveryDocument.notAfter() 111 | 112 | // then 113 | let formatter = ISO8601DateFormatter() 114 | let expectedNotBefore = formatter.date(from: "2020-08-04T00:00:00Z") 115 | let expectedNotAfter = formatter.date(from: "2025-08-04T23:59:59Z") 116 | XCTAssertEqual(notBefore, expectedNotBefore) 117 | XCTAssertEqual(notAfter, expectedNotAfter) 118 | } 119 | 120 | func testX509CertificateBrainpoolP256r1VerifyPublicKey() throws { 121 | // when 122 | let brainpoolP256r1PublicKey = discoveryDocument.brainpoolP256r1VerifyPublicKey() 123 | 124 | // then 125 | let expected = try BrainpoolP256r1.Verify.PublicKey( 126 | x962: Data( 127 | hex: "049650AC6D4D5B1201DE4CFFE99DB3A2396426A377BC95D9DC466727A2574D7C39643159E578F05A6B607E89AFDD5395EEACC8E72714489CAC3160C4BB79AA45C6" 128 | ) 129 | ) 130 | XCTAssertNotNil(brainpoolP256r1PublicKey) 131 | XCTAssertEqual(try brainpoolP256r1PublicKey?.rawValue(), try expected.rawValue()) 132 | } 133 | 134 | func testX509CertificateBrainpoolP256r1KeyExchangePublicKey() throws { 135 | // when 136 | let brainpoolP256r1PublicKey = discoveryDocument.brainpoolP256r1KeyExchangePublicKey() 137 | 138 | // then 139 | let expected = try BrainpoolP256r1.KeyExchange 140 | .PublicKey( 141 | x962: Data( 142 | hex: "049650AC6D4D5B1201DE4CFFE99DB3A2396426A377BC95D9DC466727A2574D7C39643159E578F05A6B607E89AFDD5395EEACC8E72714489CAC3160C4BB79AA45C6" 143 | ) 144 | ) 145 | XCTAssertNotNil(brainpoolP256r1PublicKey) 146 | XCTAssertEqual(try brainpoolP256r1PublicKey?.rawValue(), try expected.rawValue()) 147 | } 148 | 149 | func testIsValidCaCertificate() { 150 | XCTAssertTrue(rootCertificate.isValidCaCertificate) 151 | XCTAssertTrue(caCertificate.isValidCaCertificate) 152 | XCTAssertFalse(discoveryDocument.isValidCaCertificate) 153 | } 154 | 155 | func testIsIssuedBy() throws { 156 | XCTAssertTrue(caCertificate.issued(discoveryDocument)) 157 | XCTAssertTrue(rootCertificate.issued(caCertificate)) 158 | XCTAssertFalse(rootCertificate.issued(discoveryDocument)) 159 | } 160 | 161 | func testValidate() throws { 162 | // At Wed Jan 08 2025 09:39:49 GMT+0000 the discovery document can be validated 163 | XCTAssertTrue(try discoveryDocument.validateWith( 164 | trustStore: [caCertificate, rootCertificate], 165 | validationTime: Date(timeIntervalSince1970: 1_736_329_189) 166 | )) 167 | 168 | // At Fri Aug 08 2025 10:49:49 GMT+0000 the discovery document already expired, so it cannot be validated 169 | XCTAssertFalse(try discoveryDocument.validateWith( 170 | trustStore: [caCertificate, rootCertificate], 171 | validationTime: Date(timeIntervalSince1970: 1_754_650_189) 172 | )) 173 | 174 | XCTAssertTrue(try caCertificate.validateWith( 175 | trustStore: [rootCertificate], 176 | validationTime: Date(timeIntervalSince1970: 1_736_329_189) 177 | )) 178 | XCTAssertTrue(try rootCertificate.validateWith( 179 | trustStore: [rootCertificate], 180 | validationTime: Date(timeIntervalSince1970: 1_736_329_189) 181 | )) 182 | 183 | XCTAssertFalse(try discoveryDocument.validateWith( 184 | trustStore: [rootCertificate], 185 | validationTime: Date(timeIntervalSince1970: 1_736_329_189) 186 | )) 187 | XCTAssertFalse(try discoveryDocument.validateWith( 188 | trustStore: [caCertificate], 189 | validationTime: Date(timeIntervalSince1970: 1_736_329_189) 190 | )) 191 | } 192 | 193 | func testX509CertificateComputeSha256Fingerprint() throws { 194 | // when 195 | let sha256Fingerprint = try discoveryDocument.sha256Fingerprint() 196 | 197 | // then 198 | let expected = try Data(hex: "AB9590D2B1764E21277927A1E084EB2E041E022E62F21C4E59155EB747249CFA") 199 | XCTAssertEqual(sha256Fingerprint, expected) 200 | } 201 | 202 | func testX509CertificateSignatureAlgorithm() { 203 | XCTAssertEqual(discoveryDocument.signatureAlgorithm(), .ecdsaWithSHA256) 204 | 205 | XCTAssertEqual(pharmacyAdelheidRsaPubKeyCertificate.signatureAlgorithm(), .sha256WithRsaEncryption) 206 | } 207 | } 208 | 209 | // swiftlint:enable line_length force_try 210 | -------------------------------------------------------------------------------- /Sources/OpenSSL/Certificate/OCSPResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (Change Date see Readme), gematik GmbH 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ******* 17 | // 18 | // For additional notes and disclaimer from gematik and in case of changes by gematik find details in the "Readme" file. 19 | // 20 | 21 | @_implementationOnly import COpenSSL 22 | import Foundation 23 | 24 | /// OCSP Response 25 | public class OCSPResponse { 26 | let ocsp: OpaquePointer 27 | 28 | /// Initialize a OCSP Response from DER representation 29 | /// 30 | /// - Parameter derRepresentation: raw DER bytes 31 | /// - Throws: `OpenSSLError` when the response could not be initialized 32 | public init(der: Data) throws { 33 | ocsp = try der.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in 34 | var bytesPtr = buffer.bindMemory(to: UInt8.self).baseAddress 35 | 36 | guard let raw = d2i_OCSP_RESPONSE(nil, &bytesPtr, buffer.count) else { 37 | throw OpenSSLError(name: "OCSPResponse not DER encoded") 38 | } 39 | 40 | return raw 41 | } 42 | } 43 | 44 | /// De-initialize 45 | deinit { 46 | OCSP_RESPONSE_free(ocsp) 47 | } 48 | 49 | public enum Status: Int { 50 | case successful = 0 51 | case malformedRequest = 1 52 | case internalError = 2 53 | case tryLater = 3 54 | case sigRequired = 4 55 | case unauthorized = 5 56 | } 57 | 58 | /// Returns the response status value 59 | /// 60 | /// - Returns: `OCSP.Status` value 61 | public func status() -> Status { 62 | let res = Int(OCSP_response_status(ocsp)) 63 | guard let status = Status(rawValue: res) else { 64 | return .internalError 65 | } 66 | return status 67 | } 68 | 69 | /// Extract the *producedAt* field from the basis response. 70 | /// 71 | /// - Returns: `Date` from the *producedAt* field 72 | /// - Throws: OpenSSL error on internal error 73 | public func producedAt() throws -> Date { 74 | let basic = OCSP_response_get1_basic(ocsp) 75 | defer { 76 | OCSP_BASICRESP_free(basic) 77 | } 78 | 79 | guard let producedAt = OCSP_resp_get0_produced_at(basic) else { 80 | throw OpenSSLError(name: "ProducedAt not found") 81 | } 82 | return try producedAt.asFoundationDate() 83 | } 84 | 85 | public enum CertStatus: Int { 86 | case good = 0 87 | case revoked = 1 88 | case unknown = 2 89 | case requestedCertificateNotInResponse = 99 90 | } 91 | 92 | /// Search the OCSP response for a status information for a given `X509` certificate. 93 | /// Note: the issuer of the certificate has also to be provided 94 | /// 95 | /// - Parameters: 96 | /// - for: the certificate the status is requested for 97 | /// - issuer: the issuer of the status requested certificate 98 | /// - Returns: `OCSPResponse.CertStatus` value 99 | /// - Throws: `OpenSSLError` on internal error 100 | public func certificateStatus(for: X509, issuer: X509) throws -> CertStatus { 101 | let certId: OpaquePointer = OCSP_cert_to_id(EVP_sha1(), `for`.x509, issuer.x509) 102 | defer { 103 | OCSP_CERTID_free(certId) 104 | } 105 | let basic = OCSP_response_get1_basic(ocsp) 106 | defer { 107 | OCSP_BASICRESP_free(basic) 108 | } 109 | 110 | var status: CInt = -1 111 | let res = OCSP_resp_find_status(basic, certId, &status, nil, nil, nil, nil) 112 | guard res == 1 else { 113 | return .requestedCertificateNotInResponse 114 | } 115 | 116 | guard let certStatus = CertStatus(rawValue: Int(status)) else { 117 | throw OpenSSLError(name: "Internal error extracting certificate status") 118 | } 119 | 120 | return certStatus 121 | } 122 | 123 | /// Attempts to retrieve the certificate that directly signed this response. 124 | /// 125 | /// - Returns: The signing certificate if it was included in the certs field of the response. 126 | /// - Throws: OpenSSL error when internal error error certificate parsing error. 127 | public func getSigner() throws -> X509? { 128 | let basic = OCSP_response_get1_basic(ocsp) 129 | defer { 130 | OCSP_BASICRESP_free(basic) 131 | } 132 | 133 | var x509Pointer: OpaquePointer? = X509_new() 134 | let res = OCSP_resp_get0_signer(basic, &x509Pointer, nil) 135 | 136 | guard res == 1 else { 137 | return nil 138 | } 139 | 140 | // Note: res == 1 should guarantee a valid x509Pointer 141 | guard let x509 = x509Pointer else { 142 | throw OpenSSLError(name: "Internal error getting the OCSP response signer") 143 | } 144 | return try X509(owning: x509) 145 | } 146 | 147 | /// Checks that the basic response message is correctly signed and that the signer certificate can be validated. 148 | /// For further info see https://www.openssl.org/docs/man1.1.0/man3/OCSP_resp_get0.html -> OCSP_basic_verify() 149 | /// 150 | /// - Parameters: 151 | /// - trustedStore: a collection of trusted certificates 152 | /// - options: flags for taking influence on the behavior of the function 153 | /// - validationTime: Optional custom time for certificate validation. If nil, uses current system time. 154 | /// - Returns: true if the OCSP response is correctly signed and the signer certificate can be validated 155 | /// - Throws: OpenSSLError on a fatal internal error 156 | public func basicVerifyWith( 157 | trustedStore: C, options: BasicVerifyOptions = [], validationTime: Date? = nil 158 | ) throws -> Bool where C.Element == X509 { 159 | let basic = OCSP_response_get1_basic(ocsp) 160 | defer { 161 | OCSP_BASICRESP_free(basic) 162 | } 163 | let x509TrustedStore = X509_STORE_new() 164 | defer { 165 | X509_STORE_free(x509TrustedStore) 166 | } 167 | 168 | // Set custom validation time if provided 169 | if let customTime = validationTime { 170 | let param = X509_STORE_get0_param(x509TrustedStore) 171 | let timeT = time_t(customTime.timeIntervalSince1970) 172 | X509_VERIFY_PARAM_set_time(param, timeT) 173 | } 174 | 175 | for trustedCert in trustedStore { 176 | guard X509_STORE_add_cert(x509TrustedStore, trustedCert.x509) == 1 else { 177 | throw OpenSSLError(name: "Error populating the X.509 trust store") 178 | } 179 | } 180 | 181 | let flags = options.rawValue 182 | let res = OCSP_basic_verify(basic, nil, x509TrustedStore, flags) 183 | if res == 1 { 184 | return true 185 | } else if res == 0 { 186 | return false 187 | } else { 188 | throw OpenSSLError(name: "Fatal error when trying to verify OCSP response") 189 | } 190 | } 191 | 192 | /// Flags for function `basicVerify()`. 193 | /// For usage info refer to https://www.openssl.org/docs/man1.1.0/man3/OCSP_resp_get0.html -> OCSP_basic_verify() 194 | public struct BasicVerifyOptions: OptionSet { 195 | public let rawValue: UInt 196 | 197 | public init(rawValue: UInt) { 198 | self.rawValue = rawValue 199 | } 200 | 201 | public static let noIntern = BasicVerifyOptions(rawValue: UInt(OCSP_NOINTERN)) 202 | public static let noSigs = BasicVerifyOptions(rawValue: UInt(OCSP_NOSIGS)) 203 | public static let noVerify = BasicVerifyOptions(rawValue: UInt(OCSP_NOVERIFY)) 204 | public static let trustOther = BasicVerifyOptions(rawValue: UInt(OCSP_TRUSTOTHER)) 205 | public static let noChain = BasicVerifyOptions(rawValue: UInt(OCSP_NOCHAIN)) 206 | public static let noChecks = BasicVerifyOptions(rawValue: UInt(OCSP_NOCHECKS)) 207 | public static let noExplicit = BasicVerifyOptions(rawValue: UInt(OCSP_NOEXPLICIT)) 208 | } 209 | } 210 | 211 | // swiftlint:disable:next no_extension_access_modifier 212 | private extension UnsafePointer where Pointee == ASN1_GENERALIZEDTIME { 213 | // When handling ASN1_TIME, we always assume the format MMM DD HH:MM:SS YYYY [GMT] 214 | private static let dateFormatterWithTimezone: DateFormatter = { 215 | let dateFormatter = DateFormatter() 216 | dateFormatter.dateFormat = "MMM dd HH:mm:ss yyyy ZZZ" 217 | dateFormatter.locale = Locale(identifier: "en_US") 218 | 219 | return dateFormatter 220 | }() 221 | 222 | func asFoundationDate() throws -> Date { 223 | let bio = BIO_new(BIO_s_mem()) 224 | defer { 225 | BIO_free(bio) 226 | } 227 | 228 | guard ASN1_TIME_print(bio, self) == 1 else { 229 | throw OpenSSLError(name: "Error converting OCSP response date") 230 | } 231 | 232 | let bufferSize: CInt = 128 233 | var buffer = [UInt8](repeating: 0x0, count: Int(bufferSize)) 234 | var data = Data() 235 | var readBytes: CInt = 0 236 | 237 | repeat { 238 | readBytes = BIO_read(bio, &buffer, bufferSize) 239 | if readBytes > 0 { 240 | data.append(contentsOf: buffer[0 ..< Int(readBytes)]) 241 | } 242 | } while readBytes > 0 243 | 244 | guard let string = String(data: data, encoding: .utf8) else { 245 | throw OpenSSLError(name: "Unable to validate ASN1 TIME string") 246 | } 247 | 248 | guard let date = Self.dateFormatterWithTimezone.date(from: string) else { 249 | throw OpenSSLError(name: "Error converting date from string") 250 | } 251 | 252 | return date 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: https://github.com/gematik/Fastlane-Plugin-CI-CD.git 3 | revision: b93979c294bbfda870a80470ab133938f628e1f6 4 | tag: 0.1.0 5 | specs: 6 | fastlane-plugin-ci_cd (0.1.0) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | CFPropertyList (3.0.7) 12 | base64 13 | nkf 14 | rexml 15 | activesupport (7.2.2.1) 16 | base64 17 | benchmark (>= 0.3) 18 | bigdecimal 19 | concurrent-ruby (~> 1.0, >= 1.3.1) 20 | connection_pool (>= 2.2.5) 21 | drb 22 | i18n (>= 1.6, < 2) 23 | logger (>= 1.4.2) 24 | minitest (>= 5.1) 25 | securerandom (>= 0.3) 26 | tzinfo (~> 2.0, >= 2.0.5) 27 | addressable (2.8.7) 28 | public_suffix (>= 2.0.2, < 7.0) 29 | algoliasearch (1.27.5) 30 | httpclient (~> 2.8, >= 2.8.3) 31 | json (>= 1.5.1) 32 | artifactory (3.0.17) 33 | atomos (0.1.3) 34 | aws-eventstream (1.3.0) 35 | aws-partitions (1.1023.0) 36 | aws-sdk-core (3.214.0) 37 | aws-eventstream (~> 1, >= 1.3.0) 38 | aws-partitions (~> 1, >= 1.992.0) 39 | aws-sigv4 (~> 1.9) 40 | jmespath (~> 1, >= 1.6.1) 41 | aws-sdk-kms (1.96.0) 42 | aws-sdk-core (~> 3, >= 3.210.0) 43 | aws-sigv4 (~> 1.5) 44 | aws-sdk-s3 (1.176.1) 45 | aws-sdk-core (~> 3, >= 3.210.0) 46 | aws-sdk-kms (~> 1) 47 | aws-sigv4 (~> 1.5) 48 | aws-sigv4 (1.10.1) 49 | aws-eventstream (~> 1, >= 1.0.2) 50 | babosa (1.0.4) 51 | base64 (0.2.0) 52 | benchmark (0.4.0) 53 | bigdecimal (3.1.8) 54 | claide (1.0.3) 55 | cocoapods (1.16.2) 56 | addressable (~> 2.8) 57 | claide (>= 1.0.2, < 2.0) 58 | cocoapods-core (= 1.16.2) 59 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 60 | cocoapods-downloader (>= 2.1, < 3.0) 61 | cocoapods-plugins (>= 1.0.0, < 2.0) 62 | cocoapods-search (>= 1.0.0, < 2.0) 63 | cocoapods-trunk (>= 1.6.0, < 2.0) 64 | cocoapods-try (>= 1.1.0, < 2.0) 65 | colored2 (~> 3.1) 66 | escape (~> 0.0.4) 67 | fourflusher (>= 2.3.0, < 3.0) 68 | gh_inspector (~> 1.0) 69 | molinillo (~> 0.8.0) 70 | nap (~> 1.0) 71 | ruby-macho (>= 2.3.0, < 3.0) 72 | xcodeproj (>= 1.27.0, < 2.0) 73 | cocoapods-core (1.16.2) 74 | activesupport (>= 5.0, < 8) 75 | addressable (~> 2.8) 76 | algoliasearch (~> 1.0) 77 | concurrent-ruby (~> 1.1) 78 | fuzzy_match (~> 2.0.4) 79 | nap (~> 1.0) 80 | netrc (~> 0.11) 81 | public_suffix (~> 4.0) 82 | typhoeus (~> 1.0) 83 | cocoapods-deintegrate (1.0.5) 84 | cocoapods-downloader (2.1) 85 | cocoapods-plugins (1.0.0) 86 | nap 87 | cocoapods-search (1.0.1) 88 | cocoapods-trunk (1.6.0) 89 | nap (>= 0.8, < 2.0) 90 | netrc (~> 0.11) 91 | cocoapods-try (1.2.0) 92 | colored (1.2) 93 | colored2 (3.1.2) 94 | commander (4.6.0) 95 | highline (~> 2.0.0) 96 | concurrent-ruby (1.3.4) 97 | connection_pool (2.4.1) 98 | declarative (0.0.20) 99 | digest-crc (0.6.5) 100 | rake (>= 12.0.0, < 14.0.0) 101 | domain_name (0.6.20240107) 102 | dotenv (2.8.1) 103 | drb (2.2.1) 104 | emoji_regex (3.2.3) 105 | escape (0.0.4) 106 | ethon (0.16.0) 107 | ffi (>= 1.15.0) 108 | excon (0.112.0) 109 | faraday (1.10.4) 110 | faraday-em_http (~> 1.0) 111 | faraday-em_synchrony (~> 1.0) 112 | faraday-excon (~> 1.1) 113 | faraday-httpclient (~> 1.0) 114 | faraday-multipart (~> 1.0) 115 | faraday-net_http (~> 1.0) 116 | faraday-net_http_persistent (~> 1.0) 117 | faraday-patron (~> 1.0) 118 | faraday-rack (~> 1.0) 119 | faraday-retry (~> 1.0) 120 | ruby2_keywords (>= 0.0.4) 121 | faraday-cookie_jar (0.0.7) 122 | faraday (>= 0.8.0) 123 | http-cookie (~> 1.0.0) 124 | faraday-em_http (1.0.0) 125 | faraday-em_synchrony (1.0.0) 126 | faraday-excon (1.1.0) 127 | faraday-httpclient (1.0.1) 128 | faraday-multipart (1.0.4) 129 | multipart-post (~> 2) 130 | faraday-net_http (1.0.2) 131 | faraday-net_http_persistent (1.2.0) 132 | faraday-patron (1.0.0) 133 | faraday-rack (1.0.0) 134 | faraday-retry (1.0.3) 135 | faraday_middleware (1.2.1) 136 | faraday (~> 1.0) 137 | fastimage (2.3.1) 138 | fastlane (2.226.0) 139 | CFPropertyList (>= 2.3, < 4.0.0) 140 | addressable (>= 2.8, < 3.0.0) 141 | artifactory (~> 3.0) 142 | aws-sdk-s3 (~> 1.0) 143 | babosa (>= 1.0.3, < 2.0.0) 144 | bundler (>= 1.12.0, < 3.0.0) 145 | colored (~> 1.2) 146 | commander (~> 4.6) 147 | dotenv (>= 2.1.1, < 3.0.0) 148 | emoji_regex (>= 0.1, < 4.0) 149 | excon (>= 0.71.0, < 1.0.0) 150 | faraday (~> 1.0) 151 | faraday-cookie_jar (~> 0.0.6) 152 | faraday_middleware (~> 1.0) 153 | fastimage (>= 2.1.0, < 3.0.0) 154 | fastlane-sirp (>= 1.0.0) 155 | gh_inspector (>= 1.1.2, < 2.0.0) 156 | google-apis-androidpublisher_v3 (~> 0.3) 157 | google-apis-playcustomapp_v1 (~> 0.1) 158 | google-cloud-env (>= 1.6.0, < 2.0.0) 159 | google-cloud-storage (~> 1.31) 160 | highline (~> 2.0) 161 | http-cookie (~> 1.0.5) 162 | json (< 3.0.0) 163 | jwt (>= 2.1.0, < 3) 164 | mini_magick (>= 4.9.4, < 5.0.0) 165 | multipart-post (>= 2.0.0, < 3.0.0) 166 | naturally (~> 2.2) 167 | optparse (>= 0.1.1, < 1.0.0) 168 | plist (>= 3.1.0, < 4.0.0) 169 | rubyzip (>= 2.0.0, < 3.0.0) 170 | security (= 0.1.5) 171 | simctl (~> 1.6.3) 172 | terminal-notifier (>= 2.0.0, < 3.0.0) 173 | terminal-table (~> 3) 174 | tty-screen (>= 0.6.3, < 1.0.0) 175 | tty-spinner (>= 0.8.0, < 1.0.0) 176 | word_wrap (~> 1.0.0) 177 | xcodeproj (>= 1.13.0, < 2.0.0) 178 | xcpretty (~> 0.4.0) 179 | xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) 180 | fastlane-sirp (1.0.0) 181 | sysrandom (~> 1.0) 182 | ffi (1.17.0) 183 | fourflusher (2.3.1) 184 | fuzzy_match (2.0.4) 185 | gh_inspector (1.1.3) 186 | google-apis-androidpublisher_v3 (0.54.0) 187 | google-apis-core (>= 0.11.0, < 2.a) 188 | google-apis-core (0.11.3) 189 | addressable (~> 2.5, >= 2.5.1) 190 | googleauth (>= 0.16.2, < 2.a) 191 | httpclient (>= 2.8.1, < 3.a) 192 | mini_mime (~> 1.0) 193 | representable (~> 3.0) 194 | retriable (>= 2.0, < 4.a) 195 | rexml 196 | google-apis-iamcredentials_v1 (0.17.0) 197 | google-apis-core (>= 0.11.0, < 2.a) 198 | google-apis-playcustomapp_v1 (0.13.0) 199 | google-apis-core (>= 0.11.0, < 2.a) 200 | google-apis-storage_v1 (0.31.0) 201 | google-apis-core (>= 0.11.0, < 2.a) 202 | google-cloud-core (1.7.1) 203 | google-cloud-env (>= 1.0, < 3.a) 204 | google-cloud-errors (~> 1.0) 205 | google-cloud-env (1.6.0) 206 | faraday (>= 0.17.3, < 3.0) 207 | google-cloud-errors (1.4.0) 208 | google-cloud-storage (1.47.0) 209 | addressable (~> 2.8) 210 | digest-crc (~> 0.4) 211 | google-apis-iamcredentials_v1 (~> 0.1) 212 | google-apis-storage_v1 (~> 0.31.0) 213 | google-cloud-core (~> 1.6) 214 | googleauth (>= 0.16.2, < 2.a) 215 | mini_mime (~> 1.0) 216 | googleauth (1.8.1) 217 | faraday (>= 0.17.3, < 3.a) 218 | jwt (>= 1.4, < 3.0) 219 | multi_json (~> 1.11) 220 | os (>= 0.9, < 2.0) 221 | signet (>= 0.16, < 2.a) 222 | highline (2.0.3) 223 | http-cookie (1.0.8) 224 | domain_name (~> 0.5) 225 | httpclient (2.8.3) 226 | i18n (1.14.6) 227 | concurrent-ruby (~> 1.0) 228 | jazzy (0.15.3) 229 | cocoapods (~> 1.5) 230 | mustache (~> 1.1) 231 | open4 (~> 1.3) 232 | redcarpet (~> 3.4) 233 | rexml (>= 3.2.7, < 4.0) 234 | rouge (>= 2.0.6, < 5.0) 235 | sassc (~> 2.1) 236 | sqlite3 (~> 1.3) 237 | xcinvoke (~> 0.3.0) 238 | jmespath (1.6.2) 239 | json (2.9.0) 240 | jwt (2.9.3) 241 | base64 242 | liferaft (0.0.6) 243 | logger (1.6.3) 244 | mini_magick (4.13.2) 245 | mini_mime (1.1.5) 246 | mini_portile2 (2.8.8) 247 | minitest (5.25.4) 248 | molinillo (0.8.0) 249 | multi_json (1.15.0) 250 | multipart-post (2.4.1) 251 | mustache (1.1.1) 252 | nanaimo (0.4.0) 253 | nap (1.1.0) 254 | naturally (2.2.1) 255 | netrc (0.11.0) 256 | nkf (0.2.0) 257 | open4 (1.3.4) 258 | optparse (0.6.0) 259 | os (1.1.4) 260 | plist (3.7.1) 261 | public_suffix (4.0.7) 262 | rake (13.2.1) 263 | redcarpet (3.6.0) 264 | representable (3.2.0) 265 | declarative (< 0.1.0) 266 | trailblazer-option (>= 0.1.1, < 0.2.0) 267 | uber (< 0.2.0) 268 | retriable (3.1.2) 269 | rexml (3.4.0) 270 | rouge (3.28.0) 271 | ruby-macho (2.5.1) 272 | ruby2_keywords (0.0.5) 273 | rubyzip (2.3.2) 274 | sassc (2.4.0) 275 | ffi (~> 1.9) 276 | securerandom (0.4.1) 277 | security (0.1.5) 278 | signet (0.19.0) 279 | addressable (~> 2.8) 280 | faraday (>= 0.17.5, < 3.a) 281 | jwt (>= 1.5, < 3.0) 282 | multi_json (~> 1.10) 283 | simctl (1.6.10) 284 | CFPropertyList 285 | naturally 286 | sqlite3 (1.7.3) 287 | mini_portile2 (~> 2.8.0) 288 | sysrandom (1.0.5) 289 | terminal-notifier (2.0.0) 290 | terminal-table (3.0.2) 291 | unicode-display_width (>= 1.1.1, < 3) 292 | trailblazer-option (0.1.2) 293 | tty-cursor (0.7.1) 294 | tty-screen (0.8.2) 295 | tty-spinner (0.9.3) 296 | tty-cursor (~> 0.7) 297 | typhoeus (1.4.1) 298 | ethon (>= 0.9.0) 299 | tzinfo (2.0.6) 300 | concurrent-ruby (~> 1.0) 301 | uber (0.1.0) 302 | unicode-display_width (2.6.0) 303 | word_wrap (1.0.0) 304 | xcinvoke (0.3.0) 305 | liferaft (~> 0.0.6) 306 | xcode-install (2.6.8) 307 | claide (>= 0.9.1, < 1.1.0) 308 | fastlane (>= 2.1.0, < 3.0.0) 309 | xcodeproj (1.27.0) 310 | CFPropertyList (>= 2.3.3, < 4.0) 311 | atomos (~> 0.1.3) 312 | claide (>= 1.0.2, < 2.0) 313 | colored2 (~> 3.1) 314 | nanaimo (~> 0.4.0) 315 | rexml (>= 3.3.6, < 4.0) 316 | xcpretty (0.4.0) 317 | rouge (~> 3.28.0) 318 | xcpretty-travis-formatter (1.0.1) 319 | xcpretty (~> 0.2, >= 0.0.7) 320 | 321 | PLATFORMS 322 | ruby 323 | 324 | DEPENDENCIES 325 | fastlane (~> 2.187) 326 | fastlane-plugin-ci_cd! 327 | jazzy (~> 0.14) 328 | xcode-install (~> 2.6.6) 329 | 330 | RUBY VERSION 331 | ruby 3.1.2p20 332 | 333 | BUNDLED WITH 334 | 2.3.20 335 | --------------------------------------------------------------------------------