SwiftECC provides elliptic curve cryptography in Swift.
This encompasses:
Creating, loading and storing public and private keys
Encryption and decryption using the ECIES algorithm based on the AES block cipher and six different block modes
AEAD (Authenticated Encryption with Associated Data) encryption and decryption using the ECIES algorithm with the ChaCha20/Poly1305 or the AES/GCM cipher
Signature signing and verifying using the ECDSA algorithm, including the option of deterministic signatures
Secret key agreement using the Diffie-Hellman key agreement algorithm - ECDH
Ability to create your own domains
General elliptic curve arithmetic
Usage
In your project Package.swift file add a dependency like
SwiftECC requires Swift 5.0. It also requires that the Int and UInt types be 64 bit types.
SwiftECC uses Apple’s CryptoKit framework. Therefore, for macOS the version must be at least 10.15,
for iOS the version must be at least 13, and for watchOS the version must be at least 8.
Basics
The basic concept in SwiftECC is the Elliptic Curve Domain, represented by the Domain class.
Please, refer section 3.1 in [SEC 1] that describes the domain concept in detail.
There are 18 predefined NIST domains
and 14 predefined Brainpool domains in SwiftECC,
and it is possible to create your own characteristic 2, and odd prime characteristic domains.
You need a public key in order to encrypt a message or verify a signature, and you need a private key in order to decrypt a message or sign a message.
Given a domain, you can generate public/private key pairs or you can load them from the PEM- or DER encoding of existing keys.
Creating New Keys
For a given domain it is possible to generate a public/private key pair. For example:
let domain = Domain.instance(curve: .EC384r1)
let (pubKey, privKey) = domain.generateKeyPair()
The private key is simply a random positive integer less than the domain order. The public key is the domain generator point multiplied by the private key.
Given a private key, say ‘privKey’, you can generate the corresponding public key, like
let pubKey = ECPublicKey(privateKey: privKey)
Given a domain, say ‘dom’ and a curve point, say ‘pt’, you can generate a public key, like
let pubKey = try ECPublicKey(domain: dom, w: pt)
Loading Existing Keys
It is possible to create keys from their PEM encodings. For example
// Public key encoding - EC384r1 domain
let pubKeyPem =
"""
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEQW/MahMwMTFjwY95uOEdfBVC7HrQhTGG
TwxiPlgDiARqC6y6EQ1Ajkuhe4A02WOltRYQRXKytzspOR25UfgtagURAwxVFYzR
9cmi6FRmvvq/Tsigd/dAi4FNjniR7/Pg
-----END PUBLIC KEY-----
"""
let pubKey = try ECPublicKey(pem: pubKeyPem)
// Private key encoding in PKCS#8 format - EC384r1 domain
let privKeyPem =
"""
-----BEGIN PRIVATE KEY-----
MIG/AgEAMBAGByqGSM49AgEGBSuBBAAiBIGnMIGkAgEBBDBmpNziSYmGoWwl7apJ
M9ZdDBxkJqmxMScHGXG45ZQXSv7fIuJlsSwxK76nUiiO7gigBwYFK4EEACKhZANi
AARBb8xqEzAxMWPBj3m44R18FULsetCFMYZPDGI+WAOIBGoLrLoRDUCOS6F7gDTZ
Y6W1FhBFcrK3Oyk5HblR+C1qBREDDFUVjNH1yaLoVGa++r9OyKB390CLgU2OeJHv
8+A=
-----END PRIVATE KEY-----
"""
let privKey = try ECPrivateKey(pem: privKeyPem)
// See the key ASN1 structures
print(pubKey)
print(privKey)
The implied encryption parameters are cipher block mode = CBC, iteration count = 2048 and salt = 8 random bytes.
The password is simply a byte array, any possible interpretation of it as a string is unspecified.
The encrypted private key is compatible with, and is readable by OpenSSL.
Private keys can be created from their PEM encodings in encrypted form.
In the example the encrypted private key was created by OpenSSL using the AES-256 cipher in CBC mode with password ‘abcd’.
SwiftECC can read encrypted private key files provided they were encrypted with one of the ciphers AES-128, AES-192 or AES-256 in CBC mode.
Encryption and Decryption
Encryption and decryption is done using the ECIES algorithm based on the AES block cipher using one of
AES-128, AES-192 or AES-256 ciphers, depending on your choice.
The following cipher block modes are supported:
GCM - Galois Counter mode. This is the default mode
ECB - Electronic Codebook mode with PKCS#7 padding
CBC - Cipher Block Chaining mode with PKCS#7 padding
CFB - Cipher Feedback mode
CTR - Counter mode
OFB - Output Feedback mode
The encryption and decryption speed for domain EC256k1 (the bitcoin domain) measured on an iMac 2021, Apple M1 chip
using AES-128 are shown below - units are Megabytes per second.
Block Mode
Encrypt
Decrypt
GCM
53 MByte/Sec
53 MByte/Sec
ECB
30 MByte/Sec
30 MByte/Sec
CBC
24 MByte/Sec
25 MByte/Sec
CFB
23 MByte/Sec
23 MByte/Sec
CTR
30 MByte/Sec
30 MByte/Sec
OFB
29 MByte/Sec
29 MByte/Sec
Unless compatibility with IBM's BlueECC product is necessary, encryption / decryption using GCM block mode is deprecated.
Use the *encryptAESGCM / decryptAESGCM* methods instead. Their performance is many times better.
Key Derivation
SwiftECC uses the X9.63 Key Derivation Function to derive block cipher keying materiel. Please refer [SEC 1] section 3.6.
Six cases are considered:
The AES key and HMAC key can be retrieved with the ECPrivateKey method ‘getKeyAndMac’.
For block modes CBC, CFB, CTR, and OFB the initialization vector (IV) is 16 zero bytes.
BlueECC Compatibility
Data encrypted by SwiftECC in the EC256r1 domain with AES128/GCM, in the EC384r1 domain with AES256/GCM
and in the EC521r1 domain with AES256/GCM can be decrypted with IBM's BlueECC product using curve prime256v1,
secp384r1, and secp521r1, respectively.
Likewise, data encrypted by BlueECC with curve prime256v1, secp384r1 and secp521,
can be decrypted by SwiftECC using EC256r1 with AES128/GCM, EC384r1 with AES256/GCM and EC521r1 with AES256/GCM, respectively.
Example
import SwiftECC
// You need a public key to encrypt a message and the corresponding private key to decrypt it,
// for example from the EC163k1 domain
let pemPublic163k1 =
"""
-----BEGIN PUBLIC KEY-----
MEAwEAYHKoZIzj0CAQYFK4EEAAEDLAAEA6txn7CCae0d9AiGj3Rk5m9XflTCB81oe1fKZi4F4oip
SF2u79k8TD5J
-----END PUBLIC KEY-----
"""
let pemPrivate163k1 =
"""
-----BEGIN EC PRIVATE KEY-----
MFICAQEEFNfflqz2oOd9WpxuMZ9wJTFO1sjgoAcGBSuBBAABoS4DLAAEA6txn7CCae0d9AiGj3Rk
5m9XflTCB81oe1fKZi4F4oipSF2u79k8TD5J
-----END EC PRIVATE KEY-----
"""
let text = "The quick brown fox jumps over the lazy dog!"
do {
let pubKey = try ECPublicKey(pem: pemPublic163k1)
let privKey = try ECPrivateKey(pem: pemPrivate163k1)
let encryptedData = pubKey.encrypt(msg: text.data(using: .utf8)!, cipher: .AES128)
let decryptedData = try privKey.decrypt(msg: encryptedData, cipher: .AES128)
print(String(data: decryptedData, encoding: .utf8)!)
} catch {
print("\(error)")
}
giving
The quick brown fox jumps over the lazy dog!
AEAD Encryption and Decryption
Authenticated Encryption with Associated Data (AEAD) is implemented with the ChaCha20/Poly1305 algorithm and the AES/GCM algorithm.
Both implementations use Apple's CryptoKit framework, that takes advantage of hardware support for the AES and GCM algorithms.
Example
import SwiftECC
let plainText = "Hi, there!"
let aaData = "This is the additional authenticated data"
let (pub, priv) = Domain.instance(curve: .EC256k1).makeKeyPair()
let cipherText1 = pub.encryptChaCha(msg: Bytes(plainText.utf8), aad: Bytes(aaData.utf8))
let cipherText2 = pub.encryptAESGCM(msg: Bytes(plainText.utf8), cipher: .AES128, aad: Bytes(aaData.utf8))
do {
let text1 = try priv.decryptChaCha(msg: cipherText1, aad: Bytes(aaData.utf8))
print(String(bytes: text1, encoding: .utf8)!)
let text2 = try priv.decryptAESGCM(msg: cipherText2, cipher: .AES128, aad: Bytes(aaData.utf8))
print(String(bytes: text2, encoding: .utf8)!)
} catch {
print("Exception: \(error)")
}
giving:
Hi, there!
Hi, there!
The encryption and decryption speed for domain EC256k1 (the bitcoin domain) measured on an iMac 2021, Apple M1 chip are shown below - units are Megabytes per second.
Algorithm
Encrypt
Decrypt
ChaCha20/Poly1305
500 MByte/Sec
425 MByte/Sec
AES-128/GCM
2000 MByte/Sec
1200 MByte/Sec
Key Derivation
SwiftECC uses the X9.63 Key Derivation Function to derive block cipher keying materiel. Please refer [SEC 1] section 3.6.
Four cases are considered:
Signing data and verifying signatures is performed using the ECDSA algorithm. It is possible to generate
deterministic signatures as specified in [RFC-6979] by setting the deterministic parameter to true in the sign operation.
The message digest used in the process is determined from the domain field size as follows:
field size <= 224: SHA2-224
224 < field size <= 256: SHA2-256
256 < field size <= 384: SHA2-384
384 < field size: SHA2-512
BlueECC Compatibility
Signatures created by SwiftECC in the EC256r1, EC384r1 and EC521r1 domains can be verified by IBM's BlueECC product
using curve prime256v1, secp384r1 and secp521r1, respectively. Likewise, signatures created by BlueECC with one of the curves
prime256v1, secp384r1 and secp521r1 can be verified by SwiftECC using domains EC256r1, EC384r1 and EC521r1, respectively.
CryptoKit Compatibility
Signatures created by SwiftECC in the EC256r1, EC384r1 and EC521r1 domains can be verified by Swift CryptoKit
using curve P256, P384 and P521, respectively. Likewise, signatures created by Swift CryptoKit with one of the curves
P256, P384 and P521 can be verified by SwiftECC using domains EC256r1, EC384r1 and EC521r1, respectively.
Example
import SwiftECC
// Get a predefined domain - for example brainpool BP160r1
let domain = Domain.instance(curve: .BP160r1)
// Create your own keys
let (pubKey, privKey) = domain.makeKeyPair()
// See how they look
print(pubKey.asn1)
print(privKey.asn1)
// Store them in PEM format for future use
let pubPEM = pubKey.pem
let privPEM = privKey.pem
let message = "The quick brown fox jumps over the lazy dog!".data(using: .utf8)!
let sig = privKey.sign(msg: message)
let ok = pubKey.verify(signature: sig, msg: message)
print("Signature is", ok ? "good" : "wrong")
Given your own private key and another party's public key, you can generate a byte array that can be used as a symmetric encryption key.
The other party can generate the same byte array by using his own private key and your public key.
SwiftECC supports three mechanisms:
The basic Diffie-Hellman primitive
The X9.63 version specified in [SEC 1] section 3.6.1
The HKDF version specified in [RFC-5869]
Basic Diffie-Hellman Example
import SwiftECC
do {
let domain = Domain.instance(curve: .EC256r1)
// Party A's keys
let (pubA, privA) = domain.makeKeyPair()
// Party B's keys
let (pubB, privB) = domain.makeKeyPair()
let secretA = try privA.sharedSecret(pubKey: pubB)
let secretB = try privB.sharedSecret(pubKey: pubA)
print(secretA)
print(secretB)
} catch {
print("Exception: \(error)")
}
For the key agreement to work, the two parties must agree on which domain, which message digest,
which shared information (possibly none) and which salt (possibly none) to use.
CryptoKit Compatibility
SwiftECC key agreement is compatible with Swift CryptoKit key agreement
in that the EC256r1, EC384r1 and EC521r1 domains correspond to CryptoKit's P256, P384 and P521 curves,
and the SHA2_256, SHA2_384 and SHA2_512 message digests correspond to CryptoKit's SHA256, SHA384 and SHA512 message digests.
The sharedSecret method corresponds to the CryptoKit method sharedSecretFromKeyAgreement
The x963KeyAgreement method corresponds to the CryptoKit method x963DerivedSymmetricKey
The hkdfKeyAgreement method corresponds to the CryptoKit method hkdfDerivedSymmetricKey
To convert CryptoKit keys - e.g. ckPubKey, ckPrivKey - to the corresponding SwiftECC keys:
let eccPubKey = try ECPublicKey(pem: ckPubKey.pemRepresentation)
let eccPrivKey = try ECPrivateKey(pem: ckPrivKey.pemRepresentation)
To convert SwiftECC keys - e.g. eccPubKey, eccPrivKey - to the corresponding CryptoKit keys:
let ckPubKey = try P256.KeyAgreement.PublicKey(pemRepresentation: eccPubKey.pem)
let ckPrivKey = try P256.KeyAgreement.PrivateKey(pemRepresentation: eccPrivKey.pem)
Creating New Domains
You can create your own domains as illustrated by the two examples below.
Example
This is example 3.5 from [GUIDE]. It shows how to make your own prime characteristic domain.
SwiftECC implements the common elliptic curve arithmetic operations:
Point multiplication
Point addition
Point doubling
Point subtraction
Point negation
Is Point on curve?
It is also possible to encode curve points in either compressed- or uncompressed format,
as well as to do the reverse decoding.
Performance
To assess the performance of SwiftECC, the signature generation and verification time and the keypair generation time
was measured on an iMac 2021, Apple M1 chip. The results are shown in the table below - units are milliseconds. The columns mean:
Sign: The time it takes to sign a short message
Verify: The time it takes to verify a signature for a short message
Keypair Generation: The time it takes to generate a public/private keypair
Curve
Sign
Verify
Keypair Generation
brainpoolP160r1
0.7 mSec
1.3 mSec
2.9 mSec
brainpoolP160t1
0.7 mSec
1.4 mSec
2.9 mSec
brainpoolP192r1
0.96 mSec
1.8 mSec
3.9 mSec
brainpoolP192t1
0.96 mSec
1.9 mSec
3.9 mSec
brainpoolP224r1
1.3 mSec
2.6 mSec
5.7 mSec
brainpoolP224t1
1.3 mSec
2.6 mSec
5.7 mSec
brainpoolP256r1
1.7 mSec
3.3 mSec
7.4 mSec
brainpoolP256t1
1.7 mSec
3.3 mSec
7.4 mSec
brainpoolP320r1
2.9 mSec
5.7 mSec
13 mSec
brainpoolP320t1
2.9 mSec
5.5 mSec
13 mSec
brainpoolP384r1
4.5 mSec
8.6 mSec
21 mSec
brainpoolP384t1
4.4 mSec
8.7 mSec
21 mSec
brainpoolP512r1
9.2 mSec
19 mSec
44 mSec
brainpoolP512t1
9.3 mSec
18 mSec
44 mSec
secp192k1
0.96 mSec
1.8 mSec
4.0 mSec
secp192r1
0.96 mSec
1.9 mSec
3.9 mSec
secp224k1
1.3 mSec
2.6 mSec
5.8 mSec
secp224r1
1.3 mSec
2.6 mSec
5.7 mSec
secp256k1
1.7 mSec
3.2 mSec
7.4 mSec
secp256r1
1.7 mSec
3.3 mSec
7.5 mSec
secp384r1
4.5 mSec
8.9 mSec
21 mSec
secp521r1
9.8 mSec
19 mSec
47 mSec
sect163k1
1.2 mSec
2.2 mSec
5.1 mSec
sect163r2
1.2 mSec
2.3 mSec
5.1 mSec
sect233k1
2.3 mSec
4.5 mSec
11 mSec
sect233r1
2.3 mSec
4.5 mSec
11 mSec
sect283k1
3.5 mSec
7.0 mSec
17 mSec
sect283r1
3.5 mSec
7.1 mSec
17 mSec
sect409k1
8.0 mSec
16 mSec
41 mSec
sect409r1
8.0 mSec
16 mSec
42 mSec
sect571k1
17 mSec
35 mSec
92 mSec
sect571r1
17 mSec
34 mSec
92 mSec
Dependencies
The SwiftECC package depends on the ASN1 and BigInt packages
SwiftECC
Contents:
Usage
In your project Package.swift file add a dependency likeSwiftECC requires Swift 5.0. It also requires that the Int and UInt types be 64 bit types. SwiftECC uses Apple’s CryptoKit framework. Therefore, for macOS the version must be at least 10.15, for iOS the version must be at least 13, and for watchOS the version must be at least 8.
Basics
The basic concept in SwiftECC is the Elliptic Curve Domain, represented by the Domain class. Please, refer section 3.1 in [SEC 1] that describes the domain concept in detail.There are 18 predefined NIST domains and 14 predefined Brainpool domains in SwiftECC, and it is possible to create your own characteristic 2, and odd prime characteristic domains.
You need a public key in order to encrypt a message or verify a signature, and you need a private key in order to decrypt a message or sign a message. Given a domain, you can generate public/private key pairs or you can load them from the PEM- or DER encoding of existing keys.
Creating New Keys
For a given domain it is possible to generate a public/private key pair. For example:The private key is simply a random positive integer less than the domain order. The public key is the domain generator point multiplied by the private key. Given a private key, say ‘privKey’, you can generate the corresponding public key, like
Given a domain, say ‘dom’ and a curve point, say ‘pt’, you can generate a public key, like
Loading Existing Keys
It is possible to create keys from their PEM encodings. For examplegiving:
Encrypted Private Keys
Private keys can be encrypted as described in [PKCS#5] using the PBES2 scheme. For example:giving (for example):
The implied encryption parameters are cipher block mode = CBC, iteration count = 2048 and salt = 8 random bytes. The password is simply a byte array, any possible interpretation of it as a string is unspecified. The encrypted private key is compatible with, and is readable by OpenSSL.
Private keys can be created from their PEM encodings in encrypted form. In the example the encrypted private key was created by OpenSSL using the AES-256 cipher in CBC mode with password ‘abcd’.
giving:
SwiftECC can read encrypted private key files provided they were encrypted with one of the ciphers AES-128, AES-192 or AES-256 in CBC mode.
Encryption and Decryption
Encryption and decryption is done using the ECIES algorithm based on the AES block cipher using one of AES-128, AES-192 or AES-256 ciphers, depending on your choice.The following cipher block modes are supported:
Key Derivation
SwiftECC uses the X9.63 Key Derivation Function to derive block cipher keying materiel. Please refer [SEC 1] section 3.6. Six cases are considered:AES-128/GCM block mode
KDF generates 32 bytes.AES encryption/decryption key = bytes 0 ..< 16
Nonce = bytes 16 ..< 32
AES-192/GCM block mode
KDF generates 40 bytes.AES encryption/decryption key = bytes 0 ..< 24
Nonce = bytes 24 ..< 40
AES-256/GCM block mode
KDF generates 48 bytes.AES encryption/decryption key = bytes 0 ..< 32
Nonce = bytes 32 ..< 48
AES-128/Non-GCM block mode
KDF generates 48 bytes.AES encryption/decryption key = bytes 0 ..< 16
HMAC key = bytes 16 ..< 48
AES-192/Non-GCM block mode
KDF generates 56 bytes.AES encryption/decryption key = bytes 0 ..< 24
HMAC key = bytes 24 ..< 56
AES-256/Non-GCM block mode
KDF generates 64 bytes.AES encryption/decryption key = bytes 0 ..< 32
HMAC key = bytes 32 ..< 64
The AES key and HMAC key can be retrieved with the ECPrivateKey method ‘getKeyAndMac’.
For block modes CBC, CFB, CTR, and OFB the initialization vector (IV) is 16 zero bytes.
BlueECC Compatibility
Data encrypted by SwiftECC in the EC256r1 domain with AES128/GCM, in the EC384r1 domain with AES256/GCM and in the EC521r1 domain with AES256/GCM can be decrypted with IBM's BlueECC product using curve prime256v1, secp384r1, and secp521r1, respectively. Likewise, data encrypted by BlueECC with curve prime256v1, secp384r1 and secp521, can be decrypted by SwiftECC using EC256r1 with AES128/GCM, EC384r1 with AES256/GCM and EC521r1 with AES256/GCM, respectively.Example
giving
AEAD Encryption and Decryption
Authenticated Encryption with Associated Data (AEAD) is implemented with the ChaCha20/Poly1305 algorithm and the AES/GCM algorithm. Both implementations use Apple's CryptoKit framework, that takes advantage of hardware support for the AES and GCM algorithms.Example
giving:
The encryption and decryption speed for domain EC256k1 (the bitcoin domain) measured on an iMac 2021, Apple M1 chip are shown below - units are Megabytes per second.
Key Derivation
SwiftECC uses the X9.63 Key Derivation Function to derive block cipher keying materiel. Please refer [SEC 1] section 3.6. Four cases are considered:ChaCha20/Poly1305
KDF generates 44 bytes.Encryption/decryption key = bytes 0 ..< 32
Nonce = bytes 32 ..< 44
AES-128/GCM
KDF generates 28 bytes.AES encryption/decryption key = bytes 0 ..< 16
Nonce = bytes 16 ..< 28
AES-192/GCM
KDF generates 36 bytes.AES encryption/decryption key = bytes 0 ..< 24
Nonce = bytes 24 ..< 36
AES-256/GCM
KDF generates 44 bytes.AES encryption/decryption key = bytes 0 ..< 32
Nonce = bytes 32 ..< 44
Signing and Verifying
Signing data and verifying signatures is performed using the ECDSA algorithm. It is possible to generate deterministic signatures as specified in [RFC-6979] by setting the deterministic parameter to true in the sign operation.The message digest used in the process is determined from the domain field size as follows:
BlueECC Compatibility
Signatures created by SwiftECC in the EC256r1, EC384r1 and EC521r1 domains can be verified by IBM's BlueECC product using curve prime256v1, secp384r1 and secp521r1, respectively. Likewise, signatures created by BlueECC with one of the curves prime256v1, secp384r1 and secp521r1 can be verified by SwiftECC using domains EC256r1, EC384r1 and EC521r1, respectively.CryptoKit Compatibility
Signatures created by SwiftECC in the EC256r1, EC384r1 and EC521r1 domains can be verified by Swift CryptoKit using curve P256, P384 and P521, respectively. Likewise, signatures created by Swift CryptoKit with one of the curves P256, P384 and P521 can be verified by SwiftECC using domains EC256r1, EC384r1 and EC521r1, respectively.Example
giving (for example):
Secret Key Agreement
Given your own private key and another party's public key, you can generate a byte array that can be used as a symmetric encryption key. The other party can generate the same byte array by using his own private key and your public key.SwiftECC supports three mechanisms:
Basic Diffie-Hellman Example
giving (for example):
X9.63 Example
giving (for example):
For the key agreement to work, the two parties must agree on which domain, which message digest and which shared information (possibly none) to use.
HKDF Example
giving (for example):
For the key agreement to work, the two parties must agree on which domain, which message digest, which shared information (possibly none) and which salt (possibly none) to use.
CryptoKit Compatibility
SwiftECC key agreement is compatible with Swift CryptoKit key agreement in that the EC256r1, EC384r1 and EC521r1 domains correspond to CryptoKit's P256, P384 and P521 curves, and the SHA2_256, SHA2_384 and SHA2_512 message digests correspond to CryptoKit's SHA256, SHA384 and SHA512 message digests.To convert CryptoKit keys - e.g. ckPubKey, ckPrivKey - to the corresponding SwiftECC keys:
To convert SwiftECC keys - e.g. eccPubKey, eccPrivKey - to the corresponding CryptoKit keys:
Creating New Domains
You can create your own domains as illustrated by the two examples below.Example
This is example 3.5 from [GUIDE]. It shows how to make your own prime characteristic domain.
giving
Example
This is example 3.6 from [GUIDE]. It shows how to make your own characteristic 2 domain.
giving
Elliptic Curve Arithmetic
SwiftECC implements the common elliptic curve arithmetic operations:Performance
To assess the performance of SwiftECC, the signature generation and verification time and the keypair generation time was measured on an iMac 2021, Apple M1 chip. The results are shown in the table below - units are milliseconds. The columns mean:Dependencies
The SwiftECC package depends on the ASN1 and BigInt packages
References
Algorithms from the following books and papers have been used in the implementation. There are references in the source code where appropriate.
Acknowledgement
The AES block cipher implementation is essentially a translation to Swift of the Go Language implementation of AES.