SwiftECC provides elliptic curve cryptography in Swift. This encompasses:
dependencies: [
.package(url: "https://github.com/leif-ibsen/SwiftECC", from: "1.0.2"),
]
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.
Block Mode | Encrypt | Decrypt |
---|---|---|
GCM | 26 MByte/Sec | 26 MByte/Sec |
ECB | 19 MByte/Sec | 19 MByte/Sec |
CBC | 14 MByte/Sec | 16 MByte/Sec |
CFB | 20 MByte/Sec | 20 MByte/Sec |
CTR | 21 MByte/Sec | 21 MByte/Sec |
OFB | 25 MByte/Sec | 26 MByte/Sec |
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!
The message digest used in the process is determined from the domain field size as follows:
import SwiftECC
// Get a predefined domain - for example brainpoolP160r1
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
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 =", domain.add(p1, p2))
print("p1 * 2 =", domain.multiply(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
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
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 =", domain.add(p1, p2))
print("p1 * 2 =", domain.multiply(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
Encryption/decryption key = bytes 0 ..< 16 Initialization vector = bytes 16 ..< 32
Encryption/decryption key = bytes 0 ..< 24 Initialization vector = bytes 24 ..< 40
Encryption/decryption key = bytes 0 ..< 32 Initialization vector = bytes 32 ..< 48
Encryption/decryption key = bytes 0 ..< 16 HMAC key = bytes 16 ..< 48
Encryption/decryption key = bytes 0 ..< 24 HMAC key = bytes 24 ..< 56
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.
Curve | Sign | Verify | Keypair Generation |
---|---|---|---|
brainpoolP160r1 | 1.4 mSec | 2.5 mSec | 5.1 mSec |
brainpoolP160t1 | 1.4 mSec | 2.4 mSec | 5.1 mSec |
brainpoolP192r1 | 1.7 mSec | 3.0 mSec | 6.3 mSec |
brainpoolP192t1 | 1.7 mSec | 3.1 mSec | 6.4 mSec |
brainpoolP224r1 | 2.3 mSec | 4.3 mSec | 9.4 mSec |
brainpoolP224t1 | 2.3 mSec | 4.3 mSec | 9.4 mSec |
brainpoolP256r1 | 2.7 mSec | 5.1 mSec | 11 mSec |
brainpoolP256t1 | 2.7 mSec | 5.0 mSec | 11 mSec |
brainpoolP320r1 | 4.2 mSec | 7.7 mSec | 18 mSec |
brainpoolP320t1 | 4.2 mSec | 7.8 mSec | 18 mSec |
brainpoolP384r1 | 6.0 mSec | 11 mSec | 27 mSec |
brainpoolP384t1 | 6.1 mSec | 11 mSec | 27 mSec |
brainpoolP512r1 | 11 mSec | 21 mSec | 52 mSec |
brainpoolP512t1 | 11 mSec | 21 mSec | 52 mSec |
secp192k1 | 1.6 mSec | 3.0 mSec | 6.2 mSec |
secp192r1 | 1.6 mSec | 3.0 mSec | 6.3 mSec |
secp224k1 | 2.3 mSec | 4.2 mSec | 9.5 mSec |
secp224r1 | 2.3 mSec | 4.2 mSec | 9.4 mSec |
secp256k1 | 2.7 mSec | 5.2 mSec | 11 mSec |
secp256r1 | 2.7 mSec | 5.0 mSec | 11 mSec |
secp384r1 | 6.1 mSec | 11 mSec | 27 mSec |
secp521r1 | 11 mSec | 21 mSec | 51 mSec |
sect163k1 | 1.9 mSec | 3.6 mSec | 7.8 mSec |
sect163r2 | 1.9 mSec | 3.6 mSec | 7.9 mSec |
sect233k1 | 3.5 mSec | 6.9 mSec | 15 mSec |
sect233r1 | 3.5 mSec | 6.4 mSec | 15 mSec |
sect283k1 | 5.3 mSec | 10 mSec | 25 mSec |
sect283r1 | 5.3 mSec | 9.8 mSec | 25 mSec |
sect409k1 | 11 mSec | 22 mSec | 57 mSec |
sect409r1 | 11 mSec | 21 mSec | 58 mSec |
sect571k1 | 23 mSec | 45 mSec | 126 mSec |
sect571r1 | 23 mSec | 46 mSec | 126 mSec |
SwiftECC requires Swift 5.0.
The SwiftECC package depends on the ASN1 and BigInt packages
dependencies: [
.package(url: "https://github.com/leif-ibsen/ASN1", from: "1.2.1"),
.package(url: "https://github.com/leif-ibsen/BigInt", from: "1.1.2"),
],
Algorithms from the following books and papers have been used in the implementation. There are references in the source code where appropriate.
link |
Stars: 2 |
Last commit: 2 hours ago |
Fixed issues #3 and #4 by adding an optional parameter to the ECPublicKey.verify function
Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco