Skip to content

Commit 65b27fd

Browse files
committed
Release 2.1.0
1 parent 7da0ff4 commit 65b27fd

File tree

679 files changed

+1921
-21041
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

679 files changed

+1921
-21041
lines changed

Package.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ let package = Package(
1414
],
1515
dependencies: [
1616
// Dependencies declare other packages that this package depends on.
17-
.package(url: "https://github.com/leif-ibsen/BigInt", from: "1.14.0"),
18-
.package(url: "https://github.com/leif-ibsen/ASN1", from: "2.2.0"),
19-
.package(url: "https://github.com/leif-ibsen/Digest", from: "1.1.0"),
17+
.package(url: "https://github.com/leif-ibsen/BigInt", from: "1.15.0"),
18+
.package(url: "https://github.com/leif-ibsen/ASN1", from: "2.3.0"),
19+
.package(url: "https://github.com/leif-ibsen/Digest", from: "1.2.0"),
2020
],
2121
targets: [
2222
// Targets are the basic building blocks of a package. A target can define a module or a test suite.

README.md

+6-295
Original file line numberDiff line numberDiff line change
@@ -1,310 +1,21 @@
11
<h2><b>SwiftHPKE</b></h2>
2-
<h3><b>Contents:</b></h3>
3-
<ul>
4-
<li><a href="#use">Usage</a></li>
5-
<li><a href="#basic">Basics</a></li>
6-
<li><a href="#basic1">Creating Public and Private Keys</a></li>
7-
<li><a href="#basic2">Loading Existing Keys</a></li>
8-
<li><a href="#basic6">Encryption and Decryption</a></li>
9-
<li><a href="#basic7">Secret Export</a></li>
10-
<li><a href="#basic8">CryptoKit Compatibility</a></li>
11-
<li><a href="#basic9">Performance</a></li>
12-
<li><a href="#dep">Dependencies</a></li>
13-
<li><a href="#ref">References</a></li>
14-
</ul>
15-
SwiftHPKE implements the Hybrid Public Key Encryption standard as defined in RFC 9180.
16-
<h2 id="use"><b>Usage</b></h2>
17-
In your project Package.swift file add a dependency like<br/>
18-
19-
dependencies: [
20-
.package(url: "https://github.com/leif-ibsen/SwiftHPKE", from: "2.0.0"),
21-
]
22-
SwiftHPKE requires Swift 5.0. It also requires that the Int and UInt types be 64 bit types.
23-
SwiftHPKE uses Apple's CryptoKit framework. Therefore, for macOS the version must be at least 10.15,
24-
for iOS the version must be at least 13, and for watchOS the version must be at least 8.
25-
26-
<h2 id="basic"><b>Basics</b></h2>
27-
The basic concepts in SwiftHPKE are CipherSuite, Sender and Recipient, represented by the
28-
*CipherSuite* structure and the *Sender* and *Recipient* classes.
292

30-
A CipherSuite combines a *Key Encapsulation Mechanism* (KEM), a *Key Derivation Function* (KDF)
31-
and a *Authenticated Encryption with Associated Data* (AEAD) algorithm.
32-
33-
There are 5 different KEM's, 3 different KDF's and 4 different AEAD's giving 60 CipherSuite combinations.
34-
35-
A *Sender* is based on a specific *CipherSuite* and a *Sender* instance can encrypt (seal) a sequence of plaintexts
36-
in one of four different modes:
3+
SwiftHPKE implements the Hybrid Public Key Encryption standard as defined in RFC 9180
4+
including all four modes of operation:
375
<ul>
386
<li>Base mode</li>
397
<li>Preshared key mode</li>
408
<li>Authenticated mode</li>
419
<li>Authenticated, preshared key mode</li>
4210
</ul>
43-
A *Recipient* is also based on a specific *CipherSuite* and a *Recipient* instance can decrypt (open)
44-
a sequence of ciphertexts in the four modes shown above.
45-
46-
A *CipherSuite* instance can encrypt (seal) a single plaintext message and decrypt (open) a single
47-
ciphertext message without the need for a *Sender* instance and a *Recipient* instance.
48-
<h2 id="basic1"><b>Creating Public and Private Keys</b></h2>
49-
Given a *CipherSuite* instance it is possible to generate new public- and private keys.
50-
<h3><b>Example</b></h3>
51-
52-
import SwiftHPKE
53-
54-
let suite = CipherSuite(kem: .X25519, kdf: .KDF256, aead: .CHACHAPOLY)
55-
let (pubKey, privKey) = try suite.makeKeyPair()
56-
57-
// See the key ASN1 structures
58-
59-
print(pubKey)
60-
print(privKey)
61-
62-
giving (for example):
63-
64-
Sequence (2):
65-
Sequence (1):
66-
Object Identifier: 1.3.101.110
67-
Bit String (256): 11100111 11100111 00010111 11110101 10101000 10010101 01001010 00100010 00011010 10001001 11001011 11010001 11101101 10000101 01110101 11011111 11010110 00001101 01001110 10100100 00111011 00110100 01110000 01011000 00111111 01011011 10001010 11111010 01101000 10010011 10100001 00001101
68-
69-
Sequence (3):
70-
Integer: 0
71-
Sequence (1):
72-
Object Identifier: 1.3.101.110
73-
Octet String (34): 04 20 b0 e5 94 7d f8 72 04 8f 90 79 5f d5 b7 e4 6e ca 56 18 58 30 2e 4e 79 83 d6 46 bb 42 70 2a 34 68
74-
75-
<h2 id="basic2"><b>Loading Existing Keys</b></h2>
76-
It is possible to load existing keys from their PEM encodings or DER encodings.
77-
<h3><b>Example</b></h3>
78-
79-
import SwiftHPKE
80-
81-
// Public key encoding - curve P384
82-
let pubKeyPem =
83-
"""
84-
-----BEGIN PUBLIC KEY-----
85-
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEQW/MahMwMTFjwY95uOEdfBVC7HrQhTGG
86-
TwxiPlgDiARqC6y6EQ1Ajkuhe4A02WOltRYQRXKytzspOR25UfgtagURAwxVFYzR
87-
9cmi6FRmvvq/Tsigd/dAi4FNjniR7/Pg
88-
-----END PUBLIC KEY-----
89-
"""
90-
let pubKey = try PublicKey(pem: pubKeyPem)
91-
92-
// Private key encoding - curve P384
93-
let privKeyPem =
94-
"""
95-
-----BEGIN PRIVATE KEY-----
96-
MIG/AgEAMBAGByqGSM49AgEGBSuBBAAiBIGnMIGkAgEBBDBmpNziSYmGoWwl7apJ
97-
M9ZdDBxkJqmxMScHGXG45ZQXSv7fIuJlsSwxK76nUiiO7gigBwYFK4EEACKhZANi
98-
AARBb8xqEzAxMWPBj3m44R18FULsetCFMYZPDGI+WAOIBGoLrLoRDUCOS6F7gDTZ
99-
Y6W1FhBFcrK3Oyk5HblR+C1qBREDDFUVjNH1yaLoVGa++r9OyKB390CLgU2OeJHv
100-
8+A=
101-
-----END PRIVATE KEY-----
102-
"""
103-
let privKey = try PrivateKey(pem: privKeyPem)
104-
105-
// See the key ASN1 structures
106-
107-
print(pubKey)
108-
print(privKey)
109-
110-
giving:
111-
112-
Sequence (2):
113-
Sequence (2):
114-
Object Identifier: 1.2.840.10045.2.1
115-
Object Identifier: 1.3.132.0.34
116-
Bit String (776): 00000100 01000001 01101111 11001100 01101010 00010011 00110000 00110001 00110001 01100011 11000001 10001111 01111001 10111000 11100001 00011101 01111100 00010101 01000010 11101100 01111010 11010000 10000101 00110001 10000110 01001111 00001100 01100010 00111110 01011000 00000011 10001000 00000100 01101010 00001011 10101100 10111010 00010001 00001101 01000000 10001110 01001011 10100001 01111011 10000000 00110100 11011001 01100011 10100101 10110101 00010110 00010000 01000101 01110010 10110010 10110111 00111011 00101001 00111001 00011101 10111001 01010001 11111000 00101101 01101010 00000101 00010001 00000011 00001100 01010101 00010101 10001100 11010001 11110101 11001001 10100010 11101000 01010100 01100110 10111110 11111010 10111111 01001110 11001000 10100000 01110111 11110111 01000000 10001011 10000001 01001101 10001110 01111000 10010001 11101111 11110011 11100000
117-
118-
Sequence (3):
119-
Integer: 0
120-
Sequence (2):
121-
Object Identifier: 1.2.840.10045.2.1
122-
Object Identifier: 1.3.132.0.34
123-
Octet String (167): 30 81 a4 02 01 01 04 30 66 a4 dc e2 49 89 86 a1 6c 25 ed aa 49 33 d6 5d 0c 1c 64 26 a9 b1 31 27 07 19 71 b8 e5 94 17 4a fe df 22 e2 65 b1 2c 31 2b be a7 52 28 8e ee 08 a0 07 06 05 2b 81 04 00 22 a1 64 03 62 00 04 41 6f cc 6a 13 30 31 31 63 c1 8f 79 b8 e1 1d 7c 15 42 ec 7a d0 85 31 86 4f 0c 62 3e 58 03 88 04 6a 0b ac ba 11 0d 40 8e 4b a1 7b 80 34 d9 63 a5 b5 16 10 45 72 b2 b7 3b 29 39 1d b9 51 f8 2d 6a 05 11 03 0c 55 15 8c d1 f5 c9 a2 e8 54 66 be fa bf 4e c8 a0 77 f7 40 8b 81 4d 8e 78 91 ef f3 e0
124-
125-
<h2 id="basic6"><b>Encryption and Decryption</b></h2>
126-
A *CipherSuite* instance can encrypt (seal) a single plaintext message and decrypt (open) a single ciphertext message.
127-
128-
<h3><b>Example</b></h3>
129-
130-
// Encryption and decryption of a single message in base mode
131-
132-
import SwiftHPKE
133-
134-
// The CipherSuite to use
135-
let theSuite = CipherSuite(kem: .X448, kdf: .KDF512, aead: .AESGCM256)
136-
137-
// The recipient keys
138-
let (recipientPub, recipientPriv) = try theSuite.makeKeyPair()
139-
140-
let plainText = Bytes("Hi, there".utf8)
141-
let (encapsulatedKey, cipherText) = try theSuite.seal(publicKey: recipientPub, info: [1, 2, 3], pt: plainText, aad: [4, 5, 6])
142-
let decrypted = try theSuite.open(privateKey: recipientPriv, info: [1, 2, 3], ct: cipherText, aad: [4, 5, 6], encap: encapsulatedKey)
143-
print(String(bytes: decrypted, encoding: .utf8)!)
144-
145-
giving:
146-
147-
Hi, there
148-
149-
Using a *Sender* instance and a *Recipient* instance it is possible to encrypt a sequence of plaintext messages
150-
and decrypt a sequence of ciphertext messages.
151-
<h3><b>Example</b></h3>
152-
153-
// Encryption and decryption of several messages in authenticated mode
154-
155-
import SwiftHPKE
156-
157-
// The CipherSuite to use
158-
let theSuite = CipherSuite(kem: .P384, kdf: .KDF384, aead: .CHACHAPOLY)
159-
160-
let plainText1 = Bytes("Hi, there 1".utf8)
161-
let plainText2 = Bytes("Hi, there 2".utf8)
162-
let plainText3 = Bytes("Hi, there 3".utf8)
163-
164-
// The Sender and Recipient keys
165-
let (senderPub, senderPriv) = try theSuite.makeKeyPair()
166-
let (recipientPub, recipientPriv) = try theSuite.makeKeyPair()
167-
168-
// Create the Sender instance
169-
let sender = try Sender(suite: theSuite, publicKey: recipientPub, info: [1, 2, 3], authentication: senderPriv)
170-
171-
let cipherText1 = try sender.seal(pt: plainText1, aad: [4, 5])
172-
let cipherText2 = try sender.seal(pt: plainText2, aad: [6, 7])
173-
let cipherText3 = try sender.seal(pt: plainText3, aad: [8, 9])
174-
175-
// Create the Recipient instance
176-
let recipient = try Recipient(suite: theSuite, privateKey: recipientPriv, info: [1, 2, 3], authentication: senderPub, encap: sender.encapsulatedKey)
177-
178-
let decrypted1 = try recipient.open(ct: cipherText1, aad: [4, 5])
179-
let decrypted2 = try recipient.open(ct: cipherText2, aad: [6, 7])
180-
let decrypted3 = try recipient.open(ct: cipherText3, aad: [8, 9])
181-
182-
print(String(bytes: decrypted1, encoding: .utf8)!)
183-
print(String(bytes: decrypted2, encoding: .utf8)!)
184-
print(String(bytes: decrypted3, encoding: .utf8)!)
185-
186-
giving:
187-
188-
Hi, there 1
189-
Hi, there 2
190-
Hi, there 3
191-
192-
<h2 id="basic7"><b>Secret Export</b></h2>
193-
Given the recipients public key, a sender can generate a secret that only the recipient can know.
194-
<h3><b>Example 1</b></h3>
195-
196-
import SwiftHPKE
197-
198-
// The aead need not be .EXPORTONLY, any aead will work
199-
200-
let theSuite = CipherSuite(kem: .P256, kdf: .KDF256, aead: .EXPORTONLY)
201-
let (recipientPubKey, recipientPrivKey) = try theSuite.makeKeyPair()
202-
203-
// Generate the secret
204-
205-
let (encapsulated, secret) = try theSuite.sendExport(publicKey: recipientPubKey, info: [], context: [1, 2, 3], L: 10)
206-
print("Generated secret:", secret)
207-
208-
// The recipient retrieves the secret by means of the encapsulated key
209-
210-
let retrievedSecret = try theSuite.receiveExport(privateKey: recipientPrivKey, info: [], context: [1, 2, 3], L: 10, encap: encapsulated)
211-
print("Retrieved secret:", retrievedSecret)
212-
213-
giving (for example):
214-
215-
Generated secret: [172, 169, 119, 121, 167, 53, 213, 12, 0, 29]
216-
Retrieved secret: [172, 169, 119, 121, 167, 53, 213, 12, 0, 29]
217-
218-
<h3><b>Example 2</b></h3>
219-
220-
import SwiftHPKE
221-
222-
// The aead need not be .EXPORTONLY, any aead will work
223-
224-
let theSuite = CipherSuite(kem: .P256, kdf: .KDF256, aead: .EXPORTONLY)
225-
let (recipientPubKey, recipientPrivKey) = try theSuite.makeKeyPair()
226-
let sender = try Sender(suite: theSuite, publicKey: recipientPubKey, info: [])
227-
228-
// Generate the secret
229-
230-
let secret = try sender.sendExport(context: [1, 2, 3], L: 10)
231-
print("Generated secret:", secret)
232-
233-
// The recipient retrieves the secret by means of the encapsulated key
234-
235-
let receiver = try Recipient(suite: theSuite, privateKey: recipientPrivKey, info: [], encap: sender.encapsulatedKey)
236-
let retrievedSecret = try receiver.receiveExport(context: [1, 2, 3], L: 10)
237-
print("Retrieved secret:", retrievedSecret)
238-
239-
giving (for example):
240-
241-
Generated secret: [3, 230, 139, 128, 86, 4, 81, 78, 110, 135]
242-
Retrieved secret: [3, 230, 139, 128, 86, 4, 81, 78, 110, 135]
243-
244-
The above examples use Base mode. Preshared key mode, Authenticated mode and Authenticated, preshared key mode
245-
is also possible.
246-
<h2 id="basic8"><b>Compatibility with Apple's CryptoKit Framework</b></h2>
247-
The SwiftHPKE keys of type .P256, .P384, .P521 and .X25519 are equivalent to
248-
CryptoKit keys of type P256, P384, P521 and Curve25519. Keys of type .X448 is not supported in CryptoKit.
249-
250-
To convert CryptoKit P256 keys (similarly for P384 and P521) - say *ckPriv* and *ckPub* to SwiftHPKE keys:
251-
252-
let hpkePriv = try PrivateKey(der: Bytes(ckPriv.derRepresentation))
253-
let hpkePub = try PublicKey(der: Bytes(ckPub.derRepresentation))
254-
255-
To convert CryptoKit Curve25519 keys - say *ckPriv* and *ckPub* to SwiftHPKE keys:
256-
257-
let hpkePriv = try PrivateKey(kem: .X25519, bytes: Bytes(ckPriv.rawRepresentation))
258-
let hpkePub = try PublicKey(kem: .X25519, bytes: Bytes(ckPub.rawRepresentation))
259-
260-
To convert SwiftHPKE .P256 keys (similarly for .P384 and .P521) - say *hpkePriv* and *hpkePub* to CryptoKit keys:
261-
262-
let ckPriv = try CryptoKit.P256.KeyAgreement.PrivateKey(derRepresentation: hpkePriv.der)
263-
let ckPub = try CryptoKit.P256.KeyAgreement.PublicKey(derRepresentation: hpkePub.der)
264-
265-
To convert SwiftHPKE .X25519 keys - say *hpkePriv* and *hpkePub* to CryptoKit keys:
11+
SwiftHPKE requires Swift 5.0. It also requires that the Int and UInt types be 64 bit types.
26612

267-
let ckPriv = try CryptoKit.Curve25519.KeyAgreement.PrivateKey(rawRepresentation: hpkePriv.bytes)
268-
let ckPub = try CryptoKit.Curve25519.KeyAgreement.PublicKey(rawRepresentation: hpkePub.bytes)
13+
Its documentation is build with Apple's DocC tool and published on GitHub Pages at this location
26914

270-
<h2 id="basic9"><b>Performance</b></h2>
271-
SwiftHPKE's encryption and decryption performance was measured on an iMac 2021, Apple M1 chip.
272-
The time to create a *Sender* and *Recipient* instance in base mode is shown in the table below, depending on the KEM type - units are milliseconds.
273-
<table width="90%">
274-
<tr><th align="left" width="16%">KEM</th><th align="right" width="28%">Sender</th><th align="right" width="28%">Recipient</th></tr>
275-
<tr><td>P256</td><td align="right">7 mSec</td><td align="right">6 mSec</td></tr>
276-
<tr><td>P384</td><td align="right">20 mSec</td><td align="right">17 mSec</td></tr>
277-
<tr><td>P521</td><td align="right">46 mSec</td><td align="right">39 mSec</td></tr>
278-
<tr><td>X25519</td><td align="right">0.14 mSec</td><td align="right">0.09 mSec</td></tr>
279-
<tr><td>X448</td><td align="right">1.1 mSec</td><td align="right">0.5 mSec</td></tr>
280-
</table>
281-
The encryption and decryption speed in base mode, once the *Sender* or *Recipient* instance is created, is shown in the table below, depending on the AEAD type - units are MBytes / Sec.
282-
<table width="90%">
283-
<tr><th align="left" width="16%">AEAD</th><th align="right" width="28%">Encryption speed</th><th align="right" width="28%">Decryption speed</th></tr>
284-
<tr><td>AESGCM128</td><td align="right">3500 MB/Sec (0.91 cycles / byte)</td><td align="right">3340 MB/Sec (0.96 cycles / byte)</td></tr>
285-
<tr><td>AESGCM256</td><td align="right">3640 MB/Sec (0.88 cycles / byte)</td><td align="right">3630 MB/Sec (0.88 cycles / byte)</td></tr>
286-
<tr><td>CHACHAPOLY</td><td align="right">555 MB/Sec (5.8 cycles / byte)</td><td align="right">557 MB/Sec (5.7 cycles / byte)</td></tr>
287-
</table>
15+
https://leif-ibsen.github.io/SwiftHPKE/documentation/swifthpke
28816

289-
<h2 id="dep"><b>Dependencies</b></h2>
290-
The SwiftHPKE package depends on the ASN1, BigInt and Digest packages
17+
The documentation is also available in the <i>SwiftHPKE.doccarchive</i> file.
29118

292-
dependencies: [
293-
.package(url: "https://github.com/leif-ibsen/ASN1", from: "2.2.0"),
294-
.package(url: "https://github.com/leif-ibsen/BigInt", from: "1.14.0"),
295-
.package(url: "https://github.com/leif-ibsen/Digest", from: "1.1.0"),
296-
],
29719

298-
<h2 id="ref"><b>References</b></h2>
29920

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

303-
<ul>
304-
<li>[FIPS 180-4] - FIPS PUB 180-4 - Secure Hash Standard (SHS), August 2015</li>
305-
<li>[GUIDE] - Hankerson, Menezes, Vanstone: Guide to Elliptic Curve Cryptography. Springer 2004</li>
306-
<li>[RFC-9180] - Hybrid Public Key Encryption, February 2022</li>
307-
<li>[SEC 1] - Standards for Efficient Cryptography 1 (SEC 1), Certicom Corp. 2009</li>
308-
<li>[SEC 2] - Standards for Efficient Cryptography 2 (SEC 2), Certicom Corp. 2010</li>
309-
<li>[WARREN] - Henry S. Warren, Jr.: Montgomery Multiplication, July 2012</li>
310-
</ul>

Sources/SwiftHPKE/Base64.swift

-4
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@
55
// Created by Leif Ibsen on 06/01/2020.
66
//
77

8-
///
9-
/// There is no Base64 instances.
10-
/// Base64 exists to provide a namespace. It contains static functions for Base64 encoding and decoding.
11-
///
128
public struct Base64 {
139

1410
static let base64chars = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P",

Sources/SwiftHPKE/CipherSuite.swift

-9
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,6 @@ public typealias Byte = UInt8
1313
/// Array of unsigned 8 bit values
1414
public typealias Bytes = [UInt8]
1515

16-
/// A CipherSuite instance combines a *Key Encapsulation Mechanism* (KEM), a *Key Derivation Function* (KDF)
17-
/// and a *AEAD Encryption Algorithm* (AEAD).
18-
/// It can encrypt or decrypt a single message in one of four modes:
19-
///
20-
/// * Base mode
21-
/// * Preshared key mode
22-
/// * Authenticated mode
23-
/// * Authenticated, preshared key mode
24-
///
2516
public struct CipherSuite: CustomStringConvertible {
2617

2718
let kemStructure: KEMStructure

Sources/SwiftHPKE/PrivateKey.swift

-8
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,6 @@
88
import ASN1
99
import BigInt
1010

11-
/// There are five different private key types corresponding to the five KEM's
12-
///
13-
/// * P256 - the key is a 32 byte value corresponding to a NIST curve secp256r1 private key
14-
/// * P384 - the key is a 48 byte value corresponding to a NIST curve secp384r1 private key
15-
/// * P521 - the key is a 66 byte value corresponding to a NIST curve secp521r1 private key
16-
/// * X25519 - the key is a 32 byte value corresponding to a curve X25519 private key
17-
/// * X448 - the key is a 56 byte value corresponding to a curve X448 private key
18-
///
1911
public struct PrivateKey: CustomStringConvertible, Equatable {
2012

2113
let kem: KEM

Sources/SwiftHPKE/PublicKey.swift

-8
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,6 @@
88
import ASN1
99
import BigInt
1010

11-
/// There are five different public key types corresponding to the five KEM's
12-
///
13-
/// * P256 - the key is a 65 byte value corresponding to a NIST secp256r1 uncompressed curve point
14-
/// * P384 - the key is a 97 byte value corresponding to a NIST secp384r1 uncompressed curve point
15-
/// * P521 - the key is a 133 byte value corresponding to a NIST secp521r1 uncompressed curve point
16-
/// * X25519 - the key is a 32 byte value corresponding to a curve X25519 public key
17-
/// * X448 - the key is a 56 byte value corresponding to a curve X448 public key
18-
///
1911
public struct PublicKey: CustomStringConvertible, Equatable {
2012

2113
let kem: KEM

0 commit comments

Comments
 (0)