Skip to content

d4l-data4life/d4l-crypto-ios

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

97 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

About The Project

Swift framework for handling all of the crypto operation for the Data4Life iOS frameworks.

Installation

Swift Package Manager

To install with Swift package manager, select your project’s Swift Packages tab, and add our repository url, either as ssh or https url:

https://github.com/d4l-data4life/d4l-crypto-ios.git

OR

git@github.com:d4l-data4life/d4l-crypto-ios.git

In the next step, select the latest version, and then import the Data4LifeCrypto framework in your target.

Features

Supported symmetric algorithms

Data4LifeCrypto supports two modes of symmetric crypto operations: * AES-SHA256-CBC-PKCS7 * AES-SHA256-GCM-NOPADDING

Note: Provided by Apple Cryptokit frameworks

Supported asymmetric algorithms

Data4LifeCrypto supports one mode of asymmetric crypto operations: * RSA-OAEP-SHA256

Note: Provided by Apple Security framework

Storing and working with crypto keys

KeyPair is managed by Apple and has to be loaded using Security framework APIs that expect tag. Key has to be stored manually in prefered storage of choice.

It’s possible to generate KeyPair as it’s stored on device, and use that to encrypt Key, that way it’s possible to store data keys and encrypted sensitive information on the API.

Store keys and data: * Generate Key for encrypting data (KeyType.data) * Create key exchange format from the key type * Encrypt sensitive data using generated Key * Generate KeyPair for encrypting data key * Convert data key from Key type to Data type (Key conforms to Codable) * Encrypt key data using KeyPair * Store ciphertext, iv and encrypted data key in some storage

Read keys and data: * Load already created KeyPair from the device * Load ciphertext, iv and encrypted data key from storage * Decrypt data key using keypair * Decrypt sensitive data using decrypted data key, ciphertext and iv

Key (symmetric)

Symmetric key used for encrypt/decrypt operations.

struct Key: Codable {
    let value: SymmetricKey
    var algorithm: AlgorithmType
    let keySize: KeySize
    let type: KeyType
}

Create new key

func generate(keySize: KeySize, algorithm: AlgorithmType, type: KeyType) throws -> Key

Create key from data. It will throw an error if the size of the data does not correspond to the key type.

init(data: Data, type: KeyType) throws

// Exg.
let keyType: KeyType = .data
let key = Key(data: data, type: keyType)

Loading already existing key

let keyData = Data(...) // load from some storage
let key = try JSONDecoder().decode(Key.self, from: keyData)

Exporting key for storage

let key = Key.generate(...)
let keyData = try JSONEncoder().encode(key)

Example of creating a key and encrypting data

let keyType: KeyType = .data
let keyExchangeFormat = try KeyExchangeFactory.create(type: keyType)
let key = try Key.generate(keySize: keyExchangeFormat.size, algorithm: keyExchangeFormat.algorithm, type: type)
let iv = Data(bytes: [...]) // generate random IV

let plaintext: Data = Data(bytes: [0x00, 0x01, 0x02])
let ciphertext: Data = Data4LifeCryptor.symEncrypt(key: key, data: plaintext, iv: iv)
// Store ciphertext, iv and key in a safe way

Decrypt the data

let key: Key =  ... // fetch key from storage
let iv: Data = ... // fetch iv from storage
let ciphertext: Data =  ... // fetch ciphertext from storage
let plaintext: Data = try Data4LifeCryptor.symDecrypt(key: key, data: ciphertext, iv: iv)

KeyPair (asymmetric)

struct KeyPair: KeyPairType {
    let privateKey: AsymmetricKey
    let publicKey: AsymmetricKey
    let keySize: KeySize
    let algorithm: AlgorithmType
}

Helper methods for working with keypairs (wrapper around Security framework)

static func generate(tag: String, keySize: Int, algorithm: AlgorithmType, isPermanent: Bool = true) throws -> KeyPair
static func load(tag: String, algorithm: AlgorithmType) throws -> KeyPair
static func destroy(tag: String) throws
func store(tag: String) throws

AsymetricKey (asymmetric)

public AsymmetricKey {
    let value: SecKey
    let type: AsymmetricKeyType
    let algorithm: AlgorithmType

    init(data: Data, type: AsymmetricKeyType, keySize: KeySize) throws
}

enum AsymmetricKeyType: String {
    case `public`
    case `private`
}

Exporting asymmetric keys can be done in one of two formats PKCS#1 or SPKI or as Data

let keypair = try KeyPair.generate(...)

let pkcs1PublicKey: String = keypair.publicKey.asBase64EncodedString()
let spkiPublicKey: String = keypair.publicKey.asSPKIBase64EncodedString()
let secDataPublicKey: Data = keypair.publicKey.asData()
let jsonDataPublicKey: Data = JSONEncoder().encode(keypair) // will export `SPKI` encoded public key and ignore private key

Example of creating keypair and encrypting data

let tag: String = "com.example.keypair"
let keyType: KeyType = .appPrivate
let keyExchangeFormat = try KeyExchangeFactory.create(type: type)
let keypair = try KeyPair.generate(tag: tag, keySize: keyExchangeFormat.size, algorithm: keyExchangeFormat.algorithm)

let plaintext: Data = Data(bytes: [0x00, 0x01, 0x02])
let ciphertext: Data = try Data4LifeCryptor.asymEncrypt(key: keypair, data: plaintext)

Example of creating keypair and encrypting data only using an asymmetric key

let tag: String = "com.example.keypair"
let keyType: KeyType = .appPrivate
let keyExchangeFormat = try KeyExchangeFactory.create(type: type)
let keypair = try KeyPair.generate(tag: tag, keySize: keyExchangeFormat.size, algorithm: keyExchangeFormat.algorithm)

let plaintext: Data = Data(bytes: [0x00, 0x01, 0x02])
let ciphertext: Data = try Data4LifeCryptor.asymEncrypt(publicKey: keypair.publicKey, data: plaintext)

Decrypt the data

let tag: String = "com.example.keypair"
let keyType: KeyType = .appPrivate
let keyExchangeFormat = KeyExchangeFactory.create(type: type)
let keypair = try KeyPair.load(tag: tag, algorithm: keyExchangeFormat.algorithm)

let ciphertext: Data = ... // fetch ciphertext from storage
let plaintext: Data = try Data4LifeCryptor.asymDecrypt(key: keypair, data: ciphertext)

Decrypt the data only using an asymmetric key

let tag: String = "com.example.keypair"
let keyType: KeyType = .appPrivate
let keyExchangeFormat = KeyExchangeFactory.create(type: type)
let keypair = try KeyPair.load(tag: tag, algorithm: keyExchangeFormat.algorithm)

let ciphertext: Data = ... // fetch ciphertext from storage
let plaintext: Data = try Data4LifeCryptor.asymDecrypt(privateKey: keypair.privateKey, data: ciphertext)

Changelog

See changelog

Versioning

We use Semantic Versioning as a guideline for our versioning.

Releases use this format: {major}.{minor}.{patch}

  • Breaking changes bump {major} and reset {minor} & {patch}

  • Backward compatible changes bump {minor} and reset {patch}

  • Bug fixes bump {patch}

Contributing

You want to help or share a proposal? You have a specific problem? Read the following:

  • Code of conduct for details on our code of conduct.

  • Contributing for details about how to report bugs and propose features.

  • Developing for details about our development process and how to build and test the project.

Copyright (c) 2021 D4L data4life gGmbH / All rights reserved. Please refer to our License for further details.