-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 87f2c85
Showing
53 changed files
with
8,856 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// swift-tools-version:5.3 | ||
// The swift-tools-version declares the minimum version of Swift required to build this package. | ||
|
||
import PackageDescription | ||
|
||
let package = Package( | ||
name: "SwiftChaChaPoly", | ||
products: [ | ||
// Products define the executables and libraries a package produces, and make them visible to other packages. | ||
.library( | ||
name: "SwiftChaChaPoly", | ||
targets: ["SwiftChaChaPoly"]), | ||
], | ||
dependencies: [ | ||
// Dependencies declare other packages that this package depends on. | ||
// .package(url: /* package url */, from: "1.0.0"), | ||
], | ||
targets: [ | ||
// Targets are the basic building blocks of a package. A target can define a module or a test suite. | ||
// Targets can depend on other targets in this package, and on products in packages this package depends on. | ||
.target( | ||
name: "SwiftChaChaPoly", | ||
dependencies: []), | ||
.testTarget( | ||
name: "SwiftChaChaPolyTests", | ||
dependencies: ["SwiftChaChaPoly"]), | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
<h2><b>Description</b></h2> | ||
|
||
SwiftChaChaPoly is a Swift implementation of Authenticated Encryption with Associated Data. | ||
It is based on ChaCha20 encryption and Poly1305 authentication as defined in [RFC-7539]. | ||
|
||
<h2><b>Usage</b></h2> | ||
In your project Package.swift file add a dependency like<br/> | ||
|
||
dependencies: [ | ||
.package(url: "https://github.com/leif-ibsen/SwiftChaChaPoly", from: "1.0.0"), | ||
] | ||
|
||
<h2><b>Example</b></h2> | ||
// This example is from section 2.8.2 in [RFC-7539]. | ||
|
||
import SwiftChaChaPoly | ||
|
||
let key: Bytes = [ | ||
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, | ||
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f] | ||
let nonce: Bytes = [ | ||
0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47] | ||
let aad: Bytes = [ | ||
0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7] | ||
let text = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it." | ||
|
||
var bytes = Bytes(text.utf8) | ||
|
||
let chacha = ChaChaPoly(key, nonce) | ||
let tag = chacha.encrypt(&bytes, aad) | ||
print(tag) | ||
let ok = chacha.decrypt(&bytes, tag, aad) | ||
print(ok && bytes == Bytes(text.utf8) ? "Ok" : "Fail") | ||
|
||
Giving | ||
|
||
[26, 225, 11, 89, 79, 9, 226, 106, 126, 144, 46, 203, 208, 96, 6, 145] | ||
Ok | ||
|
||
<h2><b>Performance</b></h2> | ||
The encryption and decryption speed was measured on a MacBook Pro 2018, 2,2 GHz 6-Core Intel Core i7. The results are: | ||
<ul> | ||
<li>Encryption: 192 MBytes / sec (11 cycles / byte)</li> | ||
<li>Decryption: 213 MBytes / sec (10 cycles / byte)</li> | ||
</ul> | ||
|
||
<h2><b>Dependencies</b></h2> | ||
|
||
SwiftChaChaPoly requires Swift 5.0. It does not depend on other packages. | ||
|
||
<h2><b>References</b></h2> | ||
|
||
Algorithms from the following papers have been used in the implementation. | ||
There are references in the source code where appropriate. | ||
|
||
<ul> | ||
<li>[FILIPPO] - Filippo Valsorda: A GO IMPLEMENTATION OF POLY1305 THAT MAKES SENSE, April 2019</li> | ||
<li>[RFC-7539] - ChaCha20 and Poly1305 for IETF Protocols, May 2015</li> | ||
</ul> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
// | ||
// ChaCha20.swift | ||
// ChaChaPolyTest | ||
// | ||
// Created by Leif Ibsen on 20/09/2020. | ||
// | ||
|
||
typealias Word = UInt32 | ||
typealias Words = [UInt32] | ||
|
||
struct ChaCha20 { | ||
|
||
static func wordFromBytes(_ b: Bytes, _ n: Int) -> Word { | ||
return Word(b[n]) | Word(b[n + 1]) << 8 | Word(b[n + 2]) << 16 | Word(b[n + 3]) << 24 | ||
} | ||
|
||
let state0: Word | ||
let state1: Word | ||
let state2: Word | ||
let state3: Word | ||
let state4: Word | ||
let state5: Word | ||
let state6: Word | ||
let state7: Word | ||
let state8: Word | ||
let state9: Word | ||
let state10: Word | ||
let state11: Word | ||
let state12: Word | ||
let state13: Word | ||
let state14: Word | ||
let state15: Word | ||
let key: Bytes | ||
let nonce: Bytes | ||
|
||
init(_ key: Bytes, _ nonce: Bytes) { | ||
self.key = key | ||
self.nonce = nonce | ||
self.state0 = 0x61707865 | ||
self.state1 = 0x3320646e | ||
self.state2 = 0x79622d32 | ||
self.state3 = 0x6b206574 | ||
self.state4 = ChaCha20.wordFromBytes(self.key, 0) | ||
self.state5 = ChaCha20.wordFromBytes(self.key, 4) | ||
self.state6 = ChaCha20.wordFromBytes(self.key, 8) | ||
self.state7 = ChaCha20.wordFromBytes(self.key, 12) | ||
self.state8 = ChaCha20.wordFromBytes(self.key, 16) | ||
self.state9 = ChaCha20.wordFromBytes(self.key, 20) | ||
self.state10 = ChaCha20.wordFromBytes(self.key, 24) | ||
self.state11 = ChaCha20.wordFromBytes(self.key, 28) | ||
self.state12 = 0 | ||
self.state13 = ChaCha20.wordFromBytes(self.nonce, 0) | ||
self.state14 = ChaCha20.wordFromBytes(self.nonce, 4) | ||
self.state15 = ChaCha20.wordFromBytes(self.nonce, 08) | ||
} | ||
|
||
// [RFC-7539] section 2.3 | ||
func blockFunction(_ x: inout Words, _ counter: Word) { | ||
var x0 = self.state0 | ||
var x1 = self.state1 | ||
var x2 = self.state2 | ||
var x3 = self.state3 | ||
var x4 = self.state4 | ||
var x5 = self.state5 | ||
var x6 = self.state6 | ||
var x7 = self.state7 | ||
var x8 = self.state8 | ||
var x9 = self.state9 | ||
var x10 = self.state10 | ||
var x11 = self.state11 | ||
var x12 = counter | ||
var x13 = self.state13 | ||
var x14 = self.state14 | ||
var x15 = self.state15 | ||
for _ in 0 ..< 10 { | ||
|
||
// 8 quarterrounds unrolled | ||
|
||
x0 &+= x4 | ||
x12 ^= x0 | ||
x12 = (x12 << 16) | (x12 >> 16) | ||
x8 &+= x12 | ||
x4 ^= x8 | ||
x4 = (x4 << 12) | (x4 >> 20) | ||
x0 &+= x4 | ||
x12 ^= x0 | ||
x12 = (x12 << 8) | (x12 >> 24) | ||
x8 &+= x12 | ||
x4 ^= x8 | ||
x4 = (x4 << 7) | (x4 >> 25) | ||
x1 &+= x5 | ||
x13 ^= x1 | ||
x13 = (x13 << 16) | (x13 >> 16) | ||
x9 &+= x13 | ||
x5 ^= x9 | ||
x5 = (x5 << 12) | (x5 >> 20) | ||
x1 &+= x5 | ||
x13 ^= x1 | ||
x13 = (x13 << 8) | (x13 >> 24) | ||
x9 &+= x13 | ||
x5 ^= x9 | ||
x5 = (x5 << 7) | (x5 >> 25) | ||
x2 &+= x6 | ||
x14 ^= x2 | ||
x14 = (x14 << 16) | (x14 >> 16) | ||
x10 &+= x14 | ||
x6 ^= x10 | ||
x6 = (x6 << 12) | (x6 >> 20) | ||
x2 &+= x6 | ||
x14 ^= x2 | ||
x14 = (x14 << 8) | (x14 >> 24) | ||
x10 &+= x14 | ||
x6 ^= x10 | ||
x6 = (x6 << 7) | (x6 >> 25) | ||
x3 &+= x7 | ||
x15 ^= x3 | ||
x15 = (x15 << 16) | (x15 >> 16) | ||
x11 &+= x15 | ||
x7 ^= x11 | ||
x7 = (x7 << 12) | (x7 >> 20) | ||
x3 &+= x7 | ||
x15 ^= x3 | ||
x15 = (x15 << 8) | (x15 >> 24) | ||
x11 &+= x15 | ||
x7 ^= x11 | ||
x7 = (x7 << 7) | (x7 >> 25) | ||
x0 &+= x5 | ||
x15 ^= x0 | ||
x15 = (x15 << 16) | (x15 >> 16) | ||
x10 &+= x15 | ||
x5 ^= x10 | ||
x5 = (x5 << 12) | (x5 >> 20) | ||
x0 &+= x5 | ||
x15 ^= x0 | ||
x15 = (x15 << 8) | (x15 >> 24) | ||
x10 &+= x15 | ||
x5 ^= x10 | ||
x5 = (x5 << 7) | (x5 >> 25) | ||
x1 &+= x6 | ||
x12 ^= x1 | ||
x12 = (x12 << 16) | (x12 >> 16) | ||
x11 &+= x12 | ||
x6 ^= x11 | ||
x6 = (x6 << 12) | (x6 >> 20) | ||
x1 &+= x6 | ||
x12 ^= x1 | ||
x12 = (x12 << 8) | (x12 >> 24) | ||
x11 &+= x12 | ||
x6 ^= x11 | ||
x6 = (x6 << 7) | (x6 >> 25) | ||
x2 &+= x7 | ||
x13 ^= x2 | ||
x13 = (x13 << 16) | (x13 >> 16) | ||
x8 &+= x13 | ||
x7 ^= x8 | ||
x7 = (x7 << 12) | (x7 >> 20) | ||
x2 &+= x7 | ||
x13 ^= x2 | ||
x13 = (x13 << 8) | (x13 >> 24) | ||
x8 &+= x13 | ||
x7 ^= x8 | ||
x7 = (x7 << 7) | (x7 >> 25) | ||
x3 &+= x4 | ||
x14 ^= x3 | ||
x14 = (x14 << 16) | (x14 >> 16) | ||
x9 &+= x14 | ||
x4 ^= x9 | ||
x4 = (x4 << 12) | (x4 >> 20) | ||
x3 &+= x4 | ||
x14 ^= x3 | ||
x14 = (x14 << 8) | (x14 >> 24) | ||
x9 &+= x14 | ||
x4 ^= x9 | ||
x4 = (x4 << 7) | (x4 >> 25) | ||
} | ||
x[0] = x0 &+ self.state0 | ||
x[1] = x1 &+ self.state1 | ||
x[2] = x2 &+ self.state2 | ||
x[3] = x3 &+ self.state3 | ||
x[4] = x4 &+ self.state4 | ||
x[5] = x5 &+ self.state5 | ||
x[6] = x6 &+ self.state6 | ||
x[7] = x7 &+ self.state7 | ||
x[8] = x8 &+ self.state8 | ||
x[9] = x9 &+ self.state9 | ||
x[10] = x10 &+ self.state10 | ||
x[11] = x11 &+ self.state11 | ||
x[12] = x12 &+ counter | ||
x[13] = x13 &+ self.state13 | ||
x[14] = x14 &+ self.state14 | ||
x[15] = x15 &+ self.state15 | ||
} | ||
|
||
// [RFC-7539] section 2.4 | ||
func encrypt(_ text: inout Bytes) { | ||
var xor = Words(repeating: 0, count: 16) | ||
|
||
// bytePtr points to start of xor array | ||
|
||
let bytePtr = withUnsafePointer(to: &xor[0]) { | ||
$0.withMemoryRebound(to: Byte.self, capacity: 64) { | ||
UnsafeBufferPointer(start: $0, count: 64) | ||
} | ||
} | ||
var j = 0 | ||
var counter = Word(0) | ||
for i in 0 ..< text.count { | ||
if i % 64 == 0 { | ||
j = 0 | ||
counter += 1 | ||
blockFunction(&xor, counter) | ||
} | ||
text[i] ^= bytePtr[j] | ||
j += 1 | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.