SwiftECC
Contents:
SwiftECC provides elliptic curve cryptography in Swift. This encompasses:- Encryption and decryption using the ECIES algorithm based on the AES block 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 dependencies: [
.package(url: "https://github.com/leif-ibsen/SwiftECC", from: "2.2.0"),
]
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.
Encryption and Decryption
Encryption and decryption is done using the ECIES algorithm based on AES block cipher. The algorithm uses 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
| 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 |
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!
Signing and Verifying
Signing data and verifying signatures is performed using the ECDSA algorithm. It is possible to generate deterministic signatures as specificed 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")
giving (for example):
Sequence (2):
Sequence (2):
Object Identifier: 1.2.840.10045.2.1
Object Identifier: 1.3.36.3.3.2.8.1.1.1
Bit String (328): 00000100 00000011 00000111 00110011 01010100 00000001 10111100 01101111 10100001 01001000 11101000 01111100 10001111 00000110 00010010 11100111 11111010 10010001 00100100 01001000 11000110 01110001 00110100 01001000 10011110 01011110 11000000 10010001 01000110 01011010 01001110 01110000 00011011 01010111 10101011 01101010 00011011 01101100 01100100 01000100 01111101
Sequence (4):
Integer: 1
Octet String (20): 32 96 e0 c4 d7 f5 cb 03 0c 95 63 b1 a2 c1 2f 64 4c dc d6 4c
[0]:
Object Identifier: 1.3.36.3.3.2.8.1.1.1
[1]:
Bit String (328): 00000100 00000011 00000111 00110011 01010100 00000001 10111100 01101111 10100001 01001000 11101000 01111100 10001111 00000110 00010010 11100111 11111010 10010001 00100100 01001000 11000110 01110001 00110100 01001000 10011110 01011110 11000000 10010001 01000110 01011010 01001110 01110000 00011011 01010111 10101011 01101010 00011011 01101100 01100100 01000100 01111101
Signature is good
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.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 info: Bytes = [1, 2, 3]
let secretA = try privA.keyAgreement(pubKey: pubB, length: 16, md: .SHA2_256, sharedInfo: info)
let secretB = try privB.keyAgreement(pubKey: pubA, length: 16, md: .SHA2_256, sharedInfo: info)
print(secretA)
print(secretB)
} catch {
print("Exception: \(error)")
}
giving (for example):
[92, 161, 137, 44, 47, 30, 6, 26, 43, 183, 199, 130, 19, 254, 232, 106]
[92, 161, 137, 44, 47, 30, 6, 26, 43, 183, 199, 130, 19, 254, 232, 106]
For the key agreement to work, the two parties must agree on which domain to use, which message digest to use and which shared information (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 a CryptoKit public key - e.g. 'pubKey' - to the corresponding SwiftECC public key:
let eccKey = try ECPublickey(pem: pubKey.pemRepresentation)
To convert a SwiftECC public key - e.g. 'pubKey' - to the corresponding CryptoKit public key:
let ckKey = try P256.KeyAgreement.PublicKey(pemRepresentation: pubKey.pem)
Creating 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.
import SwiftECC
import BigInt
// Create the domain
let domain = try Domain.instance(name: "EC29", p: BInt(29), a: BInt(4), b: BInt(20), gx: BInt(1), gy: BInt(5), order: BInt(37), cofactor: 1)
let p1 = Point(BInt(5), BInt(22))
let p2 = Point(BInt(16), BInt(27))
print("p1 + p2 =", try domain.addPoints(p1, p2))
print("p1 * 2 =", try domain.multiplyPoint(p1, BInt(2)))
// Inspect the domain - please refer [SEC 1] appendix C.2
print(domain.asn1Explicit())
giving
p1 + p2 = Point(13, 6)
p1 * 2 = Point(14, 6)
Sequence (6):
Integer: 1
Sequence (2):
Object Identifier: 1.2.840.10045.1.1
Integer: 29
Sequence (2):
Octet String (1): 04
Octet String (1): 14
Octet String (3): 04 01 05
Integer: 37
Integer: 1
Example
This is example 3.6 from [GUIDE]. It shows how to make your own characteristic 2 domain.
import SwiftECC
import BigInt
// Reduction polynomial for x^4 + x^1 + 1
let rp = RP(4, 1)
// Create the domain
let domain = try Domain.instance(name: "EC4", rp: rp, a: BInt(8), b: BInt(9), gx: BInt(1), gy: BInt(1), order: BInt(22), cofactor: 2)
let p1 = Point(BInt(2), BInt(15))
let p2 = Point(BInt(12), BInt(12))
print("p1 + p2 =", try domain.addPoints(p1, p2))
print("p1 * 2 =", try domain.multiplyPoint(p1, BInt(2)))
// Inspect the domain - please refer [SEC 1] appendix C.2
print(domain.asn1Explicit())
giving
p1 + p2 = Point(1, 1)
p1 * 2 = Point(11, 2)
Sequence (6):
Integer: 1
Sequence (2):
Object Identifier: 1.2.840.10045.1.2
Sequence (2):
Integer: 4
Integer: 1
Sequence (2):
Octet String (1): 08
Octet String (1): 09
Octet String (3): 04 01 01
Integer: 22
Integer: 2
Elliptic Curve Arithmetic
SwiftECC implements the common elliptic curve arithmetic operations:- Point multiplication
- Point addition
- Point doubling
- Point subtraction
- Point negation
- Is Point on curve?
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.Encryption/decryption key = bytes 0 ..< 16
Initialization vector = bytes 16 ..< 32
AES-192/GCM block mode
KDF generates 40 bytes.Encryption/decryption key = bytes 0 ..< 24
Initialization vector = bytes 24 ..< 40
AES-256/GCM block mode
KDF generates 48 bytes.Encryption/decryption key = bytes 0 ..< 32
Initialization vector = bytes 32 ..< 48
AES-128/Non-GCM block mode
KDF generates 48 bytes.Encryption/decryption key = bytes 0 ..< 16
HMAC key = bytes 16 ..< 48
AES-192/Non-GCM block mode
KDF generates 56 bytes.Encryption/decryption key = bytes 0 ..< 24
HMAC key = bytes 24 ..< 56
AES-256/Non-GCM block mode
KDF generates 64 bytes.Encryption/decryption key = bytes 0 ..< 32
HMAC key = bytes 32 ..< 64
For block modes CBC, CFB, CTR, and OFB the initialization vector (IV) is 16 zero bytes.
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
dependencies: [
.package(url: "https://github.com/leif-ibsen/ASN1", from: "2.0.0"),
.package(url: "https://github.com/leif-ibsen/BigInt", from: "1.2.11"),
],
References
Algorithms from the following books and papers have been used in the implementation. There are references in the source code where appropriate.
- [FIPS 180-4] - FIPS PUB 180-4 - Secure Hash Standard (SHS), August 2015
- [GCM] - The Galois/Counter Mode of Operation (GCM)
- [GUIDE] - Hankerson, Menezes, Vanstone: Guide to Elliptic Curve Cryptography. Springer 2004
- [KNUTH] - Donald E. Knuth: Seminumerical Algorithms. Addison-Wesley 1971
- [NIST] - NIST Special Publication 800-38D, November 2007
- [RFC-6979] - Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA), August 2013
- [SAVACS] - E. Savacs, C.K. Koc: The Montgomery Modular Inverse - Revisited, July 2000
- [SEC 1] - Standards for Efficient Cryptography 1 (SEC 1), Certicom Corp. 2009
- [SEC 2] - Standards for Efficient Cryptography 2 (SEC 2), Certicom Corp. 2010
- [WARREN] - Henry S. Warren, Jr.: Montgomery Multiplication, July 2012
- [X9.62] - X9.62 - Public Key Cryptography For The Financial Services Industry, 1998