Skip to content

Commit

Permalink
1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
leif-ibsen committed Oct 5, 2020
0 parents commit 87f2c85
Show file tree
Hide file tree
Showing 53 changed files with 8,856 additions and 0 deletions.
28 changes: 28 additions & 0 deletions Package.swift
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"]),
]
)
59 changes: 59 additions & 0 deletions README.md
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>
218 changes: 218 additions & 0 deletions Sources/SwiftChaChaPoly/ChaCha20.swift
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
}
}

}
Loading

0 comments on commit 87f2c85

Please sign in to comment.