Skip to content

Commit c05f149

Browse files
committed
Release 3.7.0
1 parent 55f34dd commit c05f149

85 files changed

Lines changed: 52401 additions & 198 deletions

Some content is hidden

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

README.md

Lines changed: 75 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ This encompasses:
3232
In your project Package.swift file add a dependency like<br/>
3333

3434
dependencies: [
35-
.package(url: "https://github.com/leif-ibsen/SwiftECC", from: "3.6.0"),
35+
.package(url: "https://github.com/leif-ibsen/SwiftECC", from: "3.7.0"),
3636
]
3737
SwiftECC requires Swift 5.0. It also requires that the Int and UInt types be 64 bit types.
3838
SwiftECC uses Apple's CryptoKit framework. Therefore, for macOS the version must be at least 10.15,
@@ -166,7 +166,7 @@ SwiftECC can read encrypted private key files provided they were encrypted with
166166

167167
<h2 id="basic5"><b>Encryption and Decryption</b></h2>
168168
Encryption and decryption is done using the ECIES algorithm based on the AES block cipher using one of
169-
AES-128, AES-192 or AES-256 ciphers, depending on your choice.</br>
169+
AES-128, AES-192 or AES-256 ciphers, depending on your choice.<br/>
170170
The following cipher block modes are supported:
171171
<ul>
172172
<li>GCM - Galois Counter mode. This is the default mode</li>
@@ -196,38 +196,38 @@ Six cases are considered:
196196
<h3><b>AES-128/GCM block mode</b></h3>
197197
KDF generates 32 bytes.
198198

199-
AES encryption/decryption key = bytes 0 ..< 16</br>
200-
Nonce = bytes 16 ..< 32</br>
199+
AES encryption/decryption key = bytes 0 ..< 16<br/>
200+
Nonce = bytes 16 ..< 32<br/>
201201

202202
<h3><b>AES-192/GCM block mode</b></h3>
203203
KDF generates 40 bytes.
204204

205-
AES encryption/decryption key = bytes 0 ..< 24</br>
206-
Nonce = bytes 24 ..< 40</br>
205+
AES encryption/decryption key = bytes 0 ..< 24<br/>
206+
Nonce = bytes 24 ..< 40<br/>
207207

208208
<h3><b>AES-256/GCM block mode</b></h3>
209209
KDF generates 48 bytes.
210210

211-
AES encryption/decryption key = bytes 0 ..< 32</br>
212-
Nonce = bytes 32 ..< 48</br>
211+
AES encryption/decryption key = bytes 0 ..< 32<br/>
212+
Nonce = bytes 32 ..< 48<br/>
213213

214214
<h3><b>AES-128/Non-GCM block mode</b></h3>
215215
KDF generates 48 bytes.
216216

217-
AES encryption/decryption key = bytes 0 ..< 16</br>
218-
HMAC key = bytes 16 ..< 48</br>
217+
AES encryption/decryption key = bytes 0 ..< 16<br/>
218+
HMAC key = bytes 16 ..< 48<br/>
219219

220220
<h3><b>AES-192/Non-GCM block mode</b></h3>
221221
KDF generates 56 bytes.
222222

223-
AES encryption/decryption key = bytes 0 ..< 24</br>
224-
HMAC key = bytes 24 ..< 56</br>
223+
AES encryption/decryption key = bytes 0 ..< 24<br/>
224+
HMAC key = bytes 24 ..< 56<br/>
225225

226226
<h3><b>AES-256/Non-GCM block mode</b></h3>
227227
KDF generates 64 bytes.
228228

229-
AES encryption/decryption key = bytes 0 ..< 32</br>
230-
HMAC key = bytes 32 ..< 64</br>
229+
AES encryption/decryption key = bytes 0 ..< 32<br/>
230+
HMAC key = bytes 32 ..< 64<br/>
231231

232232
The AES key and HMAC key can be retrieved with the ECPrivateKey method 'getKeyAndMac'.
233233

@@ -320,26 +320,26 @@ Four cases are considered:
320320
<h3><b>ChaCha20/Poly1305</b></h3>
321321
KDF generates 44 bytes.
322322

323-
Encryption/decryption key = bytes 0 ..< 32</br>
324-
Nonce = bytes 32 ..< 44</br>
323+
Encryption/decryption key = bytes 0 ..< 32<br/>
324+
Nonce = bytes 32 ..< 44<br/>
325325

326326
<h3><b>AES-128/GCM</b></h3>
327327
KDF generates 28 bytes.
328328

329-
AES encryption/decryption key = bytes 0 ..< 16</br>
330-
Nonce = bytes 16 ..< 28</br>
329+
AES encryption/decryption key = bytes 0 ..< 16<br/>
330+
Nonce = bytes 16 ..< 28<br/>
331331

332332
<h3><b>AES-192/GCM</b></h3>
333333
KDF generates 36 bytes.
334334

335-
AES encryption/decryption key = bytes 0 ..< 24</br>
336-
Nonce = bytes 24 ..< 36</br>
335+
AES encryption/decryption key = bytes 0 ..< 24<br/>
336+
Nonce = bytes 24 ..< 36<br/>
337337

338338
<h3><b>AES-256/GCM</b></h3>
339339
KDF generates 44 bytes.
340340

341-
AES encryption/decryption key = bytes 0 ..< 32</br>
342-
Nonce = bytes 32 ..< 44</br>
341+
AES encryption/decryption key = bytes 0 ..< 32<br/>
342+
Nonce = bytes 32 ..< 44<br/>
343343

344344
<h2 id="basic6"><b>Signing and Verifying</b></h2>
345345
Signing data and verifying signatures is performed using the ECDSA algorithm. It is possible to generate
@@ -408,8 +408,13 @@ giving (for example):<br/>
408408

409409
<h2 id="basic7"><b>Secret Key Agreement</b></h2>
410410
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.
411-
The other party can generate the same byte array by using his own private key and your public key.
412-
<h3><b>Example</b></h3>
411+
The other party can generate the same byte array by using his own private key and your public key.<br/>
412+
SwiftECC supports two mechanisms:
413+
<ul>
414+
<li>The X9.63 version specified in [SEC 1] section 3.6.1</li>
415+
<li>The HKDF version specified in [RFC-5869]</li>
416+
</ul>
417+
<h3><b>X9.63 Example</b></h3>
413418

414419
import SwiftECC
415420

@@ -423,33 +428,68 @@ The other party can generate the same byte array by using his own private key an
423428
let (pubB, privB) = domain.makeKeyPair()
424429

425430
let info: Bytes = [1, 2, 3]
426-
let secretA = try privA.keyAgreement(pubKey: pubB, length: 16, md: .SHA2_256, sharedInfo: info)
427-
let secretB = try privB.keyAgreement(pubKey: pubA, length: 16, md: .SHA2_256, sharedInfo: info)
431+
let secretA = try privA.x963KeyAgreement(pubKey: pubB, length: 16, md: .SHA2_256, sharedInfo: info)
432+
let secretB = try privB.x963KeyAgreement(pubKey: pubA, length: 16, md: .SHA2_256, sharedInfo: info)
428433
print(secretA)
429434
print(secretB)
430435
} catch {
431436
print("Exception: \(error)")
432437
}
433438

434-
giving (for example):</br>
439+
giving (for example):<br/>
435440

436441
[92, 161, 137, 44, 47, 30, 6, 26, 43, 183, 199, 130, 19, 254, 232, 106]
437442
[92, 161, 137, 44, 47, 30, 6, 26, 43, 183, 199, 130, 19, 254, 232, 106]
438443

439-
For the key agreement to work, the two parties must agree on which domain to use, which message digest to use
440-
and which shared information (possibly none) to use.
444+
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.
445+
<h3><b>HKDF Example</b></h3>
446+
447+
import SwiftECC
448+
449+
do {
450+
let domain = Domain.instance(curve: .EC256r1)
451+
452+
// Party A's keys
453+
let (pubA, privA) = domain.makeKeyPair()
454+
455+
// Party B's keys
456+
let (pubB, privB) = domain.makeKeyPair()
457+
458+
let info: Bytes = [1, 2, 3]
459+
let salt: Bytes = [4, 5, 6]
460+
let secretA = try privA.hkdfKeyAgreement(pubKey: pubB, length: 16, md: .SHA2_256, sharedInfo: info, salt: salt)
461+
let secretB = try privB.hkdfKeyAgreement(pubKey: pubA, length: 16, md: .SHA2_256, sharedInfo: info, salt: salt)
462+
print(secretA)
463+
print(secretB)
464+
} catch {
465+
print("Exception: \(error)")
466+
}
467+
468+
giving (for example):<br/>
469+
470+
[202, 36, 31, 96, 207, 220, 135, 77, 130, 41, 214, 139, 214, 30, 106, 180]
471+
[202, 36, 31, 96, 207, 220, 135, 77, 130, 41, 214, 139, 214, 30, 106, 180]
472+
473+
For the key agreement to work, the two parties must agree on which domain, which message digest,
474+
which shared information (possibly none) and which salt (possibly none) to use.
441475
<h3><b>CryptoKit Compatibility</b></h3>
442476
SwiftECC key agreement is compatible with Swift CryptoKit key agreement
443477
in that the EC256r1, EC384r1 and EC521r1 domains correspond to CryptoKit's P256, P384 and P521 curves,
444-
and the SHA2_256, SHA2_384 and SHA2_512 message digests correspond to CryptoKit's SHA256, SHA384 and SHA512 message digests.
478+
and the SHA2_256, SHA2_384 and SHA2_512 message digests correspond to CryptoKit's SHA256, SHA384 and SHA512 message digests.<br/>
479+
<ul>
480+
<li>The <i>x963KeyAgreement</i> method corresponds to the CryptoKit method <i>x963DerivedSymmetricKey</i></li>
481+
<li>The <i>hkdfKeyAgreement</i> method corresponds to the CryptoKit method <i>hkdfDerivedSymmetricKey</i></li>
482+
</ul>
445483

446-
To convert a CryptoKit public key - e.g. 'ckPubKey' - to the corresponding SwiftECC public key:</br>
484+
To convert CryptoKit keys - e.g. <i>ckPubKey</i>, <i>ckPrivKey</i> - to the corresponding SwiftECC keys:<br/>
447485

448-
let eccPubKey = try ECPublickey(pem: ckPubKey.pemRepresentation)
486+
let eccPubKey = try ECPublicKey(pem: ckPubKey.pemRepresentation)
487+
let eccPrivKey = try ECPrivateKey(pem: ckPrivKey.pemRepresentation)
449488

450-
To convert a SwiftECC public key - e.g. 'eccPubKey' - to the corresponding CryptoKit public key:</br>
489+
To convert SwiftECC keys - e.g. <i>eccPubKey</i>, <i>eccPrivKey</i> - to the corresponding CryptoKit keys:<br/>
451490

452491
let ckPubKey = try P256.KeyAgreement.PublicKey(pemRepresentation: eccPubKey.pem)
492+
let ckPrivKey = try P256.KeyAgreement.PrivateKey(pemRepresentation: eccPrivKey.pem)
453493

454494
<h2 id="basic8"><b>Creating New Domains</b></h2>
455495
You can create your own domains as illustrated by the two examples below.
@@ -606,6 +646,7 @@ There are references in the source code where appropriate.
606646
<li>[KNUTH] - Donald E. Knuth: Seminumerical Algorithms. Addison-Wesley 1971</li>
607647
<li>[NIST] - NIST Special Publication 800-38D, November 2007</li>
608648
<li>[PKCS#5] - Password-Based Cryptography Specification - Version 2.0, September 2000</li>
649+
<li>[RFC-5869] - HMAC-based Extract-and-Expand Key Derivation Function (HKDF), May 2010</li>
609650
<li>[RFC-6979] - Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA), August 2013</li>
610651
<li>[SAVACS] - E. Savacs, C.K. Koc: The Montgomery Modular Inverse - Revisited, July 2000</li>
611652
<li>[SEC 1] - Standards for Efficient Cryptography 1 (SEC 1), Certicom Corp. 2009</li>
@@ -614,4 +655,4 @@ There are references in the source code where appropriate.
614655
<li>[X9.62] - X9.62 - Public Key Cryptography For The Financial Services Industry, 1998</li>
615656
</ul>
616657
<h2 id="ack"><b>Acknowledgement</b></h2>
617-
The AES block cipher implementation is essentially a translation to Swift of the Go Language implementation of AES.</br>
658+
The AES block cipher implementation is essentially a translation to Swift of the Go Language implementation of AES.<br/>

Sources/SwiftECC/PrivateKey.swift

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,9 @@ public class ECPrivateKey: CustomStringConvertible {
487487
return try Data(self.decryptAESGCM(msg: Bytes(msg), cipher: cipher, aad: Bytes(aad)))
488488
}
489489

490-
/// Constructs a shared secret key using Diffie-Hellman key agreement - please refer [SEC 1] section 3.3.1
490+
/// Constructs a shared secret key using Diffie-Hellman key agreement<br/>
491+
/// This is the ANS X9.63 version from [SEC 1] section 3.6.1<br/>
492+
/// The method is compatible with the Apple CryptoKit method *x963DerivedSymmetricKey*
491493
///
492494
/// - Parameters:
493495
/// - pubKey: The other party's public key
@@ -497,17 +499,13 @@ public class ECPrivateKey: CustomStringConvertible {
497499
/// - cofactor: Use cofactor version - *false* is default
498500
/// - Returns: A byte array which is the shared secret key
499501
/// - Throws: An exception if *this* and *pubKey* do not belong to the same domain or *length* is negative
500-
public func keyAgreement(pubKey: ECPublicKey, length: Int, md: MessageDigestAlgorithm, sharedInfo: Bytes, cofactor: Bool = false) throws -> Bytes {
501-
if self.domain != pubKey.domain {
502-
throw ECException.keyAgreementParameter
503-
}
502+
public func x963KeyAgreement(pubKey: ECPublicKey, length: Int, md: MessageDigestAlgorithm, sharedInfo: Bytes, cofactor: Bool = false) throws -> Bytes {
503+
let Z = try self.sharedSecret(pubKey, cofactor)
504504
let mda = MessageDigest(md)
505505
if length >= mda.digestLength * 0xffffffff || length < 0 {
506506
throw ECException.keyAgreementParameter
507507
}
508-
var Z = try self.domain.multiplyPoint(pubKey.w, (cofactor ? self.domain.cofactor : 1) * self.s).x.asMagnitudeBytes()
509-
Z = self.domain.align(Z)
510-
508+
511509
// [SEC 1] - section 3.6.1
512510

513511
var k: Bytes = []
@@ -531,5 +529,59 @@ public class ECPrivateKey: CustomStringConvertible {
531529
}
532530
return Bytes(k[0 ..< length])
533531
}
532+
533+
/// Deprecated - use *x963KeyAgreement* instead
534+
@available(*, deprecated, renamed: "x963KeyAgreement")
535+
public func keyAgreement(pubKey: ECPublicKey, length: Int, md: MessageDigestAlgorithm, sharedInfo: Bytes, cofactor: Bool = false) throws -> Bytes {
536+
return try x963KeyAgreement(pubKey: pubKey, length: length, md: md, sharedInfo: sharedInfo, cofactor: cofactor)
537+
}
538+
539+
/// Constructs a shared secret key using Diffie-Hellman key agreement<br/>
540+
/// This is the HKDF version from [RFC-5869]<br/>
541+
/// The method is compatible with the Apple CryptoKit method *hkdfDerivedSymmetricKey*
542+
///
543+
/// - Parameters:
544+
/// - pubKey: The other party's public key
545+
/// - length: The required length of the shared secret - a positive number
546+
/// - md: The message digest algorithm to use
547+
/// - sharedInfo: Information shared with the other party - possibly empty
548+
/// - salt: The salt to use - possibly empty
549+
/// - cofactor: Use cofactor version - *false* is default
550+
/// - Returns: A byte array which is the shared secret key
551+
/// - Throws: An exception if *this* and *pubKey* do not belong to the same domain or *length* has wrong size
552+
public func hkdfKeyAgreement(pubKey: ECPublicKey, length: Int, md: MessageDigestAlgorithm, sharedInfo: Bytes, salt: Bytes, cofactor: Bool = false) throws -> Bytes {
553+
let Z = try self.sharedSecret(pubKey, cofactor)
554+
return try ECPrivateKey.HKDF(Z, length, md, sharedInfo, salt)
555+
}
556+
557+
func sharedSecret(_ pubKey: ECPublicKey, _ cofactor: Bool) throws -> Bytes {
558+
guard self.domain == pubKey.domain else {
559+
throw ECException.keyAgreementParameter
560+
}
561+
let Z = try self.domain.multiplyPoint(pubKey.w, (cofactor ? self.domain.cofactor : 1) * self.s).x.asMagnitudeBytes()
562+
return self.domain.align(Z)
563+
}
534564

565+
static func HKDF(_ IKM: Bytes, _ length: Int, _ md: MessageDigestAlgorithm, _ sharedInfo: Bytes, _ salt: Bytes) throws -> Bytes {
566+
let mda = MessageDigest(md)
567+
let len = mda.digestLength
568+
guard length > 0 && length <= 255 * len else {
569+
throw ECException.keyAgreementParameter
570+
}
571+
var hMac = HMac(mda, salt)
572+
let PRK = hMac.doFinal(IKM)
573+
let N = (length + len) / len
574+
hMac = HMac(mda, PRK)
575+
var T = Bytes()
576+
var bytes = Bytes()
577+
for i in 1 ... N {
578+
bytes += sharedInfo
579+
bytes += [Byte(i)]
580+
let t = hMac.doFinal(bytes)
581+
T += t
582+
bytes = t
583+
hMac.reset()
584+
}
585+
return Bytes(T[0 ..< length])
586+
}
535587
}

0 commit comments

Comments
 (0)