Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 1 | # Tink for Obj-C HOW-TO |
| 2 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 3 | This document contains instructions and Obj-C code snippets for common tasks in |
| 4 | [Tink](https://github.com/google/tink). |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 5 | |
0xflotus | 5bcaf8b | 2020-04-30 17:08:14 +0200 | [diff] [blame] | 6 | ## Setup instructions |
Haris Andrianakis | 81cdb2a | 2018-07-10 14:36:08 -0700 | [diff] [blame] | 7 | |
| 8 | Tink is released as a [Cocoapod](https://cocoapods.org/). It can be installed by |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 9 | using the pod command as described below, which is the recommended way to use |
| 10 | Tink. |
Haris Andrianakis | 81cdb2a | 2018-07-10 14:36:08 -0700 | [diff] [blame] | 11 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 12 | We also provide step-by-step instructions on building and using Tink from |
| 13 | source. |
Haris Andrianakis | e3521ea | 2018-07-12 17:21:12 -0700 | [diff] [blame] | 14 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 15 | #### Supported platforms |
Haris Andrianakis | 81cdb2a | 2018-07-10 14:36:08 -0700 | [diff] [blame] | 16 | |
| 17 | * iOS 9.0 or newer |
| 18 | * Xcode 9.2 or newer |
| 19 | |
| 20 | ### Installing via Cocoapods |
| 21 | |
| 22 | 1. Change into the directory that contains your Xcode project. |
| 23 | |
| 24 | ```sh |
| 25 | cd /path/to/your/Xcode project/ |
| 26 | ``` |
| 27 | |
| 28 | 2. Initialize Cocoapods. |
| 29 | |
| 30 | ```sh |
| 31 | pod init |
| 32 | ``` |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 33 | |
Haris Andrianakis | 81cdb2a | 2018-07-10 14:36:08 -0700 | [diff] [blame] | 34 | This command creates a file called Podfile. |
| 35 | |
| 36 | 3. Edit the Podfile. |
| 37 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 38 | For the current stable release, add the following line: |
Haris Andrianakis | 81cdb2a | 2018-07-10 14:36:08 -0700 | [diff] [blame] | 39 | |
| 40 | ``` |
| 41 | pod 'Tink' |
| 42 | ``` |
| 43 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 44 | For a particular version, use a line like the following instead: |
Haris Andrianakis | 81cdb2a | 2018-07-10 14:36:08 -0700 | [diff] [blame] | 45 | |
| 46 | ``` |
Haris Andrianakis | b3f143a | 2018-07-12 16:28:50 -0700 | [diff] [blame] | 47 | pod 'Tink', '1.2.0-rc2' |
Haris Andrianakis | 81cdb2a | 2018-07-10 14:36:08 -0700 | [diff] [blame] | 48 | ``` |
| 49 | |
Haris Andrianakis | b3f143a | 2018-07-12 16:28:50 -0700 | [diff] [blame] | 50 | Note: Replace 1.2.0-rc2 with the pre-release version you want to install. |
Haris Andrianakis | 81cdb2a | 2018-07-10 14:36:08 -0700 | [diff] [blame] | 51 | |
| 52 | 4. Install the pod. |
| 53 | |
| 54 | ```sh |
| 55 | $ pod install |
| 56 | ``` |
| 57 | |
| 58 | 5. Open the newly generated .xcworkspace and start using Tink. |
| 59 | |
| 60 | You can import the umbrella header: |
| 61 | |
| 62 | ```objc |
| 63 | #import "Tink/Tink.h" |
| 64 | ``` |
| 65 | |
| 66 | Or individual headers: |
| 67 | |
| 68 | ```objc |
| 69 | #import "Tink/TINKAeadConfig.h" |
| 70 | #import "Tink/TINKAeadKeyTemplate.h" |
| 71 | #import "Tink/TINKAead.h" |
| 72 | ``` |
| 73 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 74 | ### Installing from source |
Haris Andrianakis | e3521ea | 2018-07-12 17:21:12 -0700 | [diff] [blame] | 75 | |
| 76 | #### Prerequisites |
| 77 | |
| 78 | To install Tink from the source code, the following prerequisites must be |
| 79 | installed: |
| 80 | |
| 81 | * [git](https://git-scm.com/) - to download the source of Tink |
| 82 | * [Bazel](https://www.bazel.build) v0.15.0 or newer - to build Tink |
| 83 | |
| 84 | #### Step-by-step instructions |
| 85 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 86 | 1. Clone Tink from GitHub. |
Haris Andrianakis | e3521ea | 2018-07-12 17:21:12 -0700 | [diff] [blame] | 87 | |
| 88 | ```sh |
| 89 | git clone https://github.com/google/tink/ |
| 90 | ``` |
| 91 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 92 | 2. Build the library and generate a static iOS framework. |
Haris Andrianakis | e3521ea | 2018-07-12 17:21:12 -0700 | [diff] [blame] | 93 | |
| 94 | ```sh |
| 95 | cd tink |
| 96 | export XCODE_VERSION=9.2 |
| 97 | export IOS_SDK=11.2 |
| 98 | bazel build -c opt --ios_multi_cpus=i386,x86_64,armv7,arm64 --xcode_version="${XCODE_VERSION}" --ios_sdk_version="${IOS_SDK}" //objc:Tink_framework |
| 99 | ``` |
| 100 | |
| 101 | Adjust the following options according to your build environment: |
| 102 | |
| 103 | * Set `XCODE_VERSION` to the Xcode version you are using to build your |
| 104 | application. |
| 105 | |
| 106 | * Set `IOS_SDK` to the version of the iOS SDK you are using in your |
| 107 | application. |
| 108 | |
| 109 | * The option `ios_multi_cpus` is used to generate a fat library that |
| 110 | includes multiple architectures. Before submitting your application to |
| 111 | the App Store you should generate a framework that includes only the ARM |
| 112 | architectures and link it to your binary. |
| 113 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 114 | 3. Unzip Tink\_framework.zip into your Xcode project folder. |
Haris Andrianakis | e3521ea | 2018-07-12 17:21:12 -0700 | [diff] [blame] | 115 | |
| 116 | ```sh |
| 117 | unzip bazel-bin/objc/Tink_framework.zip -d /path/to/your/project/folder/ |
| 118 | ``` |
| 119 | |
| 120 | 4. Add the static framework to your Xcode project options: |
| 121 | |
| 122 | * Open your Xcode project |
| 123 | |
| 124 | * Navigate to your project's folder and drag Tink.framework into your |
| 125 | Xcode's left pane. |
| 126 | |
| 127 | * In the following dialog select `Copy items if needed` and the target of |
| 128 | your application that will use Tink. Click `Finish`. |
| 129 | |
| 130 | * Select your project on the left pane and click on "Build Settings" |
| 131 | |
| 132 | * Find `Other Linker Flags` and add `-lc++` |
| 133 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 134 | 5. Start using Tink in your code. |
Haris Andrianakis | e3521ea | 2018-07-12 17:21:12 -0700 | [diff] [blame] | 135 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 136 | You can import the umbrella header: |
| 137 | |
| 138 | ```objc |
| 139 | #import "Tink/Tink.h" |
| 140 | ``` |
| 141 | |
| 142 | Or individual headers: |
| 143 | |
| 144 | ```objc |
| 145 | #import "Tink/TINKAeadConfig.h" |
| 146 | #import "Tink/TINKAeadKeyTemplate.h" |
| 147 | #import "Tink/TINKAead.h" |
| 148 | ``` |
Haris Andrianakis | e3521ea | 2018-07-12 17:21:12 -0700 | [diff] [blame] | 149 | |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 150 | ## Initializing Tink |
| 151 | |
| 152 | Tink provides customizable initialization, which allows for choosing specific |
| 153 | implementations (identified by _key types_) of desired primitives. This |
| 154 | initialization happens via _registration_ of the implementations. |
| 155 | |
Haris Andrianakis | 0ee8fee | 2018-07-12 11:10:39 -0700 | [diff] [blame] | 156 | For example, if you want to use all implementations of all primitives in the |
| 157 | current version of Tink, the initialization would look as follows: |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 158 | |
| 159 | ```objc |
Haris Andrianakis | 57c0a72 | 2018-07-03 10:33:56 -0700 | [diff] [blame] | 160 | #import "Tink/TINKAllConfig.h" |
| 161 | #import "Tink/TINKConfig.h" |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 162 | |
| 163 | NSError *error = nil; |
Haris Andrianakis | 0ee8fee | 2018-07-12 11:10:39 -0700 | [diff] [blame] | 164 | TINKAllConfig *config = [[TINKAllConfig alloc] initWithError:&error]; |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 165 | if (!config || error) { |
| 166 | // handle error. |
| 167 | } |
| 168 | |
| 169 | if (![TINKConfig registerConfig:config error:&error]) { |
| 170 | // handle error. |
| 171 | } |
| 172 | ``` |
| 173 | |
| 174 | To use only implementations of the AEAD primitive: |
| 175 | |
| 176 | ```objc |
Haris Andrianakis | 57c0a72 | 2018-07-03 10:33:56 -0700 | [diff] [blame] | 177 | #import "Tink/TINKAeadConfig.h" |
| 178 | #import "Tink/TINKConfig.h" |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 179 | |
Haris Andrianakis | 57c0a72 | 2018-07-03 10:33:56 -0700 | [diff] [blame] | 180 | NSError *error = nil; |
Haris Andrianakis | 0ee8fee | 2018-07-12 11:10:39 -0700 | [diff] [blame] | 181 | TINKAeadConfig *aeadConfig = [[TINKAeadConfig alloc] initWithError:&error]; |
Haris Andrianakis | 57c0a72 | 2018-07-03 10:33:56 -0700 | [diff] [blame] | 182 | if (!aeadConfig || error) { |
| 183 | // handle error. |
| 184 | } |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 185 | |
Haris Andrianakis | 57c0a72 | 2018-07-03 10:33:56 -0700 | [diff] [blame] | 186 | if (![TINKConfig registerConfig:aeadConfig error:&error]) { |
| 187 | // handle error. |
| 188 | } |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 189 | ``` |
| 190 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 191 | ## Generating new keys and keysets |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 192 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 193 | To avoid accidental leakage of sensitive key material, you should avoid mixing |
| 194 | keyset generation and usage in code. To support the separation between these |
| 195 | activities the Tink package provides a command-line tool called |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 196 | [Tinkey](TINKEY.md), which can be used for common key management tasks. |
| 197 | |
| 198 | Still, if there is a need to generate a KeysetHandle with fresh key material |
| 199 | directly in Obj-C code, one can use |
| 200 | [`TINKKeysetHandle`](https://github.com/google/tink/blob/master/objc/TINKKeysetHandle.h) |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 201 | with one of the available KeyTemplates (AeadKeyTemplate, HybridKeyTemplate, etc.): |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 202 | |
| 203 | ```objc |
Haris Andrianakis | 57c0a72 | 2018-07-03 10:33:56 -0700 | [diff] [blame] | 204 | #import "Tink/TINKAeadKeyTemplate.h" |
| 205 | #import "Tink/TINKKeysetHandle.h" |
| 206 | |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 207 | NSError *error = nil; |
Haris Andrianakis | 57c0a72 | 2018-07-03 10:33:56 -0700 | [diff] [blame] | 208 | TINKAeadKeyTemplate *tpl = [[TINKAeadKeyTemplate alloc] initWithKeyTemplate:TINKAes128Gcm error:&error]; |
| 209 | if (!tpl || error) { |
| 210 | // handle error. |
| 211 | } |
| 212 | |
| 213 | TINKKeysetHandle *handle = [[TINKKeysetHandle alloc] initWithKeyTemplate:tpl error:&error]; |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 214 | if (!handle || error) { |
| 215 | // handle error. |
| 216 | } |
| 217 | ``` |
| 218 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 219 | ## Storing keysets |
Haris Andrianakis | fb7ed28 | 2018-07-11 15:12:53 -0700 | [diff] [blame] | 220 | |
| 221 | After generating key material, you might want to persist it to a storage system. |
| 222 | Tink supports storing keysets to the iOS keychain where they remain encrypted: |
| 223 | |
| 224 | ```objc |
| 225 | #import "Tink/TINKKeysetHandle.h" |
| 226 | |
| 227 | NSError *error = nil; |
| 228 | TINKKeysetHandle *handle = [[TINKKeysetHandle alloc] initWithKeyTemplate:tpl error:&error]; |
| 229 | if (!handle || error) { |
| 230 | // handle error. |
| 231 | } |
| 232 | |
| 233 | NSString *keysetName = @"com.yourcompany.yourapp.uniqueKeysetName"; |
| 234 | if (![handle writeToKeychainWithName:keysetName overwrite:NO error:&error]) { |
| 235 | // handle error. |
| 236 | } |
| 237 | ``` |
| 238 | |
| 239 | The keysets are stored in the keychain with the following options set: |
| 240 | |
| 241 | * kSecAttrAccessible = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly |
| 242 | * kSecAttrSynchronizable = False |
| 243 | |
| 244 | These settings prevent the keysets from leaving the device and keep them |
| 245 | encrypted until the device is unlocked once. |
| 246 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 247 | ## Loading existing keysets |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 248 | |
Haris Andrianakis | fb7ed28 | 2018-07-11 15:12:53 -0700 | [diff] [blame] | 249 | To load keysets from the iOS keychain: |
| 250 | |
| 251 | ```objc |
| 252 | #import "Tink/TINKKeysetHandle.h" |
| 253 | |
| 254 | NSError *error = nil; |
| 255 | NSString *keysetName = @"com.yourcompany.yourapp.uniqueKeysetName"; |
| 256 | TINKKeysetHandle *handle = [[TINKKeysetHandle alloc] initFromKeychainWithName:keysetName error:&error]; |
| 257 | if (!handle || error) { |
| 258 | // handle error. |
| 259 | } |
| 260 | ``` |
| 261 | |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 262 | To load cleartext keysets, use |
| 263 | [`TINKKeysetHandle+Cleartext`](https://github.com/google/tink/blob/master/objc/TINKKeysetHandle+Cleartext.h) |
| 264 | and an appropriate |
| 265 | [`KeysetReader`](https://github.com/google/tink/blob/master/objc/TINKKeysetReader.h), |
| 266 | depending on the wire format of the stored keyset, for example a |
| 267 | [`TINKBinaryKeysetReader`](https://github.com/google/tink/blob/master/objc/TINKBinaryKeysetReader.h). |
| 268 | |
Haris Andrianakis | fb7ed28 | 2018-07-11 15:12:53 -0700 | [diff] [blame] | 269 | Note: We don't recommend storing keysets in cleartext in the filesystem. |
| 270 | Instead, use the iOS keychain as demonstrated above. |
| 271 | |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 272 | ```objc |
Haris Andrianakis | 57c0a72 | 2018-07-03 10:33:56 -0700 | [diff] [blame] | 273 | #import "Tink/TINKBinaryKeysetReader.h" |
| 274 | #import "Tink/TINKKeysetHandle+Cleartext.h" |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 275 | |
| 276 | NSError *error = nil; |
| 277 | NSData *binaryKeyset = ...; |
Haris Andrianakis | 57c0a72 | 2018-07-03 10:33:56 -0700 | [diff] [blame] | 278 | TINKBinaryKeysetReader *reader = [[TINKBinaryKeysetReader alloc] initWithSerializedKeyset:binaryKeyset |
| 279 | error:&error]; |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 280 | if (!reader || error) { |
| 281 | // handle error. |
| 282 | } |
| 283 | |
Haris Andrianakis | 57c0a72 | 2018-07-03 10:33:56 -0700 | [diff] [blame] | 284 | TINKKeysetHandle *handle = [[TINKKeysetHandle alloc] initCleartextKeysetHandleWithKeysetReader:reader |
| 285 | error:&error]; |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 286 | if (!handle || error) { |
| 287 | // handle error. |
| 288 | } |
| 289 | ``` |
| 290 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 291 | ## Obtaining and using primitives |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 292 | |
| 293 | [_Primitives_](PRIMITIVES.md) represent cryptographic operations offered by |
| 294 | Tink, hence they form the core of Tink API. A primitive is just an interface |
| 295 | that specifies what operations are offered by the primitive. A primitive can |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 296 | have multiple implementations, and you choose a desired implementation by using |
| 297 | a key of corresponding type (see the [this |
| 298 | section](KEY-MANAGEMENT.md#key-keyset-and-keysethandle) for details). A list of |
0xflotus | 5bcaf8b | 2020-04-30 17:08:14 +0200 | [diff] [blame] | 299 | primitives and their implementations currently supported by Tink in Objective-C |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 300 | can be found [here](PRIMITIVES.md#objective-c). |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 301 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 302 | You access implementations of a primitive via a factory that corresponds to the |
| 303 | primitive which offers corresponding `primitiveWithKeysetHandle:error:` methods. |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 304 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 305 | * AEAD via `TINKAeadFactory` |
| 306 | * MAC via `TINKMacFactory` |
| 307 | * etc. |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 308 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 309 | ### Symmetric key encryption |
| 310 | |
| 311 | You can use an [AEAD (Authenticated Encryption with Associated |
| 312 | Data)](PRIMITIVES.md#authenticated-encryption-with-associated-data) primitive to |
| 313 | encrypt or decrypt data: |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 314 | |
| 315 | ```objc |
Haris Andrianakis | 57c0a72 | 2018-07-03 10:33:56 -0700 | [diff] [blame] | 316 | #import "Tink/TINKAead.h" |
| 317 | #import "Tink/TINKKeysetHandle.h" |
| 318 | #import "Tink/TINKAeadFactory.h" |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 319 | |
| 320 | // 1. Get a handle to the key material. |
| 321 | TINKKeysetHandle *keysetHandle = ...; |
| 322 | |
| 323 | // 2. Get the primitive. |
| 324 | NSError *error = nil; |
| 325 | id<TINKAead> aead = [TINKAeadFactory primitiveWithKeysetHandle:keysetHandle error:&error]; |
| 326 | if (!aead || error) { |
| 327 | // handle error. |
| 328 | } |
| 329 | |
| 330 | // 3. Use the primitive. |
| 331 | NSData *ciphertext = [aead encrypt:plaintext withAdditionalData:aad error:&error]; |
| 332 | if (!ciphertext || error) { |
| 333 | // handle error. |
| 334 | } |
| 335 | ``` |
| 336 | |
ckl | cf1b786 | 2019-08-21 14:18:29 -0700 | [diff] [blame] | 337 | ### Hybrid encryption |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 338 | |
| 339 | To decrypt using [a combination of public key encryption and symmetric key |
| 340 | encryption](PRIMITIVES.md#hybrid-encryption): |
| 341 | |
| 342 | ```objc |
Haris Andrianakis | 57c0a72 | 2018-07-03 10:33:56 -0700 | [diff] [blame] | 343 | #import "Tink/TINKHybridDecrypt.h" |
| 344 | #import "Tink/TINKKeysetHandle.h" |
| 345 | #import "Tink/TINKHybridDecryptFactory.h" |
Haris Andrianakis | dfc76f8 | 2018-04-10 15:02:08 -0700 | [diff] [blame] | 346 | |
| 347 | // 1. Get a handle to the key material. |
| 348 | TINKKeysetHandle *keysetHandle = ...; |
| 349 | |
| 350 | // 2. Get the primitive. |
| 351 | NSError *error = nil; |
| 352 | id<TINKHybridDecrypt> hybridDecrypt = [TINKHybridDecryptFactory primitiveWithKeysetHandle:keysetHandle |
| 353 | error:&error]; |
| 354 | if (!hybridDecrypt || error) { |
| 355 | // handle error. |
| 356 | } |
| 357 | |
| 358 | // 3. Use the primitive. |
| 359 | NSData *plaintext = [hybridDecrypt decrypt:ciphertext withContextInfo:contextInfo error:&error]; |
| 360 | if (!plaintext || error) { |
| 361 | // handle error. |
| 362 | } |
| 363 | ``` |