Swift framework for handling all of the crypto operation for the Data4Life iOS frameworks.
Swift framework for handling all of the crypto operation for the Data4Life iOS frameworks.
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.
Data4LifeCrypto
supports two modes of symmetric crypto operations:
* AES-SHA256-CBC-PKCS7
* AES-SHA256-GCM-NOPADDING
Note: Provided by Apple Cryptokit frameworks
Data4LifeCrypto
supports one mode of asymmetric crypto operations:
* RSA-OAEP-SHA256
Note: Provided by Apple Security framework
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
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)
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
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)
See changelog
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}
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.