IOS (Command Line)

From Crypto++ Wiki
Jump to: navigation, search

This page will provide instructions for cross-compiling Crypto++ on the command line for iOS. There are four steps to building Crypto++ for iOS, and the process will create an iOS version of cryptest.exe, a dynamic library, and a static library. You will only use the static library (libcryptopp.a) in your Xcode projects, unless you have a jailbroken device. If you have a jailbroken device, then you can use scp (or other program) to transfer cryptest.exe and the test vectors to the device. Once on the device, you can then execute the tests from a mobile terminal.

Cross-compiling for iOS requires Xcode, so you should ensure its installed before you begin. The instructions below are for Xcode 4 and above, which means Xcode will be located at /Applications/Xcode.app. You will have to modify the setenv-ios.sh if you want to use Xcode 3 and earlier (which is located in /Developer/Xcode.app).

The examples below demonstrate a device build. For example, you will see -arch armv7 and a path that includes /Applications/Xcode.app/.../Platforms/iPhoneOS.platform. If you are building for the simulator, you should see -arch i386 and a path that includes /Applications/Xcode.app/.../Platforms/iPhoneSimulator.platform. The changes are made via setenv-ios.sh discussed below.

If you don't want to build anything yourself, then take a look at cryptopp-5.6.2-ios. Its a prebuilt version of Crypto++ 5.6.2 (revision 541) for ARMv7, ARMv7s, ARM64 and i386 simulator hosted on Github. The ZIP includes one common set of headers in include, and one fat libcryptopp.a in lib. The library was built with the iOS 7.0 SDKs. Instructions for using it are discussed under Xcode Project below.

A wiki page is available for compiling Crypto++ under Xcode at iOS (Xcode).

Set the Environment

Cryptopp-ios-100.png
Before you begin you must set the cross-compilation environment. Setting the environment will do two things. First, it will ensure the iOS toolchain is on-path. Second, it will ensure some environmental variables are set so the makefile does not erroneously configure itself for the host (i.e., Linux or Mac OS X) instead of the target (i.e., iPad or iPhone). For example, the script will set IS_IOS and IS_CROSS_COMPILE to ensure IS_X86 and IS_DARWIN are set to 0 in the makefile.

Run setenv-ios.sh to set the environment. If building for a device, supply device as an argument. If building for the simulator, supply simulator as an argument. Note well (N.B.): be sure to add the leading '.' (dot) when executing the command. The leading dot ensures the changes are applied to the current shell and child shells.

If you are configuring for a device build, then you should see output similar to below:

$ . ./setenv-ios.sh device
Configuring for Device (ARMv7)
XCODE_SDK: iPhoneOS6.1.sdk
XCODE_DEVELOPER: /Applications/Xcode.app/Contents/Developer
XCODE_TOOLCHAIN: /Applications/Xcode.app/Contents/Developer/usr/bin
XCODE_DEVELOPER_TOP: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer
IOS_ARCH: armv7
IOS_TOOLCHAIN: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/
IOS_SYSROOT: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk

If you are building for the simulator, then run the script with the simulator argument:

$ . ./setenv-ios.sh simulator
Configuring for Simulator (i386)
XCODE_SDK: iPhoneSimulator6.1.sdk
XCODE_DEVELOPER: /Applications/Xcode.app/Contents/Developer
XCODE_TOOLCHAIN: /Applications/Xcode.app/Contents/Developer/usr/bin
XCODE_DEVELOPER_TOP: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer
IOS_ARCH: i386
IOS_TOOLCHAIN: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/
IOS_SYSROOT: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator6.1.sdk

You can also specify different architectures. For example, setenv-ios.sh armv7s will configure for A6 processors and the armv7s instruction set. If you want to build a fat binary (for example, both armv7 and armv7s), then its easier to open GNUMakefile and change it by hand (search for IOS_ARCH after applying the patch).

Be sure to correct any errors before proceeding.

To verify the proper IOS_* and XCODE_* variables have been exported, execute printenv:

