Swiftpack.co - Package - leif-ibsen/AEC

Description

Release 3 of AEC provides elliptic curve cryptography in Swift. This encompasses:

  • Encryption and decryption using the ECIES algorithm based on AES/GCM block cipher
  • Signature signing and verifying using the ECDSA algorithm
  • General elliptic curve arithmetic

The basic concept in AEC is the Elliptic Curve Domain, represented by the Domain class. Section 3.1 in SEC 1 describes the domain concept in detail. There are 18 predefined NIST domains and 14 predefined Brainpool domains in AEC, and it is possible to create your own characteristic 2, and odd prime characteristic domains.

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/GCM. The algorithm uses one of AES-128, AES-192 or AES-256 block ciphers, depending on your choice.
The encryption and decryption speed for domain EC256k1 (the bitcoin domain) was measured on a MacBook Pro 2018, 2,2 GHz 6-Core Intel Core i7. The results are shown below. The numbers are valid when encrypting or decrypting more than a few kilobytes of data. For smaller data sizes the speed will be lower because the computationally heavy elliptic curve operations are the same no matter the amount of data being encrypted.
  • AES128: 280 kB/Sec
  • AES192: 240 kB/Sec
  • AES256: 220 kB/Sec

BlueECC compatibility

Data encrypted by AEC in the EC256r1 domain with AES128, in the EC384r1 domain with AES256 and in the EC521r1 domain with AES256 can be decrypted with IBM's BlueECC product with the prime256v1 curve, the secp384r1 curve and the secp521r1 curve. Likewise, data encrypted by BlueECC with the prime256v1 curve, the secp384r1 curve and the secp521 curve can be decrypted by AEC using EC256r1 with AES128, EC384r1 with AES256 and EC521r1 with AES256.

Example

import AEC

// 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 encrypted = pubKey.encrypt(msg: text.data(using: .utf8)!, cipher: .AES128)
	let decrypted = try String(data: privKey.decrypt(msg: encrypted, cipher: .AES128), encoding: .utf8)!
	print(decrypted)
} catch {
	print("\(error)")
}

giving

The quick brown fox jumps over the lazy dog!

Signing and verifying

Signing data and verifying signatures is done using the ECDSA algorithm. The message digest used 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 AEC 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 AEC using domains EC256r1, EC384r1 and EC521r1, respectively.

Example

import AEC

// Get a predefined domain - for example brainpoolP160r1

let domain = Domain.instance(.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 = Base64.pemEncode(pubKey.asn1.encode(), "PUBLIC KEY")
let privPEM = Base64.pemEncode(privKey.asn1.encode(), "EC PRIVATE KEY")

let text = "The quick brown fox jumps over the lazy dog!"

let signature = privKey.sign(msg: text.data(using: .utf8)!)
let ok = pubKey.verify(signature: signature, msg: text.data(using: .utf8)!)
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

Creating domains

You can create your own domains as illustrated by the two examples below.

Example

This is example 3.5 from 'Guide to Elliptic Curve Cryptography'. It shows how to make your own prime characteristic domain.

import AEC
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.asn1(false))

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 to Elliptic Curve Cryptography'. It shows how to make your own characteristic 2 domain.

import AEC
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.asn1(false))

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

EC arithmetic

AEC implements the common elliptic curve arithmetic operations. They are: Point multiplication, addition, doubling, subtraction, negation and is point on curve? It is also possible to encode curve points in either compressed- or uncompressed format, as well as do the reverse decoding.

Performance

To assess the performance of AEC arithmetic, the execution times for point multiplication were measured on a MacBook Pro 2018, 2,2 GHz 6-Core Intel Core i7. The domain generator point was multiplied by the curve order (giving point of infinity as result). The results are in the table below. It shows the EC domain and the time the multiplication took (in milliseconds).
EC DomainExecution time
brainpoolP160r12.5 mSec
brainpoolP160t12.5 mSec
brainpoolP192r13.1 mSec
brainpoolP192t12.9 mSec
brainpoolP224r14.5 mSec
brainpoolP224t14.5 mSec
brainpoolP256r15.4 mSec
brainpoolP256t15.5 mSec
brainpoolP320r18.2 mSec
brainpoolP320t18.1 mSec
brainpoolP384r111 mSec
brainpoolP384t111 mSec
brainpoolP512r121 mSec
brainpoolP512t121 mSec
secp192k14.6 mSec
secp192r14.6 mSec
secp224k12.7 mSec
secp224r16.3 mSec
secp256k17.3 mSec
secp256r16.7 mSec
secp384r117 mSec
secp521r132 mSec
sect163k11.9 mSec
sect163r22.1 mSec
sect233k14.9 mSec
sect233r14.0 mSec
sect283k115 mSec
sect283r115 mSec
sect409k135 mSec
sect409r112 mSec
sect571k126 mSec
sect571r175 mSec

Dependencies

AEC requires Swift 5.0.

The AEC package depends on the ASN1 and BigInt packages

dependencies: [
    .package(url: "https://github.com/leif-ibsen/ASN1", from: "1.2.0"),
    .package(url: "https://github.com/leif-ibsen/BigInt", from: "1.1.0"),
],

References

Algorithms from the following books and papers have been used in the implementation. There are references in the source code where appropriate.

  • Crandall and Pomerance: Prime Numbers - A Computational Perspective. Second Edition, Springer 2005
  • Hankerson, Menezes, Vanstone: Guide to Elliptic Curve Cryptography. Springer 2004
  • Henry S. Warren, Jr.: Montgomery Multiplication, July 2012
  • E. Savacs, C.K. Koc: The Montgomery Modular Inverse - Revisited, July 2000
  • Standards for Efficient Cryptography 1 (SEC 1), Certicom Corp. 2009
  • Standards for Efficient Cryptography 2 (SEC 2), Certicom Corp. 2010
  • X9.62 - Public Key Cryptography For The Financial Services Industry, 1998

Github

link
Stars: 1

Dependencies

Used By

Total: 0

Releases

Release 3.1.1 - 2020-01-22 13:15:21

Fixed a bug in the 'negate' function. Faster point multiplication.

Release 3.1.0 - 2020-01-15 09:06:04

AES/GCM KDF bugfix

Release 3.0.0 - 2020-01-14 13:26:19

Elliptic curve cryptography (ECIES, ECDSA) in Swift

2.1.0 - 2019-12-13 15:17:48

Performance improvement: Twice as fast point multiplication

2.0.0 - 2019-12-08 11:44:59

Release 2.0.0 supports compressed point encoding and decoding

1.1.1 - 2019-11-28 12:54:49

Changed dependencies to BigInt 1.0.1 and ASN1 1.1.1

1.1.0 - 2019-11-18 12:14:09

Changes from release 1.0.0:

ASN1 dependency changed from 1.0.0 to 1.1.0 Improved documentation

- 2019-10-31 16:17:57