iOS SDK
Overview
The ZeroKit iOS SDK brings ZeroKit to iPhones and iPads. Use it to authenticate users, encrypt, decrypt and securely share data.
Development Requirements
- Xcode 8.1+
- iOS 8.0+ (iOS 9.0+ to run the example app)
- Swift 3.0
- Objective-C compatible
End-user Requirements
- Device running iOS 8 or later (iPhone 4S or later, iPad 2 or later)
Installation
Manually
After cloning the git repository or downloading the source code follow these steps:
1. Drag and drop the ZeroKit.xcodeproj into your Xcode project.2. Add the ZeroKit.xcodeproj/Products/ZeroKit.framework to your application's Embedded Binaries.3. Build your app.
Carthage
Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
To integrate ZeroKit into your Xcode project using Carthage, specify it in your Cartfile:
Cartfile
github "ZeroKit/ZeroKit"
Run carthage update to build the framework and drag the built ZeroKit.framework into your Xcode project.
Setup
Steps:
- Create a configuration for ZeroKit.
- Initialize a ZeroKit object. After initialization you can make calls to ZeroKit
Note: ZeroKit SDK for iOS wraps our javascript SDK. For this to run properly a web view is required to provide the runtime environment. As the user of the SDK you need not to interact with this web view and should not cause any trouble in most cases.
Swift
import ZeroKit
let zeroKitConfig = ZeroKitConfig(apiBaseUrl: URL(string: "https://{TenantId}.api.tresorit.io")!)
let zeroKit = try! ZeroKit(config: zeroKitConfig)
Objective-C
#import <ZeroKit/ZeroKit-Swift.h>
NSURL *apiBaseURL = [NSURL URLWithString:@"https://{TenantId}.api.tresorit.io"];
ZeroKitConfig *config = [[ZeroKitConfig alloc] initWithApiBaseUrl:apiBaseURL];
NSError *error = nil;
ZeroKit *zeroKit = [[ZeroKit alloc] initWithConfig:config error:&error];
Password handling
A core concept of ZeroKit is that your application should not access and pass around the users' passwords. All password handling should be done by ZeroKit. For this we provide a ZeroKitPasswordField UI component you that you should present to users to enter their passwords. Some tips for using ZeroKitPasswordField class:
- You can instantiate it in code or in Interface Builder. - It is marked as @IBDesignable so you can preview it in Interface Builder and also set some custom properties.- In Interface Builder simply add a UIView to your layout and set its class to ZeroKitPasswordField. Also make sure the Module is set to ZeroKit.- When you need the user to type their password twice for confirmation (eg. during registration) you create two password fields and set the matchingField property on them. Use the passwordsMatch property to check if they match.- You can customize its appearance similarly to a UITextField.
To get the strength of a password use the passwordStrength(passwordField:, completion:) method of a ZeroKit object.
Making a call
Swift
zeroKit.encrypt(plainText: "apple", inTresor: tresorId) { cipherText, error in
guard error == nil else {
print("Encrypting failed: \(error!)")
return
}
print("Encrypted text: \(cipherText!)")
}
Objective-C
[self.zeroKit encryptWithPlainText:"apple" inTresor:tresorId completion:^(NSString * _Nullable cipherText, NSError * _Nullable error) {
if (error) {
NSLog(@"Encrypting failed: %@", error);
} else {
NSLog(@"Encrypted text: %@", cipherText);
}
}];
Identity Provider
ZeroKit comes with OpenID Connect provider implementation that you can use in your app. Use the getIdentityTokens(clientId:completion:) method of a ZeroKit object to get authorization code and identity token for the current ZeroKit user. A user must be logged in when calling this method. The ZeroKit Open ID client used in mobile apps should have the following settings:
- Redirect URL should have the following format: https://{Client ID}.{Tenant ID}.api.tresorit.io/
- Flow should be set to Hybrid
- You can optionally turn on Requires proof key (DHCE)
You can add new clients and edit settings on the management portal.
Administrative API
Most of the cryptographic operations (including invites and sharing) must be done client side by the SDK library. To provide control over these operations, and to prevent possible abuse by tampering the client, we introduced the admin API. All client initiated changes which has a permanent effect on the server has to be approved through the Admin API (typically by the server backend of the integrated app). For more information see the Administrative API documentation.
IMPORTANT: You must never include your Admin key in your client application. We provide a sample backend to handle the administrative calls for our sample app. The admin key must be kept secret and not included in any client applications that you distribute.
Example Application
An example appliction is included with ZeroKit to demonstrate its usage.
Known Issues and Limitations
Currently the SDK does not support app extensions.
API Documentation
Configuration
class ZeroKitConfig
You can provide your custom configuration when setting up ZeroKit with the ZeroKitConfig class.
**properties:**
- public var keychainAccessGroup: String?: Specify the keychain access group if your app needs one.
- public var keychainAccessibility: CFString: Keychain accessibility option. The default value is kSecAttrAccessibleWhenUnlockedThisDeviceOnly. See Apple's documentation for Keychain Item Accessibility Constants that describes the possible values for the kSecAttrAccessible key.
public init(apiUrl: URL):
Initialize a ZeroKitConfig with the API URL.
parameters:
- apiUrl: URL for the API
ZeroKit
The ZeroKit class provides the interface to the ZeroKit SDK on iOS.
public init(config: ZeroKitConfig) throws
Initialize ZeroKit with the configuration.
parameters:
- config: config for ZeroKit
throws
- ZeroKitError.cannotAddWebView
Password Strength
class PasswordStrength
It shows the length and the strength of the passwords and gives estimates of time number of guesses and time required to crack the password. We calculate this by running zxcvbn (https://github.com/dropbox/zxcvbn).
properties:
- public let score: PasswordScore: Score that represents the esitamted strength of the password.
- public let length: Int: Shows the length of the password.
- public let guessesLog10: Double: The log10 of the estimated number of guesses needed to crack the password.
- public let crackTimes: PasswordCrackTimes: Crack time estimates.
- public let warning: String?: A warning provided by the library that explains what's wrong with the password, eg. 'This is a top-10 common password'.
- public let suggestions: [String]?: Some suggestions provided by the library to improve the password.
class PasswordCrackTimes
The time required to crack the password in different scenarios.**
properties:
- public let offlineFastHashing: Double: Crack time in seconds for offline hashing 1e10 per second
- public let offlineSlowHashing: Double: Crack time in seconds for offline hashing 1e4 per second
- public let onlineNoThrottling: Double: Crack time in seconds for online 10 per second
- public let onlineThrottling: Double: Crack time in seconds for online 100 per hour
func passwordStrength(passwordField: ZeroKitPasswordField, completion: @escaping PasswordStrengthCallback)
Estimate the strength of a password.
parameters:
- passwordField: The password field containing the password typed by the user.
- completion: Called when the strength calculation completes.
passwordStrength(password: String, completion: @escaping PasswordStrengthCallback)
Estimate the strength of a password.
Prefer using passwordStrength(passwordField: ZeroKitPasswordField, userData: [String]? = nil, completion: @escaping PasswordStrengthCallback) to avoid handling the user's password.
parameters:
- password: The password.
- completion: Called when the strength calculation completes.
Registration
public func register(withUserId userId: String, registrationId: String, passwordField: ZeroKitPasswordField, completion: @escaping RegistrationCompletion)
Register a user. This is the second step of the 3-step registration flow. Before this method is called a user registration session must be initialized through the administration API of the ZeroKit backend. For more information on the registration flow please refer to the ZeroKit documentation.
parameters:
- userId: User ID received when initialized registration session
- registrationId: Registration session ID received when initialized registration session
- passwordField: Password field containing the password typed by the user
- completion: Called when registration finishes
public func register(withUserId userId: String, registrationId: String, password: String, completion: @escaping RegistrationCompletion)
Prefer using register(withUserId userId: String, registrationId: String, passwordField: ZeroKitPasswordField, completion: @escaping RegistrationCompletion) to avoid handling the user's password.
parameters:
- userId: User ID received when initialized registration session
- registrationId: Registration session ID received when initialized registration session
- password: Password chosen by the user
- completion: Called when registration finishes
Login
public func login(withUserId userId: String, passwordField: ZeroKitPasswordField, rememberMe: Bool, completion: @escaping DefaultCompletion)
Login using the ZeroKit backend.
parameters:
- userId: User ID
- passwordField: Password field containing the password typed by the user
- rememberMe: Set to true if you want to log in the user without password by calling
loginWithRememberMe - completion: Called when login finishes
public func login(withUserId userId: String, password: String, rememberMe: Bool, completion: @escaping DefaultCompletion)
Prefer using login(with userId: String, passwordField: ZeroKitPasswordField, rememberMe: Bool, completion: @escaping UserIdCallback) to avoid handling the user's password.
parameters:
- userId: User ID
- password: User password
- rememberMe: Set to true if you want to log in the user without password by calling
loginWithRememberMe - completion: Called when login finishes
public func loginByRememberMe(with userId: String, completion: @escaping DefaultCompletion)
Use this method for login if 'remember me' was set to yes for a previous login with password.
parameters:
- userId: The user ID to log in with
- completion: Called when login finishes
public func canLoginByRememberMe(with userId: String) -> Bool
Check if the user can be logged in with the loginWithRememberMe method.
parameters:
- userId: User ID
returns:
trueif the user can be logged in,falseotherwise
public func logout(completion: @escaping DefaultCompletion)
Logout. If the user logged in with 'remember me' option, then they will have to re-enter their password to access any data.
parameters:
- completion: Called when logout finishes
public func whoAmI(completion: @escaping UserIdCompletion)
Use this methods to get the logged in user's identity.
parameters:
- completion: Called when
whoAmIfinishes. Returns the user ID if logged in ornilif not.
Password change
public func changePassword(for userId: String, currentPasswordField: ZeroKitPasswordField, newPasswordField: ZeroKitPasswordField, completion: @escaping DefaultCompletion)
Change password.
parameters:
- userId: The user ID of the current user
- currentPasswordField: The password field containing the user's current password
- newPasswordField: The password field containing the user's new password
public func changePassword(for userId: String, currentPassword: String, newPassword: String, completion: @escaping DefaultCompletion)
Prefer using changePassword(for userId: String, currentPasswordField: ZeroKitPasswordField, newPasswordField: ZeroKitPasswordField, completion: @escaping DefaultCompletion) to avoid handling the user's password.
parameters:
- userId: The user ID of the current user
- currentPassword: The current password
- newPassword: The new password
Tresor management
public func createTresor(completion: @escaping TresorIdCompletion)
Creates a tresor bound to the user but it will only be usable once it's approved. The tresor ID returned in the completion callback should be saved as it is the only way to identifiy the tresor.
parameters:
- completion: Called when tresor creation finishes, contains the tresor ID if successful. Approve this tresor through the administration API of the ZeroKit backend.
public func share(tresorWithId tresorId: String, withUser userId: String, completion: @escaping OperationIdCompletion)
Shares the tresor with the given user. The operation will only be effective after it is approved using the returned OperationId. This uploads a modified tresor, but the new version is downloadable only after it has been approved. This should be done as soon as possible, as approving any operation to a tresor may invalidate any pending ones
parameters:
- tresorId: ID of the tresor to be shared
- userId: ID of the user to share the tresor with
- completion: Called when the operation finishes, contains the operationId required to approve the operation.
public func kick(userWithId userId: String, fromTresor tresorId: String, completion: @escaping OperationIdCompletion)
Removes the given user from the tresor. The operation will only be effective after it is approved using the returned OperationId.
parameters:
- userId: ID of the user to kick
- tresorId: ID of the tresor to kick from
- completion: Called when the operation finishes, contains the operationId required to approve the operation.
Encryption/Decryption
public func encrypt(plainText: String, inTresor tresorId: String, completion: @escaping CipherTextCompletion)
Encrypts the plain text by the given tresor.
parameters:
- plainText: The plain text to encrypt
- tresorId: The id of the tresor, that will be used to encrypt the text
- completion: Called when encryption finishes, contains the cipher text if successful
public func decrypt(cipherText: String, completion: @escaping PlainTextCompletion)
Decrypts the given cipherText.
note: You do not need to provide a tresor ID to decrypt the cipher text. If the user has access to the tresor that was used for encryption then decryption will succeed.
parameters:
- cipherText: ZeroKit encrypted text
- completion: Called when decryption finishes, contains the plain text if successful
public func encrypt(plainData: Data, inTresor tresorId: String, completion: @escaping CipherDataCompletion)
Encrypts the plain bytes by the given tresor.
parameters:
- plainData: The plain data to encrypt
- tresorId: The id of the tresor, that will be used to encrypt the text
- completion: Called when encryption finishes, contains the cipher data if successful
public func decrypt(cipherData: Data, completion: @escaping PlainDataCompletion)
Decrypts the given cipher bytes.
parameters:
- cipherData: ZeroKit encrypted data
- completion: Called when decryption finishes, contains the plain data if successful
Invitation links
class InvitationLink
Invitation links can be used to give access to tresors. Use the ZeroKit class to create invitation links.
properties:
- publiclet id: **String**: ID of the invitation link
- publiclet url: **URL**: url of the invitation link
class InvitationLinkPublicInfo
Contains information about an invitation link. You can get the information by calling getInvitationLinkInfo.
properties:
- public let token: String: link information for internal use, used as a parameter for acceptInvitationLink
- public let isPasswordProtected: Bool: bool value indicating if the link is password protected
- public let creatorUserId: String: the user id of the creator of this link
- public let message: String?: arbitrary string data set at the time of creation of the link
public func createInvitationLink(with linkBase: URL, forTresor tresorId: String, withMessage message: String, passwordField: ZeroKitPasswordField, completion: @escaping InvitationLinkCompletion)
Creates an invitation link that can be used by the invitee to gain access to the tresor. The secret that can be used to open the invitation link is concatenated to the end of the link after a '#'. It is done so, because this way the secret never travels to your (or our servers). We recommend that you use password protected links and to transfer these passwords on a different channel than the link. This operation needs administrative approval.
parameters:
- linkBase: the base of the link. The link secret is concatenated after this after a '#'
- tresorId: the id of the tresor
- message: optional arbitrary string data that can be retrieved without a password or any other information
- passwordField: password field containing the password required to accept the invitation link
- completion: contains the created link if successful
public func createInvitationLink(with linkBase: URL, forTresor tresorId: String, withMessage message: String, password: String, completion: @escaping InvitationLinkCompletion)
Prefer using createInvitationLink(with linkBase: URL, forTresor tresorId: String, withMessage message: String, passwordField: ZeroKitPasswordField, completion: @escaping InvitationLinkCompletion) to avoid handling the password.
parameters:
- linkBase: the base of the link. The link secret is concatenated after this after a '#'
- tresorId: the id of the tresor
- message: optional arbitrary string data that can be retrieved without a password or any other information
- password: password required to accept the invitation link
- completion: contains the created link if successful
public func createInvitationLinkWithoutPassword(with linkBase: URL, forTresor tresorId: String, withMessage message: String, completion: @escaping InvitationLinkCompletion)
Creates an invitation link that can be used by the invitee to gain access to the tresor. The secret that can be used to open the invitation link is concatenated to the end of the link after a '#'. It is done so, because this way the secret never travels to your (or our servers). We recommend that you use password protected links and to transfer these passwords on a different channel than the link. This operation needs administrative approval.
parameters:
- linkBase: the base of the link. The link secret is concatenated after this after a '#'
- tresorId: the id of the tresor
- message: optional arbitrary string data that can be retrieved without a password or any other information
- completion: contains the created link if successful
public func getInvitationLinkInfo(with secret: String, completion: @escaping InvitationLinkInfoCompletion)
Retrieves information about the link.
parameters:
- secret: The secret is in the fragment identifier of the link url
- completion: Return information about the link when finishes.
public func acceptInvitationLink(with token: String, passwordField: ZeroKitPasswordField, completion: @escaping OperationIdCompletion)
This method will add the user to the tresor of the link using the entered password.
parameters:
- token: The
tokenfield of theInvitationLinkPublicInfoof the link returned bygetInvitationLinkInfo. - passwordField: The password field containing the required password to accept the invitation link.
- completion: Called when the operation finishes, contains the operationId required to approve the operation.
public func acceptInvitationLink(with token: String, password: String, completion: @escaping OperationIdCompletion)
Prefer using acceptInvitationLink(with token: String, passwordField: ZeroKitPasswordField, completion: @escaping OperationIdCompletion) to avoid handling the password.
parameters:
- token: The
tokenfield of theInvitationLinkPublicInfoof the link returned bygetInvitationLinkInfo. - password: The password required to accept the invitation link.
- completion: Called when the operation finishes, contains the operationId required to approve the operation.
public func acceptInvitationLinkWithoutPassword(with token: String, completion: @escaping OperationIdCompletion)
This method will add the user to the tresor of the link.
parameters:
- token: The
tokenfield of theInvitationLinkPublicInfoof the link returned bygetInvitationLinkInfo. - completion: Called when the operation finishes, contains the operationId required to approve the operation.
Identity Provider
class ZeroKitIdentityTokens
properties:
public let authorizationCode: String: Authorization code
public let identityToken: String: Identity token
public let codeVerifier: String?: Contains the code verifier if you have 'Requires proof key' enabled for your client
public func getIdentityTokens(clientId: String, completion: @escaping IdentityTokensCompletion)
Get authorization code and identity tokens for the currenty logged in user. note: User must be logged in when calling this method.
parameters:
- clientId: The cliend ID for the current ZeroKit OpenID Connect client set up in the management portal.
- completion: Returns the identity tokens or an error if an error occurred.
Typealiases
Typealiases
publictypealias DefaultCompletion = (NSError?) -> Void
publictypealias RegistrationCompletion = (/ validation verifier /String?, NSError?) -> Void
publictypealias UserIdCompletion = (/ user ID /String?, NSError?) -> Void
publictypealias TresorIdCompletion = (/ tresor ID /String?, NSError?) -> Void
publictypealias CipherTextCompletion = (/ cipher text /String?, NSError?) -> Void
publictypealias PlainTextCompletion = (/ plain text /String?, NSError?) -> Void
publictypealias CipherDataCompletion = (/ cipher data /Data?, NSError?) -> Void
publictypealias PlainDataCompletion = (/ plain data /Data?, NSError?) -> Void
publictypealias InvitationLinkCompletion = (InvitationLink?, NSError?) -> Void
publictypealias InvitationLinkInfoCompletion = (InvitationLinkPublicInfo?, NSError?) -> Void
publictypealias OperationIdCompletion = (/ operation ID /String?, NSError?) -> Void
public typealias PasswordStrengthCallback = (PasswordStrength?, NSError?) -> Void
public typealias IdentityTokensCompletion = (ZeroKitIdentityTokens?, NSError?) -> Void
Password field
The ZeroKitPasswordField provides the ZeroKit SDK user with a text field that hides access to the user's password. Making it harder to make mistakes when handling the password. It is a UIView subclass, you initialize it like you would any UIView.
You can customize some appearance properties in interface builder. Customize the rest in code.
public weak var matchingField: ZeroKitPasswordField?
When the user needs to enter the password twice for confirmation, use this property to match two password fields. When password fields are matched you can use the passwordsMatch property to check if the contents of the two fields are the same.
Setting the matchingField on one will also set it on the other password field.
note: The password fields hold weak references to each other.
public weak var delegate: ZeroKitPasswordFieldDelegate?
Delegate to be notified of password field events.
public var isEmpty: Bool
true if the field is empty
public func isPasswordValid() -> Bool
true if the entered password is valid
public var passwordsMatch: Bool
Check if this field and its matchingField has the same content. Value if false if the password field's matchingField property is nil.
ZeroKitPasswordFieldDelegate
optional func passwordFieldContentsChanged(_ passwordField: ZeroKitPasswordField)
Called when the contents of the password field changes.
optional func passwordFieldReturnWasPressed(_ passwordField: ZeroKitPasswordField)
Called when the Return key was pressed.
optional func passwordFieldShouldBeginEditing(_ passwordField: ZeroKitPasswordField) -> Bool
Return false to disallow editing.
optional func passwordFieldDidBeginEditing(_ passwordField: ZeroKitPasswordField)
The password field became first responder.
optional func passwordFieldShouldEndEditing(_ passwordField: ZeroKitPasswordField) -> Bool
Return true to allow editing to stop and to resign first responder status. False to disallow the editing session to end.
optional func passwordFieldDidEndEditing(_ passwordField: ZeroKitPasswordField)
May be called if forced even if shouldEndEditing returns false (e.g. view removed from window) or endEditing(true) called.