OpenSSL complexity starts with its version string. Apple, Carthage, and some dependency analysis tools have different opinions about it. Here is how we dealt with them and submitted iOS app to the App Store.
So, we decided to update OpenSSL in iOS app #
Themis provides easy-to-use cryptography for multiple languages and platforms. We implement it on top of existing cryptography engines , such as OpenSSL or BoringSSL, which Themis uses as a source of the cryptographic primitives.
Itâs a good practice to keep dependencies of your apps up-to-date, especially if they are responsible for security. On Linux systems, the OS vendors are typically responsible for the updates, but on Apple platforms, the maintenance is up to library authors. That is us.
We needed to update OpenSSL to mitigate a recently found CVE, that didnât affect Themis directly. The current (sad) state of OpenSSL packaging for Apple platforms is a topic for another day .
Long story short: we gave a shot at packaging OpenSSL ourselves, and we were in for some surprises.
OpenSSL versioning scheme #
The canonical OpenSSL version consists of four components, with the patch version indicated by a letter: 1.0.2u , 1.1.1g .
This version can be retrieved with
OpenSSL_version()
call and the version string is stored in OpenSSL binary:
$ strings openssl.framework/openssl | grep "OpenSSL 1.0.2*"
OpenSSL 1.0.2u 20 Dec 2019
If OpenSSL is built as a framework, it has an Info.plist
file with metadata like supported platforms and package version.
Everybody uses semver nowadays #
Semantic versioning (aka semver ) is an increasingly popular approach to versioning software. It is simple, easy to understand, and handy for API consumers. Semver makes it trivial to gauge the scope of changes between releases, so you are more comfortable with making upgrade decisions. The maintainers have to have some discipline with versioning, but itâs not that hard. Many new libraries and ecosystems are now using semver by default.
But what about the older libraries?
OpenSSL has been in development long before semver has been formalized. The OpenSSL team is not very keen on changing their versioning scheme that has served them well over the years.
Unfortunately, sometimes versioning comes into conflict with other requirements.
OpenSSL version vs Apple semver #
Apple has their own strict rules for version values (see CFBundleShortVersionString definition and this technical note ). The version must be semver-compliant: no more than three numeric components and no letters allowed.
If an app has letters in its version stringâor inside version strings of any of its dependenciesâthe upload to App Store or TestFlight will fail with the following error:
ERROR ITMS-90060: This bundle is invalid.
The value for key CFBundleShortVersionString '1.0.2u' in the
Info.plist file at '${bundlePath}' must be a period-separated list
of at most three non-negative integers. Please find more information
about CFBundleShortVersionString at ...
The catch here is that nothing else warns you about this error.
Developers can successfully work on the app for months, building, signing, and testing it on devices, only to discover the problem at the final stages, ready to submit their app to the App Store.
We work with companies on demanding markets. Read how Acra database security suite protects data in critical infrastructure.
OpenSSL version vs Carthage semver #
Carthage
âa popular package manager for Apple developmentâalso requires semver-compatible versions for its packages. This version is typically the same as in the
Info.plist
file, but it can be different.
The Carthage version must have at most three numeric components, no letters allowed.
Themis is accessible via Carthage, and we have to specify the minimum OpenSSL version required.
The workaround we have arrived to is to renumber OpenSSL versions like this:
OpenSSL canonical version | Numeric version |
---|---|
1.0.2 | 1.0.200 |
1.0.2a | 1.0.201 |
1.0.2u | 1.0.221 |
1.1.1 | 1.1.100 |
1.1.1a | 1.1.101 |
1.1.1g | 1.1.107 |
The rule is simple: multiply the minor version by 100 then add alphabet order of the letter version. For example, âuâ is the 21st letter of the English alphabet, so 1.0.2u translates into 1.0.221.
OpenSSL version vs dependency analysers #
As if Apple requirements and Carthage requirements were not enough, we found out that the OpenSSL version from
Info.plist
is meaningful to certain software composition analysis tools (aka SCA, aka dependency analysers). SCA tools are often used to detect and track open source dependencies and notify developers about vulnerabilities in them.
The analysers may use
Info.plist
as a cheap way to find out the OpenSSL version, instead of looking for the
actual
OpenSSL version inside the binary.
Obviously, the analyser does not expect the minor version to be in hundreds (like 1.0.221), so to be on the safe side, it treats OpenSSL as potentially vulnerable. Such false positives are frustrating to deal with for app developers, since the warnings may be caused by transitive dependencies which the developers cannot control directly.
Happiness rating across different tools regarding OpenSSL version:
canonical version("1.0.2u") | numeric version("1.0.221") | |
---|---|---|
real OpenSSL version | đș | â ïž |
Apple (submit to App Store) | â ïž | đș |
Carthage (use in Cartfile) | â ïž | đș |
Dependency analysers (report vulnerabilities) | đș | â ïž |
Reconciling OpenSSL and App Store #
Update: another solution that worked for us is a static linkage between Themis and OpenSSL. Thus, OpenSSL Carthage package has Info.plist with canonical version, but due to static linkage, Info.plist doesn’t become a part of the application bundle, and doesn’t trigger Apple checks.
No size fits all, so the best workaround we came up with for this situation is to leave the original OpenSSL version in Info.plist, use the translated version for Carthage packages and make sure that the pre-release scripts include a step to translate the canonical version into numeric one, acceptable for App Store.
For example, you can patch OpenSSL version in
Info.plist
file like this:
$ plutil -replace CFBundleShortVersionString -string 1.0.221 Info.plist
This patching can occur on CI as an extra âscript stepâ after fetching the repository and before building an application and uploading to the App Store / TestFlight. If you know a better solution to this OpenSSL versioning problem, ping us on Twitter or drop us an email .
OpenSSL_version() function call | 1.0.2u | Runtime inspection returns true version |
Info.plist in OpenSSL.framework | 1.0.2u | Make dependency analysers happy |
Cartfiles of apps and libraries that use OpenSSL | 1.0.221 | Make Carthage happy |
Info.plist in apps submitted to the App Store | 1.0.221 | Make Apple happy |
No OpenSSL version fits all. Using canonical version during development and patching it to numeric before App Store submission seems to be a working solution.
The approach of “patching” OpenSSL version on CI raises obvious security questions:
The dependency analyser checks only
Info.plist
. Do you trust OpenSSL maintainers to use the correct version?Developers are typically able to modify CI configuration. Now consider the possibility of an insider threat, tricking you into using a vulnerable dependency.
What is a healthy, secure workflow for libraries that do not comply with semver?
Discussing these questions is out of the scope of this post and falls into the âsecurity vs real-world developmentâ territory.
But I need cryptography, what shall I do?
If youâre a developer and youâre dealing with cryptography for your app, consider using high-level cryptographic libraries like Themis instead of OpenSSL. No need to struggle with OpenSSL if your goal is to protect usersâ data.
We believe in âboring cryptoâ: accessible, strong, easy-to-use and hard-to-misuse. Check out our open-source cryptographic tools , and do not hesitate to make another step and drop us a line for any cryptographic assistance .