$ printenv| egrep "(IOS|XCODE)" | sort
IOS_ARCH=armv7
IOS_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk
IOS_TOOLCHAIN=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/
IS_IOS=1
XCODE_TOOLCHAIN=/Applications/Xcode.app/Contents/Developer/usr/bin

To verify the path has been set, simply echo it from the command line (PATH used IOS_TOOLCHAIN and XCODE_TOOLCHAIN):

$ echo $PATH
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/:
/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:
/usr/local/bin:/opt/X11/bin

Finally, setenv-ios.sh is based upon a script written by Tim Hudson for the OpenSSL project. The script was later used as a blueprint by Steve Marquess and Jeffrey Walton for other projects, including Botan, Crypto++ and Cryptlib.

Patch the Makefile

Cryptopp-ios-102.png
The makefile has to be patched to accommodate the iOS target, and it does so in two steps. First, it sets some variables that the makefile incorrectly uses for the host (e.g., Linux or Mac OS X) instead of the target (e.g., Android phone or tablet). Second, it adds a target specific configuration.

To apply the patch, unzip it into the Crypto++ directory and then execute the following from the command line.

$ cd cryptopp
$ unzip -a GNUmakefile-ios.patch.zip
$ patch -p0 -i GNUmakefile-ios.patch

The patched makefile picks up IS_CROSS_COMPILE set by setenv-ios.sh and executes the following code to unset host related variables:

# Set in the environment
ifeq ($(IS_CROSS_COMPILE),1)
  ISX86=0
  IS_LINUX=0
  IS_MINGW=0
  IS_DARWIN=0
  UNAME=CrossCompile
endif

The makefile will use three variables set by setenv-ios.sh during the cross-compilation. The first is IS_IOS, the second is IOS_ARCH, and the third IOS_SYSROOT. The makefile will execute the following code due to the script.

ifeq ($(IS_IOS),1)
  CXX = clang++

  CXXFLAGS = -DNDEBUG -g -Os -pipe -fPIC -DCRYPTOPP_DISABLE_ASM
  CXXFLAGS += -arch $(IOS_ARCH) -isysroot $(IOS_SYSROOT)
  CXXFLAGS += -stdlib=libc++

  AR = libtool
  ARFLAGS = -static -o
  LDFLAGS += -flat_namespace
endif

If required, you can add a minimum deployment target ot CXXFLAGS. For example, -mios-version-min=6.0.

The patch also adds a target for dylib, and the dylib recipe builds libcryptopp.dylib (libcryptopp.so does not use CXXFLAGS, and there was no easy way to rename libcryptopp.so).

libcryptopp.dylib: $(LIBOBJS)
	$(CXX) $(CXXFLAGS) -dynamiclib -o $@ $(LDFLAGS) $(LIBOBJS)

Its important to use clang++ (or c++ if its using clang++). Using llvm-gcc will result in a hang when compiling Integer.cpp. See Need Help with Memory Exhaustion During Compile for details.

If you want to build a multi-arch or fat binary, then change -arch $(IOS_ARCH) to -arch armv6 -arch armv7 -arch armv7s (Apple tools allow you to specify all of them at once).

Modify Config.h

config.h needs to be modified as follows. You can apply the patch or manually patch the config file. To apply the patch perform the following:

$ cd cryptopp
$ unzip -a config.h.patch.zip
$ patch -p0 -i config.h.patch

To patch it manually, first add the following to the top of config.h.

// For TARGET_OS_IPHONE and TARGET_IPHONE_SIMULATOR
#if defined(__APPLE__)
# include "TargetConditionals.h"
#endif

Second, around line 250, disable X86 and X64 assembly. Add the code before the config file uses the CRYPTOPP_DISABLE_* macros.

// For cross-compiles, just ignore host settings that bleed through for Xcode/iOS
// We will accidentally catch some device builds that use x86 on Android.
#if defined(__ANDROID__) || defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR)
# define CRYPTOPP_DISABLE_ASM 1
# define CRYPTOPP_DISABLE_SSE2 1
# define CRYPTOPP_DISABLE_SSE3 1
#endif

Finally, around line 350, ensure CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS is undefined. Unaligned data access is a problem on older ARM devices, and will result in a SIGBUS or EXCEPTION_DATATYPE_MISALIGNMENT (newer ARM CPUs behave like x86/x64). Also set CRYPTOPP_BOOL_X86 and CRYPTOPP_BOOL_X64 to 0 since they are host (and not target) settings.

Setting CRYPTOPP_BOOL_X86 and CRYPTOPP_BOOL_X64 to 0 casts a large net but ensures all architectures use the same code path. Otherwise, ARM (iPhones and iPads) will use one set of source code, and i386 (Simulator) will use a second set of source code.

// For cross-compiles, just ignore host settings that bleed through for Xcode/iOS
// We will accidentally catch some device builds that use x86 on Android.
#if defined(__ANDROID__) || defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR)
# undef CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS
# undef CRYPTOPP_BOOL_X86
# define CRYPTOPP_BOOL_X86 0
# undef CRYPTOPP_BOOL_X64
# define CRYPTOPP_BOOL_X64 0
#endif

Build the Library

Cryptopp-ios-101.png
To build the library, execute make static dylib cryptest.exe. If the environment is set and the makefile is patched, then you will see output similar to below.
$ make static cryptest.exe
clang++ -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -fPIC -arch armv7 --sysroot=/Applications/
Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk
-Wno-tautological-compare -Wno-unused-value -c 3way.cpp
clang++ -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -fPIC -arch armv7 --sysroot=/Applications/
Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk
-Wno-tautological-compare -Wno-unused-value -c adler32.cpp
clang++ -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -fPIC -arch armv7 --sysroot=/Applications/
Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk
-Wno-tautological-compare -Wno-unused-value -c algebra.cpp
...

clang++ -o cryptest.exe -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -fPIC -arch armv7
--sysroot=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/
SDKs/iPhoneOS6.1.sdk -Wno-tautological-compare -Wno-unused-value bench.o bench2.o test.o
validat1.o validat2.o validat3.o adhoc.o datatest.o regtest.o fipsalgt.o dlltest.o ./libcryptopp.a

You can verify the library was built for the correct architecture with the following.

$ xcrun -sdk iphoneos lipo -info cryptest.exe
Non-fat file: cryptest.exe is architecture: armv7
$
$ xcrun -sdk iphoneos lipo -info libcryptopp.dylib 
Non-fat file: libcryptopp.dylib is architecture: armv7

If you want to reduce the final executable size of a program when using static linking, then you can dead strip by adding -gfull to CXXFLAGS and -dead_strip to LDFLAGS. See Apple's Tuning for Performance and Responsiveness for details.

Execute the Program

If you have a jailbroken device then you can move the cryptest.exe executable and test vectors to the device to test the library. The procedures in this section use a jailbroken iPad 1 with an A4 processor running iOS 5.1.1. Jailbreaking is well beyond this wiki page, but useful information can be found at Jailbreak QA run by Saurik.

Before transferring cryptest.exe to the device, you must sign the program or use ldid. ldid is spotty at times, so its usually easier to sign the binary.

$ codesign -fs "Johnny Developer" cryptest.exe

Once the program is signed, transfer cryptest.exe, TestVectors and TestData to the device. Below, Fugu (a SFTP, SCP and SSH client) shows the local file system (MacBook Pro) on the left, and the remote file system (iPad 1) on the right.

Cryptopp-ios-103.png

With the program and test vectors on the device, SSH into the deivce and exectue the test program.

Cryptopp-ios-105.png
riemann::cryptopp$ ssh mobile@192.168.1.13
mobile@192.168.1.13's password: 
Jeffrey-Waltons-iPad:~ mobile$ cd cryptopp/
Jeffrey-Waltons-iPad:~/cryptopp mobile$ ls
TestData/  TestVectors/  cryptest.exe*
Jeffrey-Waltons-iPad:~/cryptopp mobile$ ./cryptest.exe v
Using seed: 1374910436

Testing Settings...

passed:  Your machine is little endian.
passed:  CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS is not defined. Will restrict to aligned data access.
passed:  sizeof(byte) == 1
passed:  sizeof(word16) == 2
passed:  sizeof(word32) == 4
passed:  sizeof(word64) == 8
passed:  sizeof(hword) == 2, sizeof(word) == 4, sizeof(dword) == 8
...

If all goes well, the tail of cryptest.exe v will display "All tests passed!" as shown below.

Cryptopp-ios-106.png

Once the library has been verified on a device, copy the header files (*.h), the dynamic library (libcryptopp.dylib), and the static library (libcryptopp.a) to a directory for use in other projects. Or, you can run sudo make install PREFIX=/usr/local/ios-armv7 since the libraries are for the ARMv7 platform.

Xcode Project

This section provides quick and dirty instructions for using the library in iPhone, iPad, and Simulator projects. If you want to forgo building the library, then fetch the prebuilt library and headers from Github at cryptopp-5.6.2-ios and jump ahead to Step 6.

First, combine all your libcryptopp.a files into a single, Mach-O (fat) binary. The armv7/libcryptopp.a (and others below) assumes you saved the library between builds among architectures.

$ lipo create armv7/libcryptopp.a armv7s/libcryptopp.a arm64/libcryptopp.a i386/libcryptopp.a -output libcryptopp.a

Second, verify all the architectures are present:

$ xcrun -sdk iphoneos lipo -info libcryptopp.a
Architectures in the fat file: libcryptopp.a are: armv7 armv7s arm64 i386

Third, remove non-archive files. Even though you add libcryptopp.a to the project, Xcode will still try and link against the shared objects. Apparently, Apple developers and employees did not get the memo about static linking only:

$ rm *.so *.dylib *.exe

Fourth, install the library:

sudo make install PREFIX=/usr/local/cryptopp

Fifth, fix the permissions on the files:

$ sudo chmod -R a+r /usr/local/cryptopp/*
$ sudo chmod -R a+x /usr/local/cryptopp/lib/*

Sixth, create an Xcode project. Add /usr/local/cryptopp/include as a user header search path. Also add /usr/local/cryptopp/lib as a library path. If you fetched a prebuilt library from Github, then change the path accordingly.


Xcode-cryptopp-2000.png


Under this path scheme, you will include files as:

#include "cryptopp/cryptlib.h"

Seventh, add libcryptopp.a to the project per the instructions at How to “add existing frameworks” in Xcode 4?.


Xcode-cryptopp-2001.png


Ensure the following Build Settings are present as shown in the image below. The library is built to use libc++ because that is Xcode's default.

  • C++ Language Dialect = C++11
  • C++ Standard Library = libc++ (not libstdc++)
  • Compile Source As = Objective-C++ (invokes clang++)


Xcode-cryptopp-1999.png


If the above are not set, you will receive errors about missing symbols even though it appears the symbols are present. For example, you will receive an error that std::__1::string, std::__1::vector, std::__1::char_traits<char>, or std::__1::allocator<char> cannot be found.

If you really need libstdc++ (due to other libraries), then you will need to rebuild the Crypto++ library after changing CXXFLAGS and change your Xcode settings.

Finally, use Crypto++ as usual.

Xcode-cryptopp-2002.png

Downloads

setenv-ios.sh.zip - Script to set the environment for iOS cross-compiles

cryptopp-mobile.zip - Modified Crypto++ source files to support cross compiling under Android, iOS and Windows. Unpack them directly over the top of the original files. The modified files are GNUmakefile, algparam.h, config.h, iterhash.h, wait.h and wait.cpp.

cryptopp-5.6.2-ios-6.1 - Prebuilt version of Crypto++ 5.6.2 (revision 541) for ARMv7, ARMv7s and i386 simulator hosted on Github. The ZIP includes one common set of headers in include, and one fat libcryptopp.a in lib. The library was built with the iOS 6.1 SDKs.

cryptopp-5.6.2-ios-7.0 - Prebuilt version of Crypto++ 5.6.2 (revision 541) for ARMv7, ARMv7s, ARM64 and i386 simulator hosted on Github. The ZIP includes one common set of headers in include, and one fat libcryptopp.a in lib. The library was built with the iOS 7.0 SDKs.