final The version with cleaned resources

This commit is contained in:
Ihar Katkavets
2023-08-26 12:57:33 +02:00
parent 8025041006
commit 2ec7b6c599
57 changed files with 7470 additions and 4 deletions
+2 -1
View File
@@ -1,5 +1,6 @@
.DS_Store
/.build
.build/
.swiftpm/
/Packages
/*.xcodeproj
xcuserdata/
+73
View File
@@ -0,0 +1,73 @@
# AES Streams
AES is a 128-bit block cipher. It supports key of lenght `128` bits, `192` bits or `256` bits.</br>
Initialization vector(`iv`), if present, must be `16` bytes (`128` bits) length.
## Create output(encrypting) stream
```swift
let encryptingStream = AesOutputStream(writingTo: anotherOuputStream,
key: key,
iv: iv)
try encryptingStream.open()
try encryptingStream.write(buffer, length: len)
try encryptingStream.close()
```
## Create input(decrypting) stream
```swift
let decryptingStream = AesInputStream(readingFrom: anotherInputStream,
key: key,
iv: iv)
try decryptingStream.open()
var decryptedBytes = Array<UInt8>()
while decryptingStream.hasBytesAvailable {
let tmpBufferLen = 1<<16
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
let readLen = try decryptingStream.read(&tmpBuffer, maxLength: tmpBufferLen)
decryptedBytes.append(contentsOf: readBuffer.prefix(readLen))
}
let decryptedData = Data(decryptedBytes)
```
## Constructor accepts additional parameters
```swift
let aesOptions = AesOptions.PKCS7Padding // default
// let aesOptions = AesOptions.ECBMode
// let aesOptions = AesOptions.CBCMode
let encryptingStream = AesOutputStream(writingTo: anotherOuputStream,
key: key,
iv: iv,
options: aesOptions,
chunk: AesOutputStream.defaultChunkSize)
let decryptingStream = AesInputStream(readingFrom: anotherInputStream,
key: key,
iv: iv,
options: aesOptions,
chunk: AesInputStream.defaultChunkSize)
```
where:</br>
`AesOptions.ECBMode` - doesn't use `iv`. Due to obvious weaknesses, it is generally not recommended. The source data is
divided into blocks as the length of the block of AES, 128. So the ECB mode
needs to pad data until it is same as the length of the block. Then every block
will be encrypted with the same key and same algorithm. So if we encrypt the
same plaintext, we will get the same ciphertext. So there is a high risk in this
mode. And the plaintext and ciphertext blocks are a one-to-one correspondence.
Because the encryption/ decryption is independent, so we can encrypt/decrypt the
data in parallel. And if a block of plaintext or ciphertext is broken, it wont
affect other blocks.</br>
`AesOptions.CBCMode` - uses `iv` and it must be the same length as the algorithm's block size. The total number of bytes does have to be aligned to the block size (`128` bit),
otherwise `open()` will return `alignmentError`. If `iv` is not present, a NULL (all zeroes) `iv` will be used </br>
`AesOptions.PKCS7Padding` - the total number of bytes provided by all the calls to this function when
encrypting can be arbitrary (i.e., the total number of bytes does not have to
be block aligned).
+33
View File
@@ -0,0 +1,33 @@
# ChaCha20 Stream
ChaCha20 is a stream cipher with symmetric secret key. It works on data blocks of size 64 bytes. Key length is 32 bytes.</br>
Initialization vector(`iv`) is required of 12 bytes length.</br>
The stream cipher algorithm performs 20 rounds of computations in its hash function.
## Create output(encrypting) stream
```swift
let encryptingStream = ChaCha20OutputStream(writingTo: dataOutputStream,
key: key,
iv: iv)
try encryptingStream.open()
try encryptingStream.write(buffer, length: len)
try encryptingStream.close()
```
## Create input(decrypting) stream
```swift
let decryptionStream = ChaCha20InputStream(readingFrom: dataInputStream,
key: key,
iv: iv)
try decryptingStream.open()
var decryptedBytes = [UInt8]()
while decryptingStream.hasBytesAvailable {
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
let readLen = try decryptingStream.read(&tmpBuffer, maxLength: tmpBufferLen)
decryptedBytes.append(contentsOf: tmpBuffer.prefix(readLen))
}
let decryptedData = Data(decryptedBytes)
```
+4
View File
@@ -0,0 +1,4 @@
#import <salsa20.h>
#import <chacha20.h>
#import <twofish.h>
+15
View File
@@ -0,0 +1,15 @@
// swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Core",
products: [
.library(name: "Core", targets: ["Core"]),
],
targets: [
.target(name: "Core",
path: "Sources"),
]
)
+179
View File
@@ -0,0 +1,179 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
/**
* Salsa 20 implementation adopted from the reference
* implementation by D. J. Bernstein https://cr.yp.to/chacha.html
* Taken from https://cr.yp.to/streamciphers/timings/estreambench/submissions/salsa20/chacha8/regs/chacha.c
*/
#include "chacha20.h"
#include "encrypt-portable.h"
#include <stdint.h>
#include <stdio.h>
#define ROTATE(v,c) (ROTL32(v,c))
#define XOR(v,w) ((v) ^ (w))
#define PLUS(v,w) ((v) + (w))
#define PLUSONE(v) (PLUS((v),1))
#define QUARTERROUND(a,b,c,d) \
a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \
c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \
a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \
c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);
static void chacha20_wordtobyte(u8 output[64],const u32 input[16])
{
u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
int i;
x0 = input[0];
x1 = input[1];
x2 = input[2];
x3 = input[3];
x4 = input[4];
x5 = input[5];
x6 = input[6];
x7 = input[7];
x8 = input[8];
x9 = input[9];
x10 = input[10];
x11 = input[11];
x12 = input[12];
x13 = input[13];
x14 = input[14];
x15 = input[15];
for (i = 20;i > 0;i -= 2) {
QUARTERROUND( x0, x4, x8,x12)
QUARTERROUND( x1, x5, x9,x13)
QUARTERROUND( x2, x6,x10,x14)
QUARTERROUND( x3, x7,x11,x15)
QUARTERROUND( x0, x5,x10,x15)
QUARTERROUND( x1, x6,x11,x12)
QUARTERROUND( x2, x7, x8,x13)
QUARTERROUND( x3, x4, x9,x14)
}
x0 = PLUS(x0,input[0]);
x1 = PLUS(x1,input[1]);
x2 = PLUS(x2,input[2]);
x3 = PLUS(x3,input[3]);
x4 = PLUS(x4,input[4]);
x5 = PLUS(x5,input[5]);
x6 = PLUS(x6,input[6]);
x7 = PLUS(x7,input[7]);
x8 = PLUS(x8,input[8]);
x9 = PLUS(x9,input[9]);
x10 = PLUS(x10,input[10]);
x11 = PLUS(x11,input[11]);
x12 = PLUS(x12,input[12]);
x13 = PLUS(x13,input[13]);
x14 = PLUS(x14,input[14]);
x15 = PLUS(x15,input[15]);
U32TO8_LITTLE(output + 0,x0);
U32TO8_LITTLE(output + 4,x1);
U32TO8_LITTLE(output + 8,x2);
U32TO8_LITTLE(output + 12,x3);
U32TO8_LITTLE(output + 16,x4);
U32TO8_LITTLE(output + 20,x5);
U32TO8_LITTLE(output + 24,x6);
U32TO8_LITTLE(output + 28,x7);
U32TO8_LITTLE(output + 32,x8);
U32TO8_LITTLE(output + 36,x9);
U32TO8_LITTLE(output + 40,x10);
U32TO8_LITTLE(output + 44,x11);
U32TO8_LITTLE(output + 48,x12);
U32TO8_LITTLE(output + 52,x13);
U32TO8_LITTLE(output + 56,x14);
U32TO8_LITTLE(output + 60,x15);
}
void CHACHA20_init(void)
{
return;
}
static const char sigma[16] = "expand 32-byte k";
void CHACHA20_keysetup(CHACHA20_ctx *x,const u8 *k,u32 kbits,u32 ivbits)
{
const char *constants;
x->input[0] = U8TO32_LITTLE(sigma + 0);
x->input[1] = U8TO32_LITTLE(sigma + 4);
x->input[2] = U8TO32_LITTLE(sigma + 8);
x->input[3] = U8TO32_LITTLE(sigma + 12);
x->input[4] = U8TO32_LITTLE(k + 0);
x->input[5] = U8TO32_LITTLE(k + 4);
x->input[6] = U8TO32_LITTLE(k + 8);
x->input[7] = U8TO32_LITTLE(k + 12);
x->input[8] = U8TO32_LITTLE(k + 16);
x->input[9] = U8TO32_LITTLE(k + 20);
x->input[10] = U8TO32_LITTLE(k + 24);
x->input[11] = U8TO32_LITTLE(k + 28);
}
void CHACHA20_ivsetup(CHACHA20_ctx *x,const u8 *iv)
{
x->input[12] = 0;
x->input[13] = U8TO32_LITTLE(iv + 0);
x->input[14] = U8TO32_LITTLE(iv + 4);
x->input[15] = U8TO32_LITTLE(iv + 8);
}
void CHACHA20_encrypt_bytes(CHACHA20_ctx *x,const u8 *m,u8 *c,u32 bytes)
{
u8 output[64];
int i;
if (!bytes) return;
for (;;) {
chacha20_wordtobyte(output,x->input);
x->input[12] = PLUSONE(x->input[12]);
if (bytes <= 64) {
for (i = 0;i < bytes;++i) c[i] = m[i] ^ output[i];
return;
}
for (i = 0;i < 64;++i) c[i] = m[i] ^ output[i];
bytes -= 64;
c += 64;
m += 64;
}
}
void CHACHA20_decrypt_bytes(CHACHA20_ctx *x,const u8 *c,u8 *m,u32 bytes)
{
CHACHA20_encrypt_bytes(x,c,m,bytes);
}
void CHACHA20_encrypt_blocks(CHACHA20_ctx* ctx, const u8* plaintext, u8* ciphertext, u32 blocks)
{
CHACHA20_encrypt_bytes(ctx, plaintext, ciphertext, (blocks) * CHACHA20_BLOCKLENGTH);
}
void CHACHA20_decrypt_blocks(CHACHA20_ctx* ctx, const u8* ciphertext, u8* plaintext, u32 blocks)
{
CHACHA20_decrypt_bytes(ctx, ciphertext, plaintext, (blocks) * CHACHA20_BLOCKLENGTH);
}
+92
View File
@@ -0,0 +1,92 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
/**
* Salsa 20 implementation adopted from the reference
* implementation by D. J. Bernstein https://cr.yp.to/chacha.html
* Taken from https://cr.yp.to/streamciphers/timings/estreambench/submissions/salsa20/chacha8/ref/ecrypt-sync.h
* Public domain.
*/
#ifndef chacha_h
#define chacha_h
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef uint8_t u8;
typedef uint32_t u32;
typedef struct
{
u32 input[16]; /* could be compressed */
} CHACHA20_ctx;
void CHACHA20_init();
void CHACHA20_keysetup(
CHACHA20_ctx* ctx,
const u8* key,
u32 keysize, /* Key size in bits. */
u32 ivsize); /* IV size in bits. */
void CHACHA20_ivsetup(
CHACHA20_ctx* ctx,
const u8* iv);
void CHACHA20_encrypt_bytes(
CHACHA20_ctx* ctx,
const u8* plaintext,
u8* ciphertext,
u32 msglen); /* Message length in bytes. */
void CHACHA20_decrypt_bytes(
CHACHA20_ctx* ctx,
const u8* ciphertext,
u8* plaintext,
u32 msglen); /* Message length in bytes. */
#define CHACHA20_BLOCKLENGTH 64 /* [edit] */
void CHACHA20_encrypt_blocks(
CHACHA20_ctx* ctx,
const u8* plaintext,
u8* ciphertext,
u32 blocks); /* Message length in blocks. */
void CHACHA20_decrypt_blocks(
CHACHA20_ctx* ctx,
const u8* ciphertext,
u8* plaintext,
u32 blocks); /* Message length in blocks. */
#ifdef __cplusplus
}
#endif
#endif /* salsa20_h */
+57
View File
@@ -0,0 +1,57 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
/**
* Salsa 20 implementation adopted from the reference
* implementation by D. J. Bernstein https://cr.yp.to/salsa20.html
* Taken from https://cr.yp.to/snuffle/ecrypt-portable.h
*/
#ifndef ECRYPT_PORTABLE
#define ECRYPT_PORTABLE
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define ROTL32(v, n) \
(((v) << (n)) | ((v) >> (32 - (n))))
#define U8TO32_LITTLE(p) \
(((uint32_t)((p)[0]) ) | \
((uint32_t)((p)[1]) << 8) | \
((uint32_t)((p)[2]) << 16) | \
((uint32_t)((p)[3]) << 24))
#define U32TO32_LITTLE(v) (v)
#define U32TO8_LITTLE(p, v) (((u32*)(p))[0] = U32TO32_LITTLE(v))
#ifdef __cplusplus
}
#endif
#endif
+208
View File
@@ -0,0 +1,208 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
/**
* Salsa 20 implementation adopted from the reference
* implementation by D. J. Bernstein https://cr.yp.to/salsa20.html
* Taken from https://cr.yp.to/snuffle/salsa20/ref/salsa20.c
*/
#include "salsa20.h"
#include "encrypt-portable.h"
#include <stdint.h>
#include <stdio.h>
#define ROTATE(v,c) (ROTL32(v,c))
#define XOR(v,w) ((v) ^ (w))
#define PLUS(v,w) ((v) + (w))
#define PLUSONE(v) (PLUS((v),1))
static void salsa20_wordtobyte(u8 output[64],const u32 input[16])
{
u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
int i;
x0 = input[0];
x1 = input[1];
x2 = input[2];
x3 = input[3];
x4 = input[4];
x5 = input[5];
x6 = input[6];
x7 = input[7];
x8 = input[8];
x9 = input[9];
x10 = input[10];
x11 = input[11];
x12 = input[12];
x13 = input[13];
x14 = input[14];
x15 = input[15];
for (i = 20;i > 0;i -= 2) {
x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7));
x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9));
x12 = XOR(x12,ROTATE(PLUS( x8, x4),13));
x0 = XOR( x0,ROTATE(PLUS(x12, x8),18));
x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7));
x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9));
x1 = XOR( x1,ROTATE(PLUS(x13, x9),13));
x5 = XOR( x5,ROTATE(PLUS( x1,x13),18));
x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7));
x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9));
x6 = XOR( x6,ROTATE(PLUS( x2,x14),13));
x10 = XOR(x10,ROTATE(PLUS( x6, x2),18));
x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7));
x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9));
x11 = XOR(x11,ROTATE(PLUS( x7, x3),13));
x15 = XOR(x15,ROTATE(PLUS(x11, x7),18));
x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7));
x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9));
x3 = XOR( x3,ROTATE(PLUS( x2, x1),13));
x0 = XOR( x0,ROTATE(PLUS( x3, x2),18));
x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7));
x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9));
x4 = XOR( x4,ROTATE(PLUS( x7, x6),13));
x5 = XOR( x5,ROTATE(PLUS( x4, x7),18));
x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7));
x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9));
x9 = XOR( x9,ROTATE(PLUS( x8,x11),13));
x10 = XOR(x10,ROTATE(PLUS( x9, x8),18));
x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7));
x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9));
x14 = XOR(x14,ROTATE(PLUS(x13,x12),13));
x15 = XOR(x15,ROTATE(PLUS(x14,x13),18));
}
x0 = PLUS(x0,input[0]);
x1 = PLUS(x1,input[1]);
x2 = PLUS(x2,input[2]);
x3 = PLUS(x3,input[3]);
x4 = PLUS(x4,input[4]);
x5 = PLUS(x5,input[5]);
x6 = PLUS(x6,input[6]);
x7 = PLUS(x7,input[7]);
x8 = PLUS(x8,input[8]);
x9 = PLUS(x9,input[9]);
x10 = PLUS(x10,input[10]);
x11 = PLUS(x11,input[11]);
x12 = PLUS(x12,input[12]);
x13 = PLUS(x13,input[13]);
x14 = PLUS(x14,input[14]);
x15 = PLUS(x15,input[15]);
U32TO8_LITTLE(output + 0,x0);
U32TO8_LITTLE(output + 4,x1);
U32TO8_LITTLE(output + 8,x2);
U32TO8_LITTLE(output + 12,x3);
U32TO8_LITTLE(output + 16,x4);
U32TO8_LITTLE(output + 20,x5);
U32TO8_LITTLE(output + 24,x6);
U32TO8_LITTLE(output + 28,x7);
U32TO8_LITTLE(output + 32,x8);
U32TO8_LITTLE(output + 36,x9);
U32TO8_LITTLE(output + 40,x10);
U32TO8_LITTLE(output + 44,x11);
U32TO8_LITTLE(output + 48,x12);
U32TO8_LITTLE(output + 52,x13);
U32TO8_LITTLE(output + 56,x14);
U32TO8_LITTLE(output + 60,x15);
}
void SALSA20_init(void)
{
return;
}
static const char sigma[16] = "expand 32-byte k";
static const char tau[16] = "expand 16-byte k";
void SALSA20_keysetup(SALSA20_ctx *x,const u8 *k,u32 kbits,u32 ivbits)
{
const char *constants;
x->input[1] = U8TO32_LITTLE(k + 0);
x->input[2] = U8TO32_LITTLE(k + 4);
x->input[3] = U8TO32_LITTLE(k + 8);
x->input[4] = U8TO32_LITTLE(k + 12);
if (kbits == 256) { /* recommended */
k += 16;
constants = sigma;
} else { /* kbits == 128 */
constants = tau;
}
x->input[11] = U8TO32_LITTLE(k + 0);
x->input[12] = U8TO32_LITTLE(k + 4);
x->input[13] = U8TO32_LITTLE(k + 8);
x->input[14] = U8TO32_LITTLE(k + 12);
x->input[0] = U8TO32_LITTLE(constants + 0);
x->input[5] = U8TO32_LITTLE(constants + 4);
x->input[10] = U8TO32_LITTLE(constants + 8);
x->input[15] = U8TO32_LITTLE(constants + 12);
}
void SALSA20_ivsetup(SALSA20_ctx *x,const u8 *iv)
{
x->input[6] = U8TO32_LITTLE(iv + 0);
x->input[7] = U8TO32_LITTLE(iv + 4);
x->input[8] = 0;
x->input[9] = 0;
}
void SALSA20_encrypt_bytes(SALSA20_ctx *x,const u8 *m,u8 *c,u32 bytes)
{
u8 output[64];
int i;
if (!bytes) return;
for (;;) {
salsa20_wordtobyte(output,x->input);
x->input[8] = PLUSONE(x->input[8]);
if (!x->input[8]) {
x->input[9] = PLUSONE(x->input[9]);
/* stopping at 2^70 bytes per nonce is user's responsibility */
}
if (bytes <= 64) {
for (i = 0;i < bytes;++i) c[i] = m[i] ^ output[i];
return;
}
for (i = 0;i < 64;++i) c[i] = m[i] ^ output[i];
bytes -= 64;
c += 64;
m += 64;
}
}
void SALSA20_decrypt_bytes(SALSA20_ctx *x,const u8 *c,u8 *m,u32 bytes)
{
SALSA20_encrypt_bytes(x,c,m,bytes);
}
void SALSA20_encrypt_blocks(SALSA20_ctx* ctx, const u8* plaintext, u8* ciphertext, u32 blocks)
{
SALSA20_encrypt_bytes(ctx, plaintext, ciphertext, (blocks) * SALSA20_BLOCKLENGTH);
}
void SALSA20_decrypt_blocks(SALSA20_ctx* ctx, const u8* ciphertext, u8* plaintext, u32 blocks)
{
SALSA20_decrypt_bytes(ctx, ciphertext, plaintext, (blocks) * SALSA20_BLOCKLENGTH);
}
+92
View File
@@ -0,0 +1,92 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
/**
* Salsa 20 implementation adopted from the reference
* implementation by D. J. Bernstein https://cr.yp.to/salsa20.html
* Taken from https://cr.yp.to/snuffle/salsa20/merged/ecrypt-sync.h
* Public domain.
*/
#ifndef salsa20_h
#define salsa20_h
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef uint8_t u8;
typedef uint32_t u32;
typedef struct
{
u32 input[16]; /* could be compressed */
} SALSA20_ctx;
void SALSA20_init();
void SALSA20_keysetup(
SALSA20_ctx* ctx,
const u8* key,
u32 keysize, /* Key size in bits. */
u32 ivsize); /* IV size in bits. */
void SALSA20_ivsetup(
SALSA20_ctx* ctx,
const u8* iv);
void SALSA20_encrypt_bytes(
SALSA20_ctx* ctx,
const u8* plaintext,
u8* ciphertext,
u32 msglen); /* Message length in bytes. */
void SALSA20_decrypt_bytes(
SALSA20_ctx* ctx,
const u8* ciphertext,
u8* plaintext,
u32 msglen); /* Message length in bytes. */
#define SALSA20_BLOCKLENGTH 64 /* [edit] */
void SALSA20_encrypt_blocks(
SALSA20_ctx* ctx,
const u8* plaintext,
u8* ciphertext,
u32 blocks); /* Message length in blocks. */
void SALSA20_decrypt_blocks(
SALSA20_ctx* ctx,
const u8* ciphertext,
u8* plaintext,
u32 blocks); /* Message length in blocks. */
#ifdef __cplusplus
}
#endif
#endif /* salsa20_h */
File diff suppressed because it is too large Load Diff
+218
View File
@@ -0,0 +1,218 @@
#ifndef TWOFISH_H_
#define TWOFISH_H_
/*
* Fast, portable, and easy-to-use Twofish implementation,
* Version 0.3.
* Copyright (c) 2002 by Niels Ferguson.
*
* See the twofish.c file for the details of the how and why of this code.
*
* The author hereby grants a perpetual license to everybody to
* use this code for any purpose as long as the copyright message is included
* in the source code of this or any derived work.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
// error status codes
typedef enum {
TWOFISH_SUCCESS = 0,
// actual encryption/decryption errors
TWOFISH_ERROR_FILL_KEYED_SBOXES = 1, // Twofish fill_keyed_sboxes(): Illegal argument
TWOFISH_ERROR_NOT_INITIALIZED = 2, // Twofish implementation was not initialised
TWOFISH_ERROR_ILLEGAL_KEY_LENGTH = 3, // Twofish_prepare_key: illegal key length
// platform and environment tests
TWOFISH_ERROR_PLATFORM_UNSUITABLE_UINT32 = 101, // Platform: Twofish_UInt32 type not suitable
TWOFISH_ERROR_PLATFORM_UNSUITABLE_BYTE = 102, // Platform: Twofish_Byte type not suitable
TWOFISH_ERROR_PLATFORM_GET32_IMPLEMENTED_IMPROPERLY = 103, // Platform: GET32 not implemented properly
TWOFISH_ERROR_PLATFORM_PUT32_IMPLEMENTED_IMPROPERLY = 104, // Platform: PUT32 not implemented properly
TWOFISH_ERROR_PLATFORM_ROL_ROR_IMPLEMENTED_IMPROPERLY = 105, // Platform: Twofish ROL or ROR not properly defined
TWOFISH_ERROR_PLATFORM_BSWAP_UNDEFINED = 106, // Platform: BSWAP not properly defined
TWOFISH_ERROR_PLATFORM_SELECT_BYTE_TEST_IMPLEMENTED_IMPROPERLY = 107, // Platform: SELECT_BYTE not implemented properly
TWOFISH_ERROR_TEST_ENCRYPTION_FAIL = 108, // Twofish test encryption failure
TWOFISH_ERROR_TEST_DECRYPTION_FAIL = 109, // Twofish test decryption failure
TWOFISH_ERROR_TEST_SEQUENCE_ENCRYPTION = 110, // Twofish encryption failure in sequence
TWOFISH_ERROR_TEST_SEQUENCE_DECRYPTION = 111, // Twofish decryption failure in sequence
TWOFISH_ERROR_TEST_ODD_SIZED_KEYS = 112, //Odd sized keys do not expand properly
} Twofish_Status;
/*
* PLATFORM FIXES
* ==============
*
* The following definitions have to be fixed for each particular platform
* you work on. If you have a multi-platform program, you no doubt have
* portable definitions that you can substitute here without changing
* the rest of the code.
*
* The defaults provided here should work on most PC compilers.
*/
/*
* A Twofish_Byte must be an unsigned 8-bit integer.
* It must also be the elementary data size of your C platform,
* i.e. sizeof( Twofish_Byte ) == 1.
*/
typedef unsigned char Twofish_Byte;
/*
* A Twofish_UInt32 must be an unsigned integer of at least 32 bits.
*
* This type is used only internally in the implementation, so ideally it
* would not appear in the header file, but it is used inside the
* Twofish_key structure which means it has to be included here.
*/
typedef unsigned int Twofish_UInt32;
/*
* END OF PLATFORM FIXES
* =====================
*
* You should not have to touch the rest of this file, but the code
* in twofish.c has a few things you need to fix too.
*/
/*
* Structure that contains a prepared Twofish key.
* A cipher key is used in two stages. In the first stage it is converted
* form the original form to an internal representation.
* This internal form is then used to encrypt and decrypt data.
* This structure contains the internal form. It is rather large: 4256 bytes
* on a platform with 32-bit unsigned values.
*
* Treat this as an opague structure, and don't try to manipulate the
* elements in it. I wish I could hide the inside of the structure,
* but C doesn't allow that.
*/
typedef
struct
{
Twofish_UInt32 s[4][256]; /* pre-computed S-boxes */
Twofish_UInt32 K[40]; /* Round key words */
}
Twofish_key;
/*
* Initialise and test the Twofish implementation.
*
* This function MUST be called before any other function in the
* Twofish implementation is called.
* It only needs to be called once.
*
* Apart from initialising the implementation it performs a self test.
* If the Twofish_fatal function is not called, the code passed the test.
* (See the twofish.c file for details on the Twofish_fatal function.)
*/
int Twofish_initialise();
/*
* Convert a cipher key to the internal form used for
* encryption and decryption.
*
* The cipher key is an array of bytes; the Twofish_Byte type is
* defined above to a type suitable on your platform.
*
* Any key must be converted to an internal form in the Twofisk_key structure
* before it can be used.
* The encryption and decryption functions only work with the internal form.
* The conversion to internal form need only be done once for each key value.
*
* Be sure to wipe all key storage, including the Twofish_key structure,
* once you are done with the key data.
* A simple memset( TwofishKey, 0, sizeof( TwofishKey ) ) will do just fine.
*
* Unlike most implementations, this one allows any key size from 0 bytes
* to 32 bytes. According to the Twofish specifications,
* irregular key sizes are handled by padding the key with zeroes at the end
* until the key size is 16, 24, or 32 bytes, whichever
* comes first. Note that each key of irregular size is equivalent to exactly
* one key of 16, 24, or 32 bytes.
*
* WARNING: Short keys have low entropy, and result in low security.
* Anything less than 8 bytes is utterly insecure. For good security
* use at least 16 bytes. I prefer to use 32-byte keys to prevent
* any collision attacks on the key.
*
* The key length argument key_len must be in the proper range.
* If key_len is not in the range 0,...,32 this routine attempts to generate
* a fatal error (depending on the code environment),
* and at best (or worst) returns without having done anything.
*
* Arguments:
* key Array of key bytes
* key_len Number of key bytes, must be in the range 0,1,...,32.
* xkey Pointer to an Twofish_key structure that will be filled
* with the internal form of the cipher key.
*/
int Twofish_prepare_key(
Twofish_Byte key[],
int key_len,
Twofish_key * xkey
);
// Fills the given Twofish_key structure with zeros [AP]
void Twofish_clear_key(Twofish_key * xkey);
/*
* Encrypt a single block of data.
*
* This function encrypts a single block of 16 bytes of data.
* If you want to encrypt a larger or variable-length message,
* you will have to use a cipher mode, such as CBC or CTR.
* These are outside the scope of this implementation.
*
* The xkey structure is not modified by this routine, and can be
* used for further encryption and decryption operations.
*
* Arguments:
* xkey pointer to Twofish_key, internal form of the key
* produces by Twofish_prepare_key()
* p Plaintext to be encrypted
* c Place to store the ciphertext
*/
void Twofish_encrypt(
Twofish_key * xkey,
Twofish_Byte p[16],
Twofish_Byte c[16]
);
/*
* Decrypt a single block of data.
*
* This function decrypts a single block of 16 bytes of data.
* If you want to decrypt a larger or variable-length message,
* you will have to use a cipher mode, such as CBC or CTR.
* These are outside the scope of this implementation.
*
* The xkey structure is not modified by this routine, and can be
* used for further encryption and decryption operations.
*
* Arguments:
* xkey pointer to Twofish_key, internal form of the key
* produces by Twofish_prepare_key()
* c Ciphertext to be decrypted
* p Place to store the plaintext
*/
void Twofish_decrypt(
Twofish_key * xkey,
Twofish_Byte c[16],
Twofish_Byte p[16]
);
#ifdef __cplusplus
}
#endif
#endif
+5
View File
@@ -0,0 +1,5 @@
module Core {
header "../Headers/Core-Bridging-Header.h"
link "Core"
export *
}
+47
View File
@@ -0,0 +1,47 @@
The `windowBits` parameter is the base two logarithm of the window size
(the size of the history buffer). It should be in the range 8..15 for this
version of the library. Larger values of this parameter result in better
compression at the expense of memory usage.
`windowBits` is used to control the compression level and the format of the
compressed data. According to the zlib manual, the windowBits parameter can
have the following values:
- negative value means that the compressed data will have no zlib header or
trailer, and will use the deflate format instead. windowBits can also
be -8..-15 for raw deflate. In this case, -windowBits determines the window
size. deflate() will then generate raw deflate data with no zlib header or
trailer, and will not compute a check value. This is useful for some
applications that already have their own headers, such as PNG images.
- value between 8 and 15 means that the compressed data will have a zlib header
and trailer, and will use a fixed window size of 2^windowBits bytes. The
default value is 15, which corresponds to a window size of 32 KB.
2^15 = 32KB
2^14 = 16KB
2^13 = 8KB
2^12 = 4KB
2^11 = 2KB
2^10 = 1KB
2^9 = 512B
2^8 = 256B
For the current implementation of deflate(), a windowBits value of 8 (a
window size of 256 bytes) is not supported. As a result, a request for 8
will result in 9 (a 512-byte window). In that case, providing 8 to
inflateInit2() will result in an error when the zlib header with 9 is
checked against the initialization of inflate(). The remedy is to not use 8
with deflateInit2() with this initialization, or at least in that case use 9
with inflateInit2().
- value between 16 and 31 means that the compressed data will have a gzip header
and trailer, and will use a variable window size of 2^(windowBits-16) bytes.
The maximum value is 31, which corresponds to a window size of 128 KB.
windowBits can also be greater than 15 for optional gzip encoding. Add
16 to windowBits to write a simple gzip header and trailer around the
compressed data instead of a zlib wrapper. The gzip header will have no
file name, no extra data, no comment, no modification time (set to zero), no
header crc, and the operating system will be set to the appropriate value,
if the operating system was determined at compile time.
+17 -2
View File
@@ -11,13 +11,28 @@ let package = Package(
name: "StreamKit",
targets: ["StreamKit"]),
],
dependencies: [
.package(path: "./Core")
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "StreamKit"),
name: "StreamKit",
dependencies: [
.product(name: "Core", package: "Core")
],
path: "Sources"),
.testTarget(
name: "StreamKitTests",
dependencies: ["StreamKit"]),
dependencies: ["StreamKit"],
path: "Tests",
resources: [
.copy("Resources/1MB"),
.copy("Resources/16B"),
.copy("Resources/PlainText"),
.copy("Resources/PlainText.gz"),
]),
]
)
+52 -1
View File
@@ -1,5 +1,56 @@
# StreamKit
The set of ciphers written on Swift
This open-source Swift library offers a comprehensive collection of cryptographic algorithms. These ciphers can be structured into chains, facilitating the seamless flow of output from one cipher stream to another. This architecture enables concurrent tasks, such as encrypting data while writing the encrypted result to a file. Integrate "StreamKit" into your projects to efficiently utilize these cryptographic functionalities.
# How to use it
For example it's possible to encrypt some data and simulteniously write the encrypted result to a file.
```swift
let secureFileURL: URL = ...
let fileHandle = try! FileHandle(forWritingTo: secureFileURL)
let outputFileStream = FileOutputStream(with: fileHandle)
try outputFileStream.open()
let encryptingStream = Salsa20OutputStream(writingTo: outputFileStream,
key: key,
iv: iv)
try encryptingStream.open()
let tmpBufferLen = 1<<16
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while inputFileStream.hasBytesAvailable {
let readLen = inputFileStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try encryptingStream.write(tmpBuffer, length: readLen)
}
try encryptingStream.close()
try outputFileStream.close()
```
Another example demonstrate reading the encrypted file
```swift
let inputFileStream = FileInputStream(withFileHandle: try! FileHandle(forReadingFrom: secureFileURL))
try inputFileStream.open()
let bufferingStream = BufferOutputStream()
try bufferingStream.open()
let decryptingStream = Salsa20InputStream(readingFrom: inputFileStream,
key: key,
iv: iv)
try decryptingStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while decryptingStream.hasBytesAvailable {
let readLen = try decryptingStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try bufferingStream.write(tmpBuffer, length: readLen)
}
decryptingStream.close()
try bufferingStream.close()
inputFileStream.close()
```
### Sponsored by [KeePassium](https://github.com/keepassium)
+32
View File
@@ -0,0 +1,32 @@
# Salsa20 Stream
Salsa20 is a stream cipher with symmetric secret key. It works on data blocks of size 64 bytes. Key length can be 16 or 32(recommended) bytes.</br>
Initialization vector(`iv`) is required of 8 bytes length.
## Create output(encrypting) stream
```swift
let encryptingStream = Salsa20OutputStream(writingTo: anotherOutputStream,
key: key,
iv: iv)
try encryptingStream.open()
try encryptingStream.write(buffer, length: len)
try encryptingStream.close()
```
## Create input(decrypting) stream
```swift
let decryptingStream = Salsa20InputStream(readingFrom: anotherInputStream,
key: key,
iv: iv)
try decryptingStream.open()
var decryptedBytes = [UInt8]()
while decryptingStream.hasBytesAvailable {
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
let readLen = try decryptingStream.read(&tmpBuffer, maxLength: tmpBufferLen)
decryptedBytes.append(contentsOf: tmpBuffer.prefix(readLen))
}
let decryptedData = Data(decryptedBytes)
```
+154
View File
@@ -0,0 +1,154 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
import CommonCrypto
public final class AesInputStream: InputStream {
public static let defaultChunkSize = 1<<15
private let nestedStream: InputStream
private var cryptorRef: CCCryptorRef?
private let bufferSize: Int
private var encryptedBuffer: UnsafeMutablePointer<UInt8>
private var decryptedBuffer: UnsafeMutablePointer<UInt8>
private let key: [UInt8]
private let iv: [UInt8]
private var isOpen = false
private var eofReached = false
private var decryptedBufferUsedLen: Int
private var decryptedBufferAvailableLen: Int
private var status: Int32 = 0
private let options: AesOptions
private let blockSize = 16
public init(readingFrom nestedStream: InputStream,
key: [UInt8],
iv: [UInt8],
options: AesOptions = AesOptions.PKCS7Padding,
chunkSize: Int = AesInputStream.defaultChunkSize) {
self.nestedStream = nestedStream
self.options = options
self.key = key
self.encryptedBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: chunkSize)
self.decryptedBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: chunkSize)
self.bufferSize = chunkSize
self.decryptedBufferUsedLen = 0
self.decryptedBufferAvailableLen = chunkSize
self.iv = iv
}
deinit {
encryptedBuffer.deallocate()
decryptedBuffer.deallocate()
}
public var hasBytesAvailable: Bool {
return !eofReached || decryptedBufferReadyLen > 0
}
public func open() throws {
guard !isOpen else { fatalError("The stream can be opened only once") }
isOpen = true
guard iv.count == blockSize else {
throw AesStreamError(kind: .ivSizeError)
}
status = CCCryptorCreate(CCOperation(kCCDecrypt),
CCAlgorithm(kCCAlgorithmAES),
CCOptions(options),
key,
key.count,
iv,
&cryptorRef)
guard status == kCCSuccess else {
throw AesStreamError(code: status)
}
}
public func read(_ outBuffer: UnsafeMutablePointer<UInt8>, maxLength: Int) throws -> Int {
guard isOpen else { fatalError("The stream is not opened") }
var totalReadCount = 0
while maxLength-totalReadCount > 0 && hasBytesAvailable {
try fillDecryptedBuffer()
let readCount = writeOutTo(outBuffer+totalReadCount, count: maxLength-totalReadCount)
totalReadCount += readCount
}
return totalReadCount
}
private var decryptedBufferReadyLen: Int {
return bufferSize-decryptedBufferUsedLen-decryptedBufferAvailableLen
}
private func fillDecryptedBuffer() throws {
if decryptedBufferReadyLen > 0 {
return
}
let outBuf = decryptedBuffer+(bufferSize-decryptedBufferAvailableLen)
let outAvailable = decryptedBufferAvailableLen
var outMoved = 0
let readLength = try nestedStream.read(encryptedBuffer, maxLength: bufferSize)
if readLength > 0 {
status = CCCryptorUpdate(cryptorRef,
encryptedBuffer,
readLength,
outBuf,
outAvailable,
&outMoved)
}
else if !nestedStream.hasBytesAvailable {
eofReached = true
status = CCCryptorFinal(cryptorRef,
outBuf,
outAvailable,
&outMoved)
}
guard status == kCCSuccess else {
throw AesStreamError(code: status)
}
decryptedBufferAvailableLen -= outMoved
}
private func writeOutTo(_ outBuffer: UnsafeMutablePointer<UInt8>, count: Int) -> Int {
let outLen = min(decryptedBufferReadyLen, count)
outBuffer.initialize(from: decryptedBuffer+decryptedBufferUsedLen, count: outLen)
decryptedBufferUsedLen += outLen
if decryptedBufferReadyLen == 0 {
(decryptedBufferUsedLen, decryptedBufferAvailableLen) = (0, bufferSize)
}
return outLen
}
public func close() {
guard isOpen else { fatalError("The stream is not opened") }
_ = CCCryptorRelease(cryptorRef)
}
}
+131
View File
@@ -0,0 +1,131 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
import CommonCrypto
public final class AesOutputStream: OutputStream {
public static let defaultChunkSize = 1<<15
private var cryptorRef: CCCryptorRef?
private var outBuffer: UnsafeMutablePointer<UInt8>
private var inBuffer: UnsafeMutablePointer<UInt8>
private let chunkSize: Int
private let nestedStream: OutputStream
private let iv: [UInt8]
private let key: [UInt8]
private var isOpen = false
private var status: Int32 = 0
private let options: AesOptions
private let blockSize = 16
public init(writingTo outputStream: OutputStream,
key: [UInt8],
iv: [UInt8],
options: AesOptions = AesOptions.PKCS7Padding,
chunkSize: Int = AesOutputStream.defaultChunkSize) {
self.nestedStream = outputStream
self.options = options
self.outBuffer = UnsafeMutablePointer.allocate(capacity: chunkSize)
self.inBuffer = UnsafeMutablePointer.allocate(capacity: chunkSize)
self.chunkSize = chunkSize
self.key = key
self.iv = iv
}
deinit {
outBuffer.deallocate()
}
public var hasSpaceAvailable: Bool {
return nestedStream.hasSpaceAvailable
}
public func open() throws {
guard !isOpen else { fatalError("The stream can be opened only once") }
isOpen = true
guard iv.count == blockSize else {
throw AesStreamError(kind: .ivSizeError)
}
status = CCCryptorCreate(CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES),
CCOptions(options),
key,
key.count,
iv,
&cryptorRef)
guard status == kCCSuccess else {
throw AesStreamError(code: status)
}
}
public func write(_ buffer: UnsafePointer<UInt8>, length: Int) throws {
guard isOpen else { fatalError("The stream is not opened") }
var remainingLen = length
var totalReadLen = 0
while remainingLen > 0 {
let inBufAvailable = min(chunkSize, remainingLen)
inBuffer.initialize(from: buffer+totalReadLen, count: inBufAvailable)
var outBufCount = 0
status = CCCryptorUpdate(cryptorRef,
inBuffer,
inBufAvailable,
outBuffer,
chunkSize,
&outBufCount)
guard status == kCCSuccess else {
throw AesStreamError(code: status)
}
if outBufCount > 0 {
try nestedStream.write(outBuffer, length: outBufCount)
}
totalReadLen += inBufAvailable
remainingLen -= inBufAvailable
}
}
public func close() throws {
guard isOpen else { fatalError("The stream is not opened") }
var outBufCount = 0
status = CCCryptorFinal(cryptorRef,
outBuffer,
chunkSize,
&outBufCount)
guard status == kCCSuccess else {
throw AesStreamError(code: status)
}
try nestedStream.write(outBuffer, length: outBufCount)
_ = CCCryptorRelease(cryptorRef)
}
}
+95
View File
@@ -0,0 +1,95 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
import CommonCrypto
public typealias AesOptions = Int
public extension AesOptions {
static let CBCMode = 0
static let PKCS7Padding = kCCOptionPKCS7Padding
static let ECBMode = kCCOptionECBMode
}
public struct AesStreamError: LocalizedError {
public enum Kind {
case paramError
case bufferTooSmall
case memoryFailure
case alignmentError
case decodeError
case unimplemented
case overflow
case rngFailure
case unspecifiedError
case callSequenceError
case keySizeError
case ivSizeError
case otherError(code: Int32)
}
public let file: String
public let line: Int
public let kind: Kind
internal init(file: String = #file, line: Int = #line, code: Int32) {
self.file = String(describing: file)
self.line = line
switch Int(code) {
case kCCParamError:
kind = .paramError
case kCCBufferTooSmall:
kind = .bufferTooSmall
case kCCMemoryFailure:
kind = .memoryFailure
case kCCAlignmentError:
kind = .alignmentError
case kCCDecodeError:
kind = .decodeError
case kCCUnimplemented:
kind = .unimplemented
case kCCOverflow:
kind = .overflow
case kCCRNGFailure:
kind = .rngFailure
case kCCUnspecifiedError:
kind = .unspecifiedError
case kCCCallSequenceError:
kind = .callSequenceError
case kCCKeySizeError:
kind = .keySizeError
case kCCAlignmentError:
kind = .alignmentError
default:
kind = .otherError(code: code)
}
}
internal init(file: String = #file, line: Int = #line, kind: Kind) {
self.file = String(describing: file)
self.line = line
self.kind = kind
}
}
@@ -0,0 +1,60 @@
//
// MIT License
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
public final class BufferInputStream: InputStream {
private var offset = 0
private var buffer: [UInt8]
public var hasBytesAvailable: Bool {
return offset < buffer.count
}
public init(withBuffer payload: [UInt8]) {
self.buffer = payload
}
public func open() throws { }
public func read(_ toBuffer: UnsafeMutablePointer<UInt8>, maxLength len: Int) throws -> Int {
if offset == buffer.count {
return 0
}
let bytesLeft = buffer.count-offset
let readLen = min(bytesLeft, len)
buffer.withUnsafeBufferPointer { ubp in
if let ptr = ubp.baseAddress {
toBuffer.initialize(from: ptr.advanced(by: offset), count: readLen)
}
}
offset += readLen
return readLen
}
public func close() { }
}
@@ -0,0 +1,44 @@
//
// MIT License
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
public final class BufferOutputStream: OutputStream {
public private(set) var buffer = [UInt8]()
public var hasSpaceAvailable: Bool {
return true
}
public init() {}
public func open() throws {}
public func write(_ fromBuffer: UnsafePointer<UInt8>, length: Int) throws {
buffer.append(contentsOf: UnsafeBufferPointer(start: fromBuffer, count: length))
}
public func close() throws {}
}
+46
View File
@@ -0,0 +1,46 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
let ChaCha20IVSize = 12
let ChaCha20KeySize = 32
public struct ChaCha20StreamError: LocalizedError {
public enum Kind {
case keySizeError
case ivSizeError
case bufferTooSmall
}
public let file: String
public let line: Int
public let kind: Kind
internal init(file: String = #file, line: Int = #line, kind: Kind) {
self.file = String(describing: file)
self.line = line
self.kind = kind
}
}
@@ -0,0 +1,195 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
import Core
public final class ChaCha20InputStream: InputStream {
public static let defaultChunkSize = 1<<15
private let nestedStream: InputStream
private let bufferSize: Int
private var encryptedBuffer: UnsafeMutablePointer<UInt8>
private var decryptedBuffer: UnsafeMutablePointer<UInt8>
private let key: [UInt8]
private let iv: [UInt8]
private var isOpen = false
private var eofReached = false
private var encryptedBufferDirtyLen: Int = 0
private var encryptedBufferAvailableLen: Int
private var decryptedBufferDirtyLen: Int = 0
private var decryptedBufferAvailableLen: Int
private var context = CHACHA20_ctx()
private let blockLen: Int = Int(CHACHA20_BLOCKLENGTH)
public init(readingFrom nestedStream: InputStream,
key: [UInt8],
iv: [UInt8],
chunkSize: Int = ChaCha20InputStream.defaultChunkSize) {
self.nestedStream = nestedStream
self.key = key
self.encryptedBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: chunkSize)
self.decryptedBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: chunkSize)
self.bufferSize = chunkSize
self.decryptedBufferAvailableLen = chunkSize
self.encryptedBufferAvailableLen = chunkSize
self.iv = iv
}
deinit {
encryptedBuffer.deallocate()
decryptedBuffer.deallocate()
}
public var hasBytesAvailable: Bool {
return !eofReached || encryptedBufferReadyLen > 0 || decryptedBufferReadyLen > 0
}
public func open() throws {
guard !isOpen else { fatalError("The stream can be opened only once") }
isOpen = true
guard key.count == ChaCha20KeySize else {
throw ChaCha20StreamError(kind: .keySizeError)
}
guard iv.count == ChaCha20IVSize else {
throw ChaCha20StreamError(kind: .ivSizeError)
}
CHACHA20_init();
let keySize = key.count
let ivSize = iv.count
key.withUnsafeBufferPointer { keyubp in
CHACHA20_keysetup(&context, keyubp.baseAddress!, u32(keySize*8), u32(ivSize*8))
}
iv.withUnsafeBufferPointer { ivubp in
CHACHA20_ivsetup(&context, ivubp.baseAddress!)
}
}
public func read(_ outBuffer: UnsafeMutablePointer<UInt8>, maxLength: Int) throws -> Int {
guard isOpen else { fatalError("The stream is not opened") }
var totalReadCount = 0
var remainingLen = maxLength
while remainingLen > 0 && hasBytesAvailable {
try fillEncryptedBuffer()
if nestedStream.hasBytesAvailable {
try decryptBlocks()
}
else {
try decryptRemaining()
}
let readCount = writeOutTo(outBuffer+totalReadCount, count: remainingLen)
if readCount == 0 { break }
remainingLen -= readCount
totalReadCount += readCount
}
return totalReadCount
}
private var encryptedBufferReadyLen: Int {
return bufferSize-encryptedBufferDirtyLen-encryptedBufferAvailableLen
}
private var encryptedBufferFilledLen: Int {
return bufferSize-encryptedBufferAvailableLen
}
private var isEncryptedBufferFull: Bool {
return encryptedBufferAvailableLen == 0
}
private func fillEncryptedBuffer() throws {
if eofReached || isEncryptedBufferFull {
return
}
let canTake = encryptedBufferAvailableLen/blockLen*blockLen
let inBuf = encryptedBuffer+encryptedBufferFilledLen
let readLen = try nestedStream.read(inBuf, maxLength: canTake)
if !nestedStream.hasBytesAvailable {
eofReached = true
}
encryptedBufferAvailableLen -= readLen
}
private var decryptedBufferReadyLen: Int {
return bufferSize-decryptedBufferDirtyLen-decryptedBufferAvailableLen
}
private var decryptedBufferFilledLen: Int {
return bufferSize-decryptedBufferAvailableLen
}
private func decryptBlocks() throws {
if decryptedBufferReadyLen > blockLen {
return
}
let inBufAvailable = encryptedBufferReadyLen
let outBufAvailable = decryptedBufferAvailableLen
let numOfBlocks = min(inBufAvailable,outBufAvailable)/blockLen
if numOfBlocks > 0 {
let processBytesLen = numOfBlocks*blockLen
let inBuffer = encryptedBuffer+encryptedBufferDirtyLen
let outBuffer = decryptedBuffer+decryptedBufferFilledLen
CHACHA20_decrypt_blocks(&context, inBuffer, outBuffer, u32(numOfBlocks))
encryptedBufferDirtyLen += processBytesLen
if encryptedBufferReadyLen == 0 {
(encryptedBufferDirtyLen, encryptedBufferAvailableLen) = (0, bufferSize)
}
decryptedBufferAvailableLen -= processBytesLen
}
}
private func decryptRemaining() throws {
let inBufAvailable = encryptedBufferReadyLen
let outBufAvailable = decryptedBufferAvailableLen
if inBufAvailable > 0 && outBufAvailable >= inBufAvailable {
let inBuffer = encryptedBuffer+encryptedBufferDirtyLen
let outBuffer = decryptedBuffer+decryptedBufferFilledLen
CHACHA20_decrypt_bytes(&context, inBuffer, outBuffer, u32(inBufAvailable))
encryptedBufferDirtyLen += inBufAvailable
decryptedBufferAvailableLen -= inBufAvailable
}
}
private func writeOutTo(_ outBuffer: UnsafeMutablePointer<UInt8>, count: Int) -> Int {
let outLen = min(decryptedBufferReadyLen, count)
if outLen > 0 {
outBuffer.initialize(from: decryptedBuffer+decryptedBufferDirtyLen, count: outLen)
decryptedBufferDirtyLen += outLen
if decryptedBufferReadyLen == 0 {
(decryptedBufferDirtyLen, decryptedBufferAvailableLen) = (0, bufferSize)
}
}
return outLen
}
public func close() { }
}
@@ -0,0 +1,161 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
import Core
public final class ChaCha20OutputStream: OutputStream {
public static let defaultChunkSize = 1<<15
private var inChunkBuffer: UnsafeMutablePointer<UInt8>
private var outChunkBuffer: UnsafeMutablePointer<UInt8>
private let chunkBufferLen: Int
private let nestedStream: OutputStream
private let iv: [UInt8]
private let key: [UInt8]
private var isOpen = false
private var context = CHACHA20_ctx()
private var inChunkBufferDirtyLen: Int = 0
private var inChunkBufferAvailableLen: Int
private let blockLen: Int = Int(CHACHA20_BLOCKLENGTH)
public init(writingTo outputStream: OutputStream,
key: [UInt8],
iv: [UInt8],
chunkSize: Int = ChaCha20OutputStream.defaultChunkSize) {
self.nestedStream = outputStream
self.inChunkBuffer = UnsafeMutablePointer.allocate(capacity: chunkSize)
self.outChunkBuffer = UnsafeMutablePointer.allocate(capacity: chunkSize)
self.chunkBufferLen = chunkSize
self.key = key
self.iv = iv
self.inChunkBufferAvailableLen = chunkSize
}
deinit {
inChunkBuffer.deallocate()
outChunkBuffer.deallocate()
}
public var hasSpaceAvailable: Bool {
return nestedStream.hasSpaceAvailable
}
public func open() throws {
guard !isOpen else { fatalError("The stream can be opened only once") }
isOpen = true
guard key.count == ChaCha20KeySize else {
throw ChaCha20StreamError(kind: .keySizeError)
}
guard iv.count == ChaCha20IVSize else {
throw ChaCha20StreamError(kind: .ivSizeError)
}
CHACHA20_init();
let keySize = key.count
let ivSize = iv.count
key.withUnsafeBufferPointer { keyubp in
CHACHA20_keysetup(&context, keyubp.baseAddress!, u32(keySize*8), u32(ivSize*8))
}
iv.withUnsafeBufferPointer { ivubp in
CHACHA20_ivsetup(&context, ivubp.baseAddress!)
}
}
public func write(_ buffer: UnsafePointer<UInt8>, length: Int) throws {
guard isOpen else { fatalError("The stream is not opened") }
var remainingLen = length
var totalReadLen = 0
while remainingLen > 0 {
let tookLen = fillChunkBuffer(buffer+totalReadLen, remainingLen)
remainingLen -= tookLen
totalReadLen += tookLen
if try encryptChunkBuffer() == 0 { break }
}
}
private var isInChunkBufferFull: Bool {
return inChunkBufferAvailableLen == 0
}
private var inChunkBufferFilledLen: Int {
return chunkBufferLen-inChunkBufferAvailableLen
}
private var inChunkBufferReadyLen: Int {
return chunkBufferLen-inChunkBufferDirtyLen-inChunkBufferAvailableLen
}
private func fillChunkBuffer(_ buffer: UnsafePointer<UInt8>, _ length: Int) -> Int {
if isInChunkBufferFull {
return 0
}
let numOfBlocks = min(length, inChunkBufferAvailableLen)/blockLen
if numOfBlocks > 0 {
let tookLen = numOfBlocks*blockLen
let inBuf = inChunkBuffer+inChunkBufferFilledLen
inBuf.initialize(from: buffer, count: tookLen)
inChunkBufferAvailableLen -= tookLen
return tookLen
}
else {
let tookLen = min(length, inChunkBufferAvailableLen)
let inBuf = inChunkBuffer+inChunkBufferFilledLen
inBuf.initialize(from: buffer, count: tookLen)
inChunkBufferAvailableLen -= tookLen
return tookLen
}
}
private func encryptChunkBuffer() throws -> Int {
let numOfBlocks = inChunkBufferReadyLen/blockLen
let processBytesLen = numOfBlocks*blockLen
if numOfBlocks > 0 {
let inBuffer = inChunkBuffer+inChunkBufferDirtyLen
CHACHA20_encrypt_blocks(&context, inBuffer, outChunkBuffer, u32(numOfBlocks))
try nestedStream.write(outChunkBuffer, length: processBytesLen)
inChunkBufferDirtyLen += processBytesLen
if inChunkBufferDirtyLen == inChunkBufferFilledLen {
(inChunkBufferDirtyLen, inChunkBufferAvailableLen) = (0, chunkBufferLen)
}
}
return processBytesLen
}
public func close() throws {
guard isOpen else { fatalError("The stream is not opened") }
if inChunkBufferReadyLen > 0 {
let inBuffer = inChunkBuffer+inChunkBufferDirtyLen
CHACHA20_encrypt_bytes(&context, inBuffer, outChunkBuffer, u32(inChunkBufferReadyLen))
try nestedStream.write(outChunkBuffer, length: inChunkBufferReadyLen)
}
}
}
+64
View File
@@ -0,0 +1,64 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
public final class FileInputStream: InputStream {
private let fileHandle: FileHandle
private var eofReached = false
public init(with fileHandle: FileHandle) {
self.fileHandle = fileHandle
}
public init?(with localFileURL: URL) {
guard let fileHandle = try? FileHandle(forReadingFrom: localFileURL) else {
return nil
}
self.fileHandle = fileHandle
}
public var hasBytesAvailable: Bool {
return !eofReached
}
public func open() throws {
}
public func read(_ buffer: UnsafeMutablePointer<UInt8>, maxLength len: Int) -> Int {
let readData = fileHandle.readData(ofLength: len)
eofReached = (readData.count != len)
readData.withUnsafeBytes {
if let baseAddress = $0.baseAddress {
let t = baseAddress.assumingMemoryBound(to: UInt8.self)
buffer.initialize(from: t, count: readData.count)
}
}
return readData.count
}
public func close() {
}
}
+57
View File
@@ -0,0 +1,57 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
public final class FileOutputStream: OutputStream {
private let fileHandle: FileHandle
public init(with fileHandle: FileHandle) {
self.fileHandle = fileHandle
}
public init?(with localFileURL: URL) {
guard
FileManager.default.createFile(atPath: localFileURL.path, contents: nil),
let fileHandle = try? FileHandle(forWritingTo: localFileURL) else {
return nil
}
self.fileHandle = fileHandle
}
public var hasSpaceAvailable: Bool {
return true
}
public func open() throws { }
public func write(_ buffer: UnsafePointer<UInt8>, length: Int) throws {
let data = Data(bytes: buffer, count: length)
fileHandle.write(data)
}
public func close() throws {
}
}
+170
View File
@@ -0,0 +1,170 @@
//
// MIT License
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
import zlib
public final class GzipInputStream: InputStream {
public static let defaultDeflateChunkSize = 1<<15
public static let defaultInflateChunkSize = 1<<15
private var zstream: z_stream
private let nestedStream: InputStream
private let inflateBufferSize: Int
private let deflateBufferSize: Int
private var deflateBuffer: UnsafeMutablePointer<UInt8>
private var inflateBuffer: UnsafeMutablePointer<UInt8>
private var inflateUsedLen: Int
private var inflateAvailableLen: Int
private var eofReached = false
private var isOpen = false
private var status: Int32 = Z_OK
private var windowBits: Int32
/// - Parameters:
/// - inputStream: <#inputStream description#>
/// - windowBits: shall be a base 2 logarithm of the maximum window size to use, and shall be a value between 9 and 15. If the input data was compressed with a larger window size, subsequent attempts to decompress this data will fail with Z_DATA_ERROR, rather than try to allocate a larger window.
/// - deflateBufferSize: <#deflateBufferSize description#>
/// - inflateBufferSize: <#inflateBufferSize description#>
public init(readingFrom nestedStream: InputStream,
windowBits: Int32 = MAX_WBITS + 16,
deflateChunkSize: Int = GzipInputStream.defaultDeflateChunkSize,
inflateChunkSize: Int = GzipInputStream.defaultInflateChunkSize) {
self.nestedStream = nestedStream
self.windowBits = windowBits
self.deflateBufferSize = deflateChunkSize
self.inflateBufferSize = inflateChunkSize
self.deflateBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: deflateBufferSize)
self.inflateBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: inflateBufferSize)
self.inflateUsedLen = 0
self.inflateAvailableLen = inflateChunkSize
zstream = z_stream()
}
deinit {
deflateBuffer.deallocate()
inflateBuffer.deallocate()
}
public func open() throws {
guard !isOpen else { fatalError("The stream can be opened only once") }
isOpen = true
let streamSize = Int32(MemoryLayout<z_stream>.size)
let zlibVersion = ZLIB_VERSION
status = inflateInit2_(&zstream, windowBits, zlibVersion, streamSize)
guard status == Z_OK else {
throw GzipStreamError(code: status, description: zstream.msg)
}
zstream.avail_in = 0
zstream.avail_out = 0
zstream.next_in = nil
zstream.next_out = nil
}
public var hasBytesAvailable: Bool {
return !eofReached || bytesReadyToRead > 0
}
private var bytesReadyToRead: Int {
return inflateBufferSize-inflateUsedLen-inflateAvailableLen
}
private var isInflateBufferFull: Bool {
return inflateAvailableLen == 0
}
public func read(_ outBuffer: UnsafeMutablePointer<UInt8>, maxLength: Int) throws -> Int {
guard isOpen else { fatalError("The stream is not opened") }
var totalReadLen = 0
var remainingLen = maxLength
while remainingLen > 0 && hasBytesAvailable {
try fillInflateBuffer()
let copiedLen = copyReadyBytesTo(outBuffer+totalReadLen, count: remainingLen)
totalReadLen += copiedLen
remainingLen -= copiedLen
}
return totalReadLen
}
private func copyReadyBytesTo(_ outBuffer: UnsafeMutablePointer<UInt8>, count: Int) -> Int {
let outLen = min(bytesReadyToRead, count)
outBuffer.initialize(from: inflateBuffer+inflateUsedLen, count: outLen)
inflateUsedLen += outLen
if bytesReadyToRead == 0 {
(inflateUsedLen, inflateAvailableLen) = (0, inflateBufferSize)
}
return outLen
}
private func fillInflateBuffer() throws {
if isInflateBufferFull {
return
}
zstream.next_out = (inflateBuffer+inflateBufferSize-inflateAvailableLen)
zstream.avail_out = uInt(inflateAvailableLen)
if zstream.avail_in == 0 {
let readLen = try nestedStream.read(deflateBuffer, maxLength: deflateBufferSize)
zstream.next_in = deflateBuffer
zstream.avail_in = uInt(readLen)
if readLen == 0 {
eofReached = true
try finalizeInflate()
return
}
}
status = inflate(&zstream, Z_NO_FLUSH)
if status == Z_STREAM_END {
eofReached = true
try finalizeInflate()
}
guard status == Z_OK else {
throw GzipStreamError(code: status, description: zstream.msg)
}
inflateAvailableLen = Int(zstream.avail_out)
}
private func finalizeInflate() throws {
status = inflateEnd(&zstream)
if status != Z_OK {
throw GzipStreamError(code: status, description: zstream.msg)
}
}
public func close() {
guard isOpen else { fatalError("The stream is not opened") }
_ = inflateEnd(&zstream)
nestedStream.close()
}
}
+137
View File
@@ -0,0 +1,137 @@
//
// MIT License
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
import zlib
public final class GzipOutputStream: OutputStream {
public static let defaultDeflateChunkSize = 1 << 14
private let nestedStream: OutputStream
private var zstream: z_stream
private var deflateBuffer: UnsafeMutablePointer<UInt8>
private let deflateBufferSize: Int
private var isOpen = false
private let windowBits: Int32
private let compressionLevel: Int32
private var status: Int32 = Z_OK
public var hasSpaceAvailable: Bool {
return true
}
/// returns Z_STREAM_ERROR if level is not a valid compression level
/// - Parameters:
/// - windowBits: shall be a base 2 logarithm of the maximum window size to use, and shall be a value between 9 and 15.
///
public init(writingTo nestedStream: OutputStream,
windowBits: Int32 = MAX_WBITS + 16,
deflateChunkSize: Int = defaultDeflateChunkSize,
compressionLevel: GzipCompressionLevel = .defaultCompression) {
self.nestedStream = nestedStream
self.windowBits = windowBits
self.deflateBufferSize = deflateChunkSize
self.compressionLevel = compressionLevel
self.deflateBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: deflateBufferSize)
zstream = z_stream()
}
deinit {
deflateBuffer.deallocate()
}
public func open() throws {
guard !isOpen else { fatalError("The stream can be opened only once") }
isOpen = true
let zlibVersion = ZLIB_VERSION
let streamSize = MemoryLayout<z_stream>.size
status = deflateInit2_(&zstream,
compressionLevel,
Z_DEFLATED,
windowBits,
MAX_MEM_LEVEL,
Z_DEFAULT_STRATEGY,
zlibVersion,
Int32(streamSize))
guard status == Z_OK else {
throw GzipStreamError(code: status, description: zstream.msg)
}
}
public func write(_ buffer: UnsafePointer<UInt8>, length: Int) throws {
guard isOpen else { fatalError("The stream is not opened") }
zstream.next_in = UnsafeMutablePointer(mutating: buffer)
zstream.avail_in = UInt32(length)
do {
while zstream.avail_in > 0 {
repeat {
zstream.avail_out = UInt32(deflateBufferSize)
zstream.next_out = deflateBuffer
status = deflate(&zstream, Z_NO_FLUSH)
guard status != Z_STREAM_ERROR else {
throw GzipStreamError(code: status, description: zstream.msg)
}
let outBytesLength = deflateBufferSize-Int(zstream.avail_out)
try nestedStream.write(deflateBuffer, length: outBytesLength)
} while zstream.avail_out == 0
}
}
catch {
deflateEnd(&zstream)
throw error
}
}
public func close() throws {
guard isOpen else { fatalError("The stream is not opened") }
zstream.avail_in = 0
zstream.next_in = nil
do {
repeat {
zstream.avail_out = UInt32(deflateBufferSize)
zstream.next_out = deflateBuffer
status = deflate(&zstream, Z_FINISH)
guard status != Z_STREAM_ERROR else {
throw GzipStreamError(code: status, description: zstream.msg)
}
let outBytesCount = deflateBufferSize-Int(zstream.avail_out)
try nestedStream.write(deflateBuffer, length: outBytesCount)
} while status != Z_STREAM_END
deflateEnd(&zstream)
}
catch {
deflateEnd(&zstream)
throw error
}
}
}
+77
View File
@@ -0,0 +1,77 @@
//
// MIT License
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
import zlib
public typealias GzipCompressionLevel = Int32
public extension GzipCompressionLevel {
static let noCompression = Z_NO_COMPRESSION
static let bestSpeed = Z_BEST_SPEED
static let bestCompression = Z_BEST_COMPRESSION
static let defaultCompression = Z_DEFAULT_COMPRESSION
}
public struct GzipStreamError: LocalizedError {
public enum Kind {
case streamError
case dataError
case memoryError
case bufferError
case versionError
case otherError(code: Int32)
}
public let file: String
public let line: Int
public let code: Int
public let description: String?
public let kind: Kind
internal init(file: String = #file, line: Int = #line, code: Int32, description dPtr: UnsafePointer<CChar>?) {
self.file = String(describing: file)
self.code = Int(code)
self.line = line
self.description = dPtr.flatMap { String(cString: $0, encoding: .utf8) }
switch code {
case Z_STREAM_ERROR: // -2
kind = .streamError
case Z_DATA_ERROR: // -3
kind = .dataError
case Z_MEM_ERROR: // -4
kind = .memoryError
case Z_BUF_ERROR: // -5
kind = .bufferError
case Z_VERSION_ERROR: // -6
kind = .versionError
default:
kind = .otherError(code: code)
}
}
public var errorDescription: String? {
return "\(file):\(line) " + (description ?? "")
}
}
+46
View File
@@ -0,0 +1,46 @@
//
// MIT License
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
public protocol InputStream {
var hasBytesAvailable: Bool { get }
func open() throws
func read(_ toBuffer: UnsafeMutablePointer<UInt8>, maxLength len: Int) throws -> Int
func close()
}
public extension InputStream {
func readToEnd(_ chunkLen: Int = 1<<15) throws -> Data {
var result = Data()
var tmpBuffer = Array<UInt8>(repeating: 0, count: chunkLen)
while hasBytesAvailable {
let readLen = try read(&tmpBuffer, maxLength: chunkLen)
result.append(&tmpBuffer, count: readLen)
}
return result
}
}
+48
View File
@@ -0,0 +1,48 @@
//
// MIT License
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
public protocol OutputStream {
var hasSpaceAvailable: Bool { get }
func open() throws
func write(_ buffer: UnsafePointer<UInt8>, length: Int) throws
func close() throws
}
public extension OutputStream {
func write(_ data: Data) throws {
try data.withUnsafeBytes { urbp in
try urbp.withMemoryRebound(to: UInt8.self) { buffer in
try buffer.baseAddress.map { try write($0, length: data.count) }
}
}
}
func write(_ string: String, ofEncoding encoding: String.Encoding) throws {
try string.data(using: encoding).map { try write($0) }
}
}
+50
View File
@@ -0,0 +1,50 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
let Salsa20IVSize = 8
let Salsa20KeySize128 = 16
let Salsa20KeySize256 = 32
public struct Salsa20StreamError: LocalizedError {
public enum Kind {
case keySizeError
case ivSizeError
case bufferTooSmall
}
public let file: String
public let line: Int
public let kind: Kind
internal init(file: String = #file, line: Int = #line, kind: Kind) {
self.file = String(describing: file)
self.line = line
self.kind = kind
}
}
@@ -0,0 +1,195 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
import Core
public final class Salsa20InputStream: InputStream {
public static let defaultChunkSize = 1<<15
private let nestedStream: InputStream
private let bufferSize: Int
private var encryptedBuffer: UnsafeMutablePointer<UInt8>
private var decryptedBuffer: UnsafeMutablePointer<UInt8>
private let key: [UInt8]
private let iv: [UInt8]
private var isOpen = false
private var eofReached = false
private var encryptedBufferDirtyLen: Int = 0
private var encryptedBufferAvailableLen: Int
private var decryptedBufferDirtyLen: Int = 0
private var decryptedBufferAvailableLen: Int
private var context = SALSA20_ctx()
private let salsa20BlockLen: Int = Int(SALSA20_BLOCKLENGTH)
public init(readingFrom nestedStream: InputStream,
key: [UInt8],
iv: [UInt8],
chunkSize: Int = Salsa20InputStream.defaultChunkSize) {
self.nestedStream = nestedStream
self.key = key
self.encryptedBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: chunkSize)
self.decryptedBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: chunkSize)
self.bufferSize = chunkSize
self.decryptedBufferAvailableLen = chunkSize
self.encryptedBufferAvailableLen = chunkSize
self.iv = iv
}
deinit {
encryptedBuffer.deallocate()
decryptedBuffer.deallocate()
}
public var hasBytesAvailable: Bool {
return !eofReached || encryptedBufferReadyLen > 0 || decryptedBufferReadyLen > 0
}
public func open() throws {
guard !isOpen else { fatalError("The stream can be opened only once") }
isOpen = true
guard [Salsa20KeySize128, Salsa20KeySize256].contains(key.count) else {
throw Salsa20StreamError(kind: .keySizeError)
}
guard iv.count == Salsa20IVSize else {
throw Salsa20StreamError(kind: .ivSizeError)
}
SALSA20_init();
let keySize = key.count
let ivSize = iv.count
key.withUnsafeBufferPointer { keyubp in
SALSA20_keysetup(&context, keyubp.baseAddress!, u32(keySize*8), u32(ivSize*8))
}
iv.withUnsafeBufferPointer { ivubp in
SALSA20_ivsetup(&context, ivubp.baseAddress!)
}
}
public func read(_ outBuffer: UnsafeMutablePointer<UInt8>, maxLength: Int) throws -> Int {
guard isOpen else { fatalError("The stream is not opened") }
var totalReadCount = 0
var remainingLen = maxLength
while remainingLen > 0 && hasBytesAvailable {
try fillEncryptedBuffer()
if nestedStream.hasBytesAvailable {
try decryptBlocks()
}
else {
try decryptRemaining()
}
let readCount = writeOutTo(outBuffer+totalReadCount, count: remainingLen)
if readCount == 0 { break }
remainingLen -= readCount
totalReadCount += readCount
}
return totalReadCount
}
private var encryptedBufferReadyLen: Int {
return bufferSize-encryptedBufferDirtyLen-encryptedBufferAvailableLen
}
private var encryptedBufferFilledLen: Int {
return bufferSize-encryptedBufferAvailableLen
}
private var isEncryptedBufferFull: Bool {
return encryptedBufferAvailableLen == 0
}
private func fillEncryptedBuffer() throws {
if eofReached || isEncryptedBufferFull {
return
}
let canTake = encryptedBufferAvailableLen/salsa20BlockLen*salsa20BlockLen
let inBuf = encryptedBuffer+encryptedBufferFilledLen
let readLen = try nestedStream.read(inBuf, maxLength: canTake)
if !nestedStream.hasBytesAvailable {
eofReached = true
}
encryptedBufferAvailableLen -= readLen
}
private var decryptedBufferReadyLen: Int {
return bufferSize-decryptedBufferDirtyLen-decryptedBufferAvailableLen
}
private var decryptedBufferFilledLen: Int {
return bufferSize-decryptedBufferAvailableLen
}
private func decryptBlocks() throws {
if decryptedBufferReadyLen > SALSA20_BLOCKLENGTH {
return
}
let inBufAvailable = encryptedBufferReadyLen
let outBufAvailable = decryptedBufferAvailableLen
let numOfBlocks = min(inBufAvailable,outBufAvailable)/salsa20BlockLen
if numOfBlocks > 0 {
let processBytesLen = numOfBlocks*salsa20BlockLen
let inBuffer = encryptedBuffer+encryptedBufferDirtyLen
let outBuffer = decryptedBuffer+decryptedBufferFilledLen
SALSA20_decrypt_blocks(&context, inBuffer, outBuffer, u32(numOfBlocks))
encryptedBufferDirtyLen += processBytesLen
if encryptedBufferReadyLen == 0 {
(encryptedBufferDirtyLen, encryptedBufferAvailableLen) = (0, bufferSize)
}
decryptedBufferAvailableLen -= processBytesLen
}
}
private func decryptRemaining() throws {
let inBufAvailable = encryptedBufferReadyLen
let outBufAvailable = decryptedBufferAvailableLen
if inBufAvailable > 0 && outBufAvailable >= inBufAvailable {
let inBuffer = encryptedBuffer+encryptedBufferDirtyLen
let outBuffer = decryptedBuffer+decryptedBufferFilledLen
SALSA20_decrypt_bytes(&context, inBuffer, outBuffer, u32(inBufAvailable))
encryptedBufferDirtyLen += inBufAvailable
decryptedBufferAvailableLen -= inBufAvailable
}
}
private func writeOutTo(_ outBuffer: UnsafeMutablePointer<UInt8>, count: Int) -> Int {
let outLen = min(decryptedBufferReadyLen, count)
if outLen > 0 {
outBuffer.initialize(from: decryptedBuffer+decryptedBufferDirtyLen, count: outLen)
decryptedBufferDirtyLen += outLen
if decryptedBufferReadyLen == 0 {
(decryptedBufferDirtyLen, decryptedBufferAvailableLen) = (0, bufferSize)
}
}
return outLen
}
public func close() { }
}
@@ -0,0 +1,162 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
import Core
public final class Salsa20OutputStream: OutputStream {
public static let defaultChunkSize = 1<<15
private var inChunkBuffer: UnsafeMutablePointer<UInt8>
private var outChunkBuffer: UnsafeMutablePointer<UInt8>
private let chunkBufferLen: Int
private let nestedStream: OutputStream
private let iv: [UInt8]
private let key: [UInt8]
private var isOpen = false
private var context = SALSA20_ctx()
private var inChunkBufferDirtyLen: Int = 0
private var inChunkBufferAvailableLen: Int
private let salsa20BlockLen: Int = Int(SALSA20_BLOCKLENGTH)
public init(writingTo outputStream: OutputStream,
key: [UInt8],
iv: [UInt8],
chunkSize: Int = Salsa20OutputStream.defaultChunkSize) {
self.nestedStream = outputStream
self.inChunkBuffer = UnsafeMutablePointer.allocate(capacity: chunkSize)
self.outChunkBuffer = UnsafeMutablePointer.allocate(capacity: chunkSize)
self.chunkBufferLen = chunkSize
self.key = key
self.iv = iv
self.inChunkBufferAvailableLen = chunkSize
}
deinit {
inChunkBuffer.deallocate()
outChunkBuffer.deallocate()
}
public var hasSpaceAvailable: Bool {
return nestedStream.hasSpaceAvailable
}
public func open() throws {
guard !isOpen else { fatalError("The stream can be opened only once") }
isOpen = true
guard [Salsa20KeySize128, Salsa20KeySize256].contains(key.count) else {
throw Salsa20StreamError(kind: .keySizeError)
}
guard iv.count == Salsa20IVSize else {
throw Salsa20StreamError(kind: .ivSizeError)
}
SALSA20_init();
let keySize = key.count
let ivSize = iv.count
key.withUnsafeBufferPointer { keyubp in
SALSA20_keysetup(&context, keyubp.baseAddress!, u32(keySize*8), u32(ivSize*8))
}
iv.withUnsafeBufferPointer { ivubp in
SALSA20_ivsetup(&context, ivubp.baseAddress!)
}
}
public func write(_ buffer: UnsafePointer<UInt8>, length: Int) throws {
guard isOpen else { fatalError("The stream is not opened") }
var remainingLen = length
var totalReadLen = 0
while remainingLen > 0 {
let tookLen = fillChunkBuffer(buffer+totalReadLen, remainingLen)
remainingLen -= tookLen
totalReadLen += tookLen
if try encryptChunkBuffer() == 0 { break }
}
}
private var isInChunkBufferFull: Bool {
return inChunkBufferAvailableLen == 0
}
private var inChunkBufferFilledLen: Int {
return chunkBufferLen-inChunkBufferAvailableLen
}
private var inChunkBufferReadyLen: Int {
return chunkBufferLen-inChunkBufferDirtyLen-inChunkBufferAvailableLen
}
private func fillChunkBuffer(_ buffer: UnsafePointer<UInt8>, _ length: Int) -> Int {
if isInChunkBufferFull {
return 0
}
let numOfBlocks = min(length, inChunkBufferAvailableLen)/salsa20BlockLen
if numOfBlocks > 0 {
let tookLen = numOfBlocks*salsa20BlockLen
let inBuf = inChunkBuffer+inChunkBufferFilledLen
inBuf.initialize(from: buffer, count: tookLen)
inChunkBufferAvailableLen -= tookLen
return tookLen
}
else {
let tookLen = min(length, inChunkBufferAvailableLen)
let inBuf = inChunkBuffer+inChunkBufferFilledLen
inBuf.initialize(from: buffer, count: tookLen)
inChunkBufferAvailableLen -= tookLen
return tookLen
}
}
private func encryptChunkBuffer() throws -> Int {
let numOfBlocks = inChunkBufferReadyLen/salsa20BlockLen
let processBytesLen = numOfBlocks*salsa20BlockLen
if numOfBlocks > 0 {
let inBuffer = inChunkBuffer+inChunkBufferDirtyLen
SALSA20_encrypt_blocks(&context, inBuffer, outChunkBuffer, u32(numOfBlocks))
try nestedStream.write(outChunkBuffer, length: processBytesLen)
inChunkBufferDirtyLen += processBytesLen
if inChunkBufferDirtyLen == inChunkBufferFilledLen {
(inChunkBufferDirtyLen, inChunkBufferAvailableLen) = (0, chunkBufferLen)
}
}
return processBytesLen
}
public func close() throws {
guard isOpen else { fatalError("The stream is not opened") }
if inChunkBufferReadyLen > 0 {
let inBuffer = inChunkBuffer+inChunkBufferDirtyLen
SALSA20_encrypt_bytes(&context, inBuffer, outChunkBuffer, u32(inChunkBufferReadyLen))
try nestedStream.write(outChunkBuffer, length: inChunkBufferReadyLen)
}
}
}
+85
View File
@@ -0,0 +1,85 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
let TwoFishIVSize = 16
public struct TwoFishStreamError: LocalizedError {
public enum Kind {
case fillKeyedSBoxes
case notInitialized
case illegalKeyLength
case platformUnsuitableUInt32
case platformUnsuitableByte
case platformGet32ImplementedImproperly
case platformPut32ImplementedImproperly
case platformRolRoRImplementedImproperly
case platformBSwapUndefined
case platformSelectByteTestImplementedImproperly
case testEncryptionFail
case testDecryptionFail
case testSequenceEncryptionFail
case testSequenceDecryptionFail
case testOddSizedKeysFail
case keySizeError
case ivSizeError
case dataNotAligned
case otherError(code: Int32)
}
public let file: String
public let line: Int
public let kind: Kind
internal init(file: String = #file, line: Int = #line, kind: Kind) {
self.file = String(describing: file)
self.line = line
self.kind = kind
}
internal init(file: String = #file, line: Int = #line, code: Int32) {
self.file = String(describing: file)
self.line = line
let mapper: [Int32: Kind] = [
1: .fillKeyedSBoxes,
2: .notInitialized,
3: .illegalKeyLength,
101: .platformUnsuitableUInt32,
102: .platformUnsuitableByte,
103: .platformGet32ImplementedImproperly,
104: .platformPut32ImplementedImproperly,
105: .platformRolRoRImplementedImproperly,
106: .platformBSwapUndefined,
107: .platformSelectByteTestImplementedImproperly,
108: .testEncryptionFail,
109: .testDecryptionFail,
110: .testSequenceEncryptionFail,
111: .testSequenceDecryptionFail,
112: .testOddSizedKeysFail
]
self.kind = mapper[code] ?? .otherError(code: code)
}
}
@@ -0,0 +1,227 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
import Core
public final class TwoFishInputStream: InputStream {
public static let defaultChunkSize = 1<<15
private let nestedStream: InputStream
private let bufferSize: Int
private var encryptedBuffer: UnsafeMutablePointer<UInt8>
private var decryptedBuffer: UnsafeMutablePointer<UInt8>
private let key: [UInt8]
private var iv: [UInt8]
private var isOpen = false
private var eofReached = false
private var encryptedBufferDirtyLen: Int = 0
private var encryptedBufferAvailableLen: Int
private var decryptedBufferDirtyLen: Int = 0
private var decryptedBufferAvailableLen: Int
private var context: Twofish_key
private let blockLen = 16
private var status: Int32 = 0
public init(readingFrom nestedStream: InputStream,
key: [UInt8],
iv: [UInt8],
chunkSize: Int = TwoFishInputStream.defaultChunkSize) {
self.nestedStream = nestedStream
self.key = key
self.encryptedBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: chunkSize)
self.decryptedBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: chunkSize)
self.bufferSize = chunkSize
self.decryptedBufferAvailableLen = chunkSize
self.encryptedBufferAvailableLen = chunkSize
self.iv = iv
self.context = Twofish_key()
}
deinit {
encryptedBuffer.deallocate()
decryptedBuffer.deallocate()
}
public var hasBytesAvailable: Bool {
return !eofReached || encryptedBufferReadyLen > 0 || decryptedBufferReadyLen > 0
}
public func open() throws {
guard !isOpen else { fatalError("The stream can be opened only once") }
isOpen = true
guard iv.count == TwoFishIVSize else {
throw TwoFishStreamError(kind: .ivSizeError)
}
status = Twofish_initialise();
guard status == TWOFISH_SUCCESS.rawValue else {
throw TwoFishStreamError(code: status)
}
var key = key
status = Twofish_prepare_key(&key, Int32(key.count), &context)
guard status == TWOFISH_SUCCESS.rawValue else {
throw TwoFishStreamError(code: status)
}
}
public func read(_ outBuffer: UnsafeMutablePointer<UInt8>, maxLength: Int) throws -> Int {
guard isOpen else { fatalError("The stream is not opened") }
var totalReadCount = 0
var remainingLen = maxLength
while remainingLen > 0 && hasBytesAvailable {
try fillEncryptedBuffer()
if nestedStream.hasBytesAvailable {
try decryptBlocks()
}
else {
try decryptRemaining()
}
let readCount = writeOutTo(outBuffer+totalReadCount, count: remainingLen)
if readCount == 0 { break }
remainingLen -= readCount
totalReadCount += readCount
}
return totalReadCount
}
private var encryptedBufferReadyLen: Int {
return bufferSize-encryptedBufferDirtyLen-encryptedBufferAvailableLen
}
private var encryptedBufferFilledLen: Int {
return bufferSize-encryptedBufferAvailableLen
}
private var isEncryptedBufferFull: Bool {
return encryptedBufferAvailableLen == 0
}
private func fillEncryptedBuffer() throws {
if eofReached || isEncryptedBufferFull {
return
}
let canTake = encryptedBufferAvailableLen/blockLen*blockLen
let inBuf = encryptedBuffer+encryptedBufferFilledLen
let readLen = try nestedStream.read(inBuf, maxLength: canTake)
if !nestedStream.hasBytesAvailable {
eofReached = true
}
encryptedBufferAvailableLen -= readLen
}
private var decryptedBufferReadyLen: Int {
return bufferSize-decryptedBufferDirtyLen-decryptedBufferAvailableLen
}
private var decryptedBufferFilledLen: Int {
return bufferSize-decryptedBufferAvailableLen
}
private func decryptBlocks() throws {
if decryptedBufferReadyLen > blockLen {
return
}
let inBufAvailable = encryptedBufferReadyLen
let outBufAvailable = decryptedBufferAvailableLen
let numOfBlocks = min(inBufAvailable,outBufAvailable)/blockLen
if numOfBlocks > 0 {
let processBytesLen = numOfBlocks*blockLen
let inBuffer = encryptedBuffer+encryptedBufferDirtyLen
let outBuffer = decryptedBuffer+decryptedBufferFilledLen
for n in 0..<numOfBlocks {
let blockStartPos = n*blockLen
Twofish_decrypt(&context, inBuffer+blockStartPos, outBuffer+blockStartPos)
for i in 0..<blockLen {
(outBuffer+blockStartPos)[i] ^= iv[i]
}
memcpy(&iv, inBuffer+blockStartPos, blockLen)
}
encryptedBufferDirtyLen += processBytesLen
if encryptedBufferReadyLen == 0 {
(encryptedBufferDirtyLen, encryptedBufferAvailableLen) = (0, bufferSize)
}
decryptedBufferAvailableLen -= processBytesLen
}
}
private func decryptRemaining() throws {
if encryptedBufferReadyLen > 0 && decryptedBufferAvailableLen >= encryptedBufferReadyLen {
let numOfBlocks = min(encryptedBufferReadyLen,decryptedBufferAvailableLen)/blockLen
if numOfBlocks > 0 {
for _ in 0..<numOfBlocks {
let inBuffer = encryptedBuffer+encryptedBufferDirtyLen
let outBuffer = decryptedBuffer+decryptedBufferFilledLen
Twofish_decrypt(&context, inBuffer, outBuffer)
for i in 0..<blockLen {
(outBuffer)[i] ^= iv[i]
}
memcpy(&iv, inBuffer, blockLen)
encryptedBufferDirtyLen += blockLen
if encryptedBufferReadyLen == 0 {
(encryptedBufferDirtyLen, encryptedBufferAvailableLen) = (0, bufferSize)
}
decryptedBufferAvailableLen -= blockLen
}
}
guard encryptedBufferReadyLen%blockLen == 0 else {
throw TwoFishStreamError(kind: .dataNotAligned)
}
if !nestedStream.hasBytesAvailable && encryptedBufferReadyLen == 0 {
removePaddinDataFromDecryptedBuffer()
}
}
}
private func removePaddinDataFromDecryptedBuffer() {
if decryptedBufferReadyLen > 0 {
let lastByte = (decryptedBuffer+decryptedBufferFilledLen-1).pointee
decryptedBufferAvailableLen += Int(lastByte)
}
}
private func writeOutTo(_ outBuffer: UnsafeMutablePointer<UInt8>, count: Int) -> Int {
let outLen = min(decryptedBufferReadyLen, count)
if outLen > 0 {
outBuffer.initialize(from: decryptedBuffer+decryptedBufferDirtyLen, count: outLen)
decryptedBufferDirtyLen += outLen
if decryptedBufferReadyLen == 0 {
(decryptedBufferDirtyLen, decryptedBufferAvailableLen) = (0, bufferSize)
}
}
return outLen
}
public func close() {
guard isOpen else { fatalError("The stream is not opened") }
}
}
@@ -0,0 +1,172 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
import Core
public final class TwoFishOutputStream: OutputStream {
public static let defaultChunkSize = 1<<15
private var inChunkBuffer: UnsafeMutablePointer<UInt8>
private var outChunkBuffer: UnsafeMutablePointer<UInt8>
private let chunkBufferLen: Int
private let nestedStream: OutputStream
private var iv: [UInt8]
private let key: [UInt8]
private var isOpen = false
private var context: Twofish_key
private var inChunkBufferDirtyLen: Int = 0
private var inChunkBufferAvailableLen: Int
private let blockLen = 16
private var status: Int32 = 0
public init(writingTo outputStream: OutputStream,
key: [UInt8],
iv: [UInt8],
chunkSize: Int = TwoFishOutputStream.defaultChunkSize) {
self.nestedStream = outputStream
self.inChunkBuffer = UnsafeMutablePointer.allocate(capacity: chunkSize)
self.outChunkBuffer = UnsafeMutablePointer.allocate(capacity: chunkSize)
self.chunkBufferLen = chunkSize
self.key = key
self.iv = iv
self.inChunkBufferAvailableLen = chunkSize
self.context = Twofish_key()
}
deinit {
inChunkBuffer.deallocate()
}
public var hasSpaceAvailable: Bool {
return nestedStream.hasSpaceAvailable
}
public func open() throws {
guard !isOpen else { fatalError("The stream can be opened only once") }
isOpen = true
guard iv.count == TwoFishIVSize else {
throw TwoFishStreamError(kind: .ivSizeError)
}
status = Twofish_initialise();
guard status == TWOFISH_SUCCESS.rawValue else {
throw TwoFishStreamError(code: status)
}
var key = key
status = Twofish_prepare_key(&key, Int32(key.count), &context)
guard status == TWOFISH_SUCCESS.rawValue else {
throw TwoFishStreamError(code: status)
}
}
public func write(_ buffer: UnsafePointer<UInt8>, length: Int) throws {
guard isOpen else { fatalError("The stream is not opened") }
var remainingLen = length
var totalReadLen = 0
while remainingLen > 0 {
let tookLen = fillChunkBuffer(buffer+totalReadLen, remainingLen)
remainingLen -= tookLen
totalReadLen += tookLen
if try encryptChunkBuffer() == 0 { break }
}
}
private var isInChunkBufferFull: Bool {
return inChunkBufferAvailableLen == 0
}
private var inChunkBufferFilledLen: Int {
return chunkBufferLen-inChunkBufferAvailableLen
}
private var inChunkBufferReadyLen: Int {
return chunkBufferLen-inChunkBufferDirtyLen-inChunkBufferAvailableLen
}
private func fillChunkBuffer(_ buffer: UnsafePointer<UInt8>, _ length: Int) -> Int {
if isInChunkBufferFull {
return 0
}
let numOfBlocks = min(length, inChunkBufferAvailableLen)/blockLen
if numOfBlocks > 0 {
let tookLen = numOfBlocks*blockLen
let inBuf = inChunkBuffer+inChunkBufferFilledLen
inBuf.initialize(from: buffer, count: tookLen)
inChunkBufferAvailableLen -= tookLen
return tookLen
}
else {
let tookLen = min(length, inChunkBufferAvailableLen)
let inBuf = inChunkBuffer+inChunkBufferFilledLen
inBuf.initialize(from: buffer, count: tookLen)
inChunkBufferAvailableLen -= tookLen
return tookLen
}
}
private func encryptChunkBuffer() throws -> Int {
let numOfBlocks = inChunkBufferReadyLen/blockLen
let processBytesLen = numOfBlocks*blockLen
if numOfBlocks > 0 {
let inBuffer = inChunkBuffer+inChunkBufferDirtyLen
var block: [UInt8] = Array(repeating: 0, count: blockLen)
for n in 0..<numOfBlocks {
let blockStartPos = n*blockLen
for i in 0..<blockLen {
block[i] = inBuffer[blockStartPos + i]^iv[i]
}
Twofish_encrypt(&context, &block, outChunkBuffer+blockStartPos)
for i in 0..<blockLen {
iv[i] = (outChunkBuffer+blockStartPos)[i]
}
}
try nestedStream.write(outChunkBuffer, length: processBytesLen)
inChunkBufferDirtyLen += processBytesLen
if inChunkBufferDirtyLen == inChunkBufferFilledLen {
(inChunkBufferDirtyLen, inChunkBufferAvailableLen) = (0, chunkBufferLen)
}
}
return processBytesLen
}
public func close() throws {
guard isOpen else { fatalError("The stream is not opened") }
let paddingLen = blockLen-inChunkBufferReadyLen
let inBuffer = inChunkBuffer+inChunkBufferDirtyLen
(inBuffer+inChunkBufferFilledLen).initialize(repeating: UInt8(paddingLen), count: paddingLen)
inChunkBufferAvailableLen -= paddingLen
var block: [UInt8] = Array(repeating: 0, count: blockLen)
for i in 0..<blockLen {
block[i] = inBuffer[i] ^ iv[i]
}
Twofish_encrypt(&context, &block, outChunkBuffer)
try nestedStream.write(outChunkBuffer, length: inChunkBufferReadyLen)
}
}
+41
View File
@@ -0,0 +1,41 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
extension Data {
init(fromHex str: String) {
let stippedSpaces = str.filter { $0 != " " }.map { $0 }
let pairs = stride(from: 0, to: stippedSpaces.endIndex, by: 2)
.map { (stippedSpaces[$0], $0 < stippedSpaces.index(before: stippedSpaces.endIndex) ? stippedSpaces[$0.advanced(by: 1)] : nil) }
let array: [UInt8] = pairs.compactMap { pair in
let string = String(format: "%@%@", "\(pair.0)", pair.1 != nil ? "\(pair.1!)":"")
let byte = UInt8(string, radix: 16)
return byte
}
self = Data(array)
}
}
+60
View File
@@ -0,0 +1,60 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
import CommonCrypto
public func md5(_ url: URL) -> [UInt8]? {
let bufferSize = 1<<11
do {
let file = try FileHandle(forReadingFrom: url)
defer {
file.closeFile()
}
var context = CC_MD5_CTX()
CC_MD5_Init(&context)
while autoreleasepool(invoking: {
let data = file.readData(ofLength: bufferSize)
if data.count > 0 {
data.withUnsafeBytes {
_ = CC_MD5_Update(&context, $0.baseAddress, numericCast(data.count))
}
return true
}
else {
return false
}
}) { }
var digest: [UInt8] = Array(repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
_ = CC_MD5_Final(&digest, &context)
return digest
}
catch {
return nil
}
}
+174
View File
@@ -0,0 +1,174 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import XCTest
import StreamKit
final class AesStreamTests: XCTestCase {
func testForWrongKey() {
let sourceBufLen = 128
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(31)
let iv = genBufferOfLen(8)
XCTAssertThrowsError(try encrypt(sourceBuf, sourceBufLen, key, iv))
}
func testForWrongIV() {
let sourceBufLen = 128
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(7)
XCTAssertThrowsError(try encrypt(sourceBuf, sourceBufLen, key, iv))
}
func testEncryptZeroLenBuffer() throws {
let len = 0
let sourceBuf = genBufferOfLen(len)
let key = genBufferOfLen(16)
let iv = genBufferOfLen(16)
let compressedBuf = try encrypt(sourceBuf, len, key, iv)
XCTAssertEqual(16, compressedBuf.count)
}
func testEncryptZeroLenBufferWithVariousKeys() throws {
let len = 0
let sourceBuf = genBufferOfLen(len)
let iv = genBufferOfLen(16)
for keySize in [16,24,32] {
let key = genBufferOfLen(keySize)
let compressedBuf = try encrypt(sourceBuf, len, key, iv)
XCTAssertEqual(16, compressedBuf.count)
}
}
func testThatEncryptWithWrongKeySizesThrowsError() throws {
let sourceBufLen = 512
let sourceBuf = genBufferOfLen(sourceBufLen)
let iv = genBufferOfLen(16)
for keyLen in (0...15).map({$0}) {
let key = genBufferOfLen(keyLen)
XCTAssertThrowsError(try encrypt(sourceBuf, sourceBufLen, key, iv))
}
}
func testEncryptVariousLenBuffers() throws {
for pow in 0...14 {
let len = 1<<pow
let sourceBuf = genBufferOfLen(len)
let key = genBufferOfLen(16)
let iv = genBufferOfLen(16)
let compressedBuf = try encrypt(sourceBuf, len, key, iv)
XCTAssertTrue(compressedBuf.count > 0)
}
}
func testEncryptDecryptZeroLenBuffer() throws {
try encryptDecryptBufferOfLen(0)
}
func testEncryptOddLenBuffers() throws {
for len in [1,15,17,31,33,63,65,127,129] {
try encryptDecryptBufferOfLen(len)
}
}
func testEncryptDecryptVariousLenBuffers() throws {
for pow in 0...14 {
try encryptDecryptBufferOfLen(1<<pow)
}
}
func disabled_testPerformanceEncryptDecrypt1MBFile() throws {
self.measure {
try! encryptDecryptBufferOfLen(1<<20, 32, 1<<15)
}
}
}
extension AesStreamTests {
func encrypt(_ buffer: UnsafePointer<UInt8>,
_ len: Int,
_ key: [UInt8],
_ iv: [UInt8],
_ chunkSize: Int = AesOutputStream.defaultChunkSize
) throws -> [UInt8] {
let dataOutputStream = BufferOutputStream()
try dataOutputStream.open()
let encryptingStream = AesOutputStream(writingTo: dataOutputStream,
key: key,
iv: iv,
chunkSize: chunkSize)
try encryptingStream.open()
try encryptingStream.write(buffer, length: len)
try encryptingStream.close()
let resultData = dataOutputStream.buffer
try dataOutputStream.close()
return resultData
}
func decrypt(_ buffer: [UInt8],
_ key: [UInt8],
_ iv: [UInt8],
_ chunkSize: Int = AesInputStream.defaultChunkSize
) throws -> [UInt8] {
let dataInputStream = BufferInputStream(withBuffer: buffer)
try dataInputStream.open()
let decryptingStream = AesInputStream(readingFrom: dataInputStream, key: key, iv: iv, chunkSize: chunkSize)
try decryptingStream.open()
var result = Array<UInt8>()
let bufLen = 1<<16
var readBuffer = Array<UInt8>(repeating: 0, count: bufLen)
while decryptingStream.hasBytesAvailable {
let readLen = try decryptingStream.read(&readBuffer, maxLength: bufLen)
result.append(contentsOf: readBuffer.prefix(readLen))
}
return result
}
func encryptDecryptBufferOfLen(_ bufLen: Int) throws {
for chunkLen in [128,256,512,1024,2048,4096]{
for keySize in [16,24,32] {
try encryptDecryptBufferOfLen(bufLen, keySize, chunkLen)
}
}
}
func encryptDecryptBufferOfLen(_ bufLen: Int,
_ keyLen: Int,
_ chunkSize: Int
) throws {
let sourceBuf = genBufferOfLen(bufLen)
let key = genBufferOfLen(keyLen)
let iv = genBufferOfLen(16)
let encryptedBuf = try encrypt(sourceBuf, bufLen, key, iv, chunkSize)
let decryptedBuf = try decrypt(encryptedBuf, key, iv, chunkSize)
XCTAssertNotEqual(sourceBuf, encryptedBuf, "\(bufLen) \(keyLen) \(chunkSize)")
XCTAssertEqual(sourceBuf, decryptedBuf, "\(bufLen) \(keyLen) \(chunkSize)")
}
}
@@ -0,0 +1,53 @@
//
// MIT License
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import XCTest
import StreamKit
final class BufferInputStreamTests: XCTestCase {
private func checkRead(_ array: inout [UInt8], _ chunkBufLen: Int) throws {
let inStream = BufferInputStream(withBuffer: array)
try inStream.open()
defer {
inStream.close()
}
var result = [UInt8]()
var chunkBuf: [UInt8] = Array(repeating: 0, count: chunkBufLen)
while inStream.hasBytesAvailable {
let readLen = try inStream.read(&chunkBuf, maxLength: chunkBuf.count)
result.append(contentsOf: chunkBuf.prefix(readLen))
}
XCTAssertEqual(array, result)
}
func testRead_1KB() throws {
let len = 1<<10
var array = genBufferOfLen(len)
for i in stride(from: 100, to: len, by: 100) {
try checkRead(&array, i)
}
}
}
@@ -0,0 +1,42 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import XCTest
import StreamKit
final class BufferOutputStreamTests: XCTestCase {
private func checkWrite(_ buffer: inout [UInt8]) throws {
let outStream = BufferOutputStream()
try outStream.open()
try outStream.write(&buffer, length: buffer.count)
XCTAssertEqual(outStream.buffer, buffer)
try outStream.close()
}
func testWriteDataOfLenghtUpTo_1Kb() throws {
var array: [UInt8] = genBufferOfLen(1<<10)
try checkWrite(&array)
}
}
@@ -0,0 +1,232 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import XCTest
import StreamKit
final class ChaCha20StreamTests: XCTestCase {
func testForWrongKey() {
let sourceBufLen = 128
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(16)
let iv = genBufferOfLen(12)
XCTAssertThrowsError(try encrypt(sourceBuf, sourceBufLen, key, iv))
}
func testForWrongIV() {
let sourceBufLen = 128
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(13)
XCTAssertThrowsError(try encrypt(sourceBuf, sourceBufLen, key, iv))
}
func testEncryptZeroLenBuffer() throws {
let sourceBufLen = 0
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(12)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
XCTAssertTrue(encryptedBuf.count == 0)
}
func testEncrypt63Bytes() throws {
let sourceBufLen = 63
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(12)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
XCTAssertTrue(encryptedBuf.count == 63)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
func testEncrypt64Bytes() throws {
let sourceBufLen = 64
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(12)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
XCTAssertTrue(encryptedBuf.count == 64)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
func testEncrypt65Bytes() throws {
let sourceBufLen = 65
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(12)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
XCTAssertTrue(encryptedBuf.count == 65)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
func testEncrypt127Bytes() throws {
let sourceBufLen = 127
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(12)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv, 127)
XCTAssertTrue(encryptedBuf.count == 127)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
func testEncrypt128Bytes() throws {
let sourceBufLen = 128
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(12)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv, 127)
XCTAssertTrue(encryptedBuf.count == 128)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
func testEncrypt256Bytes() throws {
let sourceBufLen = 256
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(12)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv, 127)
XCTAssertTrue(encryptedBuf.count == 256)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
func testEncryptDecryptZeroLenBuffer() throws {
try encryptDecryptBufferOfLen(0)
}
func testEncryptDecrypt16Bytes() throws {
try encryptDecryptBufferOfLen(16)
}
func testEncryptDecrypt64Bytes() throws {
try encryptDecryptBufferOfLen(64)
}
func testEncryptDecrypt65Bytes() throws {
try encryptDecryptBufferOfLen(65, 64)
}
func testEncryptDecrypt192Bytes() throws {
try encryptDecryptBufferOfLen(192, 65)
}
func testEncryptDecrypt1024Bytes_2() throws {
try encryptDecryptBufferOfLen(127)
}
func testEncryptDecrypt128Bytes() throws {
try encryptDecryptBufferOfLen(128)
}
func testEncryptVariousLenBuffers() throws {
for pow in 0...14 {
let sourceBufLen = 1<<pow
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(12)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
XCTAssertTrue(encryptedBuf.count == sourceBuf.count)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
}
func testEncryptDecryptVariousLenBuffers() throws {
for pow in 0...14 {
try encryptDecryptBufferOfLen(1<<pow)
}
}
func disabled_testPerformanceEncryptDecrypt1MBFile() throws {
self.measure {
try! encryptDecryptBufferOfLen(1<<20)
}
}
}
extension ChaCha20StreamTests {
func encrypt(_ buffer: UnsafePointer<UInt8>,
_ len: Int,
_ key: [UInt8],
_ iv: [UInt8],
_ chunkSize: Int = ChaCha20OutputStream.defaultChunkSize
) throws -> [UInt8] {
let dataOutputStream = BufferOutputStream()
try dataOutputStream.open()
let encryptingStream = ChaCha20OutputStream(writingTo: dataOutputStream,
key: key,
iv: iv,
chunkSize: chunkSize)
try encryptingStream.open()
try encryptingStream.write(buffer, length: len)
try encryptingStream.close()
let resultData = dataOutputStream.buffer
try dataOutputStream.close()
return resultData
}
func decrypt(_ buffer: [UInt8],
_ key: [UInt8],
_ iv: [UInt8],
_ chunkSize: Int = ChaCha20InputStream.defaultChunkSize
) throws -> [UInt8] {
let dataInputStream = BufferInputStream(withBuffer: buffer)
try dataInputStream.open()
let decryptionStream = ChaCha20InputStream(readingFrom: dataInputStream,
key: key,
iv: iv,
chunkSize: chunkSize)
try decryptionStream.open()
var result = Array<UInt8>()
let bufLen = 1<<16
var readBuffer = Array<UInt8>(repeating: 0, count: bufLen)
while decryptionStream.hasBytesAvailable {
let readLen = try decryptionStream.read(&readBuffer, maxLength: bufLen)
result.append(contentsOf: readBuffer.prefix(readLen))
}
return result
}
func encryptDecryptBufferOfLen(_ bufLen: Int) throws {
for chunkSize in [64,65,127,128,129,512,1023,1024] {
try encryptDecryptBufferOfLen(bufLen,chunkSize)
}
}
func encryptDecryptBufferOfLen(_ bufLen: Int,
_ chunkSize: Int = ChaCha20OutputStream.defaultChunkSize
) throws {
let sourceBuf = genBufferOfLen(bufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(12)
let encryptedBuf = try encrypt(sourceBuf, bufLen, key, iv, chunkSize)
let decryptedBuf = try decrypt(encryptedBuf, key, iv, chunkSize)
XCTAssertEqual(bufLen, decryptedBuf.count, "\(bufLen) \(chunkSize)")
XCTAssertEqual(sourceBuf, decryptedBuf, "\(bufLen) \(chunkSize)")
}
}
+83
View File
@@ -0,0 +1,83 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import XCTest
import StreamKit
final class FileStreamTests: XCTestCase {
static var tmpDir: URL!
override class func setUp() {
super.setUp()
tmpDir = try! createTmpFolder()
}
func testCopyFile() throws {
let inFileURL = fileURL("1MB")!
let inFileHandle = try! FileHandle(forReadingFrom: inFileURL)
let inputFileStream = FileInputStream(with: inFileHandle)
try inputFileStream.open()
let outputFileURL = createTmpFileURL(Self.tmpDir)
let fileHandle = try! FileHandle(forWritingTo: outputFileURL)
let outputFileStream = FileOutputStream(with: fileHandle)
try outputFileStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while inputFileStream.hasBytesAvailable {
let readLen = inputFileStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try outputFileStream.write(tmpBuffer, length: readLen)
}
inputFileStream.close()
try outputFileStream.close()
XCTAssertEqual(md5(inFileURL), md5(outputFileURL))
}
func testRead16BFile() throws {
let inFileURL = fileURL("16B")!
let inFileHandle = try! FileHandle(forReadingFrom: inFileURL)
let inputFileStream = FileInputStream(with: inFileHandle)
try inputFileStream.open()
defer {
inputFileStream.close()
}
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
XCTAssertEqual(16, inputFileStream.read(&tmpBuffer, maxLength: 16))
XCTAssertTrue(inputFileStream.hasBytesAvailable)
XCTAssertEqual(0, inputFileStream.read(&tmpBuffer, maxLength: 16))
XCTAssertFalse(inputFileStream.hasBytesAvailable)
}
override class func tearDown() {
super.tearDown()
try! removeTmpFolder(tmpDir)
}
}
+154
View File
@@ -0,0 +1,154 @@
//
// MIT License
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import XCTest
import StreamKit
final class GzipStreamTests: XCTestCase {
func testCompressZeroLenBuffer() throws {
let len = 0
let sourceBuf = genBufferOfLen(len)
let compressedBuf = try compress(sourceBuf, len)
XCTAssertTrue(compressedBuf.count > 0)
}
func testComressVariousLenBuffers() throws {
for i in 0...14 {
let len = 1<<i
let sourceBuf = genBufferOfLen(len)
let compressedBuf = try compress(sourceBuf, len)
XCTAssertTrue(compressedBuf.count > 0)
}
}
func testCompressDecompressZeroLenBuffer() throws {
try compressDecompressBufferOfLen(0)
}
func testCompressDecompressVariousLenBuffers() throws {
for i in 0...14 {
try compressDecompressBufferOfLen(1<<i)
}
}
func testCompressDecompressOddLenBuffers() throws {
for len in [1,15,17,31,33,63,65,127,129] {
try compressDecompressBufferOfLen(len)
}
}
func testCompressDecompressEmptyBufferWithVariousWindowSizes() throws {
for windowSize in stride(from: Int32(9), to: 15, by: 1) {
try compressDecompressBufferOfLen(0, windowSize, 8, .defaultCompression, 8, 8)
}
}
func disabled_testPerformanceCompressDecompress1MBFile() throws {
self.measure {
try! compressDecompressBufferOfLen(1<<20, 15, 1<<15, .defaultCompression, 1<<15, 1<<15)
}
}
}
extension GzipStreamTests {
func compress(_ buffer: UnsafePointer<UInt8>,
_ len: Int,
_ windowBits: Int32 = 15,
_ compressedBufferSize: Int = GzipOutputStream.defaultDeflateChunkSize
) throws -> [UInt8] {
let dataOutputStream = BufferOutputStream()
try dataOutputStream.open()
let compressStream = GzipOutputStream(writingTo: dataOutputStream,
windowBits: windowBits,
deflateChunkSize: compressedBufferSize)
try compressStream.open()
try compressStream.write(buffer, length: len)
try! compressStream.close()
let resultData = dataOutputStream.buffer
try dataOutputStream.close()
return resultData
}
func decompress(_ buffer: [UInt8],
_ windowBits: Int32 = 15,
_ inChunkSize: Int = GzipInputStream.defaultDeflateChunkSize,
_ outChunkSize: Int = GzipInputStream.defaultInflateChunkSize
) throws -> [UInt8] {
let dataInputStream = BufferInputStream(withBuffer: buffer)
try dataInputStream.open()
let decompressStream = GzipInputStream(readingFrom: dataInputStream,
windowBits: windowBits,
deflateChunkSize: inChunkSize,
inflateChunkSize: outChunkSize)
try decompressStream.open()
var result = Array<UInt8>()
let bufLen = 1<<16
var readBuffer = Array<UInt8>(repeating: 0, count: bufLen)
while decompressStream.hasBytesAvailable {
let readLen = try decompressStream.read(&readBuffer, maxLength: bufLen)
result.append(contentsOf: readBuffer.prefix(readLen))
}
return result
}
func compressDecompressBufferOfLen(_ len: Int) throws {
let chunkSizes = [8, 32, 256, 1024]
for chunkSize in chunkSizes {
for inChunkSize in chunkSizes {
for outChunkSize in chunkSizes {
try compressDecompressBufferOfLen(len,
15,
chunkSize,
GzipCompressionLevel.defaultCompression,
inChunkSize,
outChunkSize)
}
}
}
}
func compressDecompressBufferOfLen(_ len: Int,
_ windowBits: Int32 = 15,
_ compressChunk: Int,
_ compressionLevel: GzipCompressionLevel = .defaultCompression,
_ decompressInChunk: Int,
_ decompressOutChunk: Int
) throws {
let sourceBuf: [UInt8] = genBufferOfLen(len)
let compressedBuf = try compress(sourceBuf, len, windowBits, compressChunk)
var decompressedBuf = try decompress(compressedBuf,
windowBits,
decompressInChunk,
decompressOutChunk)
XCTAssertEqual(len, decompressedBuf.count)
XCTAssertEqual(memcmp(sourceBuf, &decompressedBuf, len), 0)
}
}
+1
View File
@@ -0,0 +1 @@
Já;ŽY–¬\¨)×1òZì
Binary file not shown.
+199
View File
@@ -0,0 +1,199 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Mattis aliquam faucibus purus in massa tempor nec. Pellentesque habitant morbi tristique senectus et netus. Fames ac turpis egestas maecenas. Tristique risus nec feugiat in. Risus feugiat in ante metus dictum. Eu mi bibendum neque egestas congue quisque egestas diam in. In aliquam sem fringilla ut. Placerat duis ultricies lacus sed turpis. Penatibus et magnis dis parturient montes nascetur ridiculus mus mauris. Augue mauris augue neque gravida in fermentum et. Id neque aliquam vestibulum morbi blandit. Sit amet est placerat in egestas erat imperdiet sed euismod. Et ligula ullamcorper malesuada proin libero nunc consequat interdum. Faucibus ornare suspendisse sed nisi. Gravida dictum fusce ut placerat orci nulla pellentesque. Tincidunt lobortis feugiat vivamus at augue eget. Morbi tincidunt ornare massa eget egestas purus viverra. Amet mattis vulputate enim nulla.
Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique. Scelerisque viverra mauris in aliquam sem fringilla ut. Ut lectus arcu bibendum at varius. Nisl vel pretium lectus quam id leo in vitae turpis. At consectetur lorem donec massa sapien faucibus. Ut venenatis tellus in metus vulputate eu scelerisque felis. Sed libero enim sed faucibus turpis in eu mi. Eu sem integer vitae justo eget magna fermentum. Vitae elementum curabitur vitae nunc sed velit dignissim. Morbi blandit cursus risus at ultrices mi tempus. Amet nisl suscipit adipiscing bibendum est ultricies. Nisl vel pretium lectus quam.
Non enim praesent elementum facilisis leo vel fringilla est. Varius duis at consectetur lorem donec massa. Eu volutpat odio facilisis mauris. Nunc non blandit massa enim nec dui nunc mattis. Mauris pharetra et ultrices neque ornare. Faucibus turpis in eu mi bibendum neque. Commodo viverra maecenas accumsan lacus. Libero volutpat sed cras ornare arcu dui vivamus. Felis eget velit aliquet sagittis. Magna sit amet purus gravida quis blandit turpis cursus. Sit amet justo donec enim diam vulputate. Malesuada fames ac turpis egestas integer eget. Convallis convallis tellus id interdum velit laoreet id. Vitae suscipit tellus mauris a diam. Eget duis at tellus at urna condimentum mattis. Quis vel eros donec ac odio. Dictum sit amet justo donec. Commodo odio aenean sed adipiscing diam donec adipiscing. Massa tincidunt dui ut ornare.
Viverra mauris in aliquam sem fringilla ut. Morbi tempus iaculis urna id volutpat lacus laoreet non curabitur. Placerat orci nulla pellentesque dignissim enim sit amet venenatis urna. Congue eu consequat ac felis donec et. Feugiat scelerisque varius morbi enim. Risus viverra adipiscing at in. Ultricies lacus sed turpis tincidunt id aliquet. Nullam non nisi est sit amet. Accumsan sit amet nulla facilisi. Libero justo laoreet sit amet cursus. Sed ullamcorper morbi tincidunt ornare massa eget egestas purus viverra. Scelerisque in dictum non consectetur a erat nam at. In ante metus dictum at. Odio facilisis mauris sit amet. Sollicitudin aliquam ultrices sagittis orci a scelerisque purus semper eget. Semper viverra nam libero justo laoreet sit amet cursus sit.
Aliquam etiam erat velit scelerisque in. Posuere morbi leo urna molestie at. Vitae et leo duis ut diam quam. Id velit ut tortor pretium viverra. Sollicitudin ac orci phasellus egestas tellus rutrum tellus pellentesque. Mattis aliquam faucibus purus in massa tempor. Id neque aliquam vestibulum morbi blandit cursus risus at ultrices. Lacus vestibulum sed arcu non odio euismod lacinia at quis. Dolor sit amet consectetur adipiscing elit pellentesque habitant morbi. In hac habitasse platea dictumst vestibulum rhoncus est. Accumsan tortor posuere ac ut consequat semper viverra. At tellus at urna condimentum mattis pellentesque. Pellentesque elit ullamcorper dignissim cras tincidunt lobortis feugiat. Tempor nec feugiat nisl pretium fusce id. Suspendisse in est ante in nibh mauris cursus mattis. Nisl vel pretium lectus quam id leo in. Volutpat diam ut venenatis tellus in metus vulputate. Ut enim blandit volutpat maecenas volutpat. Etiam erat velit scelerisque in dictum.
Nisl tincidunt eget nullam non nisi est sit amet. Turpis massa sed elementum tempus egestas sed. A scelerisque purus semper eget duis. Ut sem viverra aliquet eget sit amet tellus. Vulputate sapien nec sagittis aliquam malesuada bibendum arcu vitae elementum. In metus vulputate eu scelerisque felis imperdiet. Quam adipiscing vitae proin sagittis nisl rhoncus mattis rhoncus. Augue interdum velit euismod in. Nisl rhoncus mattis rhoncus urna neque viverra justo. Bibendum est ultricies integer quis auctor elit sed vulputate mi. Faucibus et molestie ac feugiat sed lectus vestibulum mattis. Laoreet suspendisse interdum consectetur libero id faucibus nisl. Id eu nisl nunc mi ipsum faucibus vitae. Nibh mauris cursus mattis molestie a.
Semper viverra nam libero justo laoreet sit amet. Volutpat lacus laoreet non curabitur gravida arcu ac. Eget aliquet nibh praesent tristique magna sit amet purus. Ac tortor vitae purus faucibus ornare suspendisse. Fusce id velit ut tortor pretium viverra suspendisse potenti. Sodales ut etiam sit amet nisl purus in. Aliquam sem fringilla ut morbi tincidunt augue interdum velit euismod. Sapien pellentesque habitant morbi tristique senectus. Suspendisse in est ante in. A arcu cursus vitae congue mauris. A arcu cursus vitae congue mauris rhoncus aenean.
In iaculis nunc sed augue lacus viverra vitae congue eu. Sed arcu non odio euismod lacinia at quis risus sed. Elementum facilisis leo vel fringilla est ullamcorper eget nulla facilisi. Pretium fusce id velit ut tortor pretium viverra. Tincidunt tortor aliquam nulla facilisi cras fermentum. Massa sed elementum tempus egestas sed sed. Ultricies tristique nulla aliquet enim. In nibh mauris cursus mattis. Faucibus vitae aliquet nec ullamcorper sit. Dolor sit amet consectetur adipiscing elit.
Praesent semper feugiat nibh sed pulvinar proin. Egestas congue quisque egestas diam in. Ut aliquam purus sit amet luctus venenatis lectus magna fringilla. Risus feugiat in ante metus dictum at tempor. Mauris rhoncus aenean vel elit scelerisque mauris pellentesque pulvinar pellentesque. Semper risus in hendrerit gravida rutrum quisque non. Enim tortor at auctor urna. Auctor eu augue ut lectus arcu. Sed odio morbi quis commodo odio aenean. Feugiat sed lectus vestibulum mattis. Fusce id velit ut tortor pretium viverra suspendisse potenti nullam. Faucibus turpis in eu mi bibendum neque.
Sed vulputate mi sit amet mauris commodo quis imperdiet. Adipiscing enim eu turpis egestas pretium. Sed euismod nisi porta lorem. Tincidunt augue interdum velit euismod in. Mattis aliquam faucibus purus in massa tempor nec feugiat nisl. Cras pulvinar mattis nunc sed blandit libero volutpat sed cras. Ultricies tristique nulla aliquet enim tortor. Id velit ut tortor pretium viverra suspendisse potenti nullam. Dictum at tempor commodo ullamcorper a lacus vestibulum. Mauris nunc congue nisi vitae suscipit. Quis blandit turpis cursus in hac habitasse platea dictumst quisque. Egestas erat imperdiet sed euismod nisi porta lorem. Sit amet purus gravida quis blandit. Vel orci porta non pulvinar neque laoreet suspendisse interdum. Amet risus nullam eget felis eget nunc lobortis mattis. Posuere morbi leo urna molestie at elementum eu facilisis sed. Justo eget magna fermentum iaculis eu non diam phasellus. Amet commodo nulla facilisi nullam vehicula ipsum. Odio ut sem nulla pharetra diam sit amet.
Amet risus nullam eget felis eget nunc lobortis mattis aliquam. Dui nunc mattis enim ut tellus elementum. Mattis vulputate enim nulla aliquet. Amet volutpat consequat mauris nunc congue nisi. Eget nulla facilisi etiam dignissim diam. Sit amet justo donec enim diam vulputate ut pharetra sit. Vulputate ut pharetra sit amet aliquam id. Erat pellentesque adipiscing commodo elit at imperdiet dui accumsan sit. Porta nibh venenatis cras sed felis eget velit aliquet sagittis. Amet facilisis magna etiam tempor. Commodo sed egestas egestas fringilla phasellus faucibus. Dictum fusce ut placerat orci nulla pellentesque dignissim. Nulla aliquet porttitor lacus luctus accumsan tortor posuere ac. Nulla facilisi etiam dignissim diam quis enim lobortis scelerisque. Ipsum dolor sit amet consectetur.
Habitant morbi tristique senectus et netus et malesuada fames. Amet consectetur adipiscing elit duis tristique sollicitudin nibh sit amet. Quis commodo odio aenean sed adipiscing diam donec. Vulputate eu scelerisque felis imperdiet proin fermentum. Ut eu sem integer vitae justo. Mauris pharetra et ultrices neque ornare aenean euismod elementum. Enim facilisis gravida neque convallis a cras semper auctor neque. Ultrices in iaculis nunc sed augue lacus viverra. Id interdum velit laoreet id. Ut morbi tincidunt augue interdum. Ac felis donec et odio pellentesque diam volutpat commodo. Platea dictumst vestibulum rhoncus est pellentesque elit. Commodo elit at imperdiet dui accumsan. Aenean sed adipiscing diam donec adipiscing tristique risus nec feugiat. Adipiscing tristique risus nec feugiat in. Elit at imperdiet dui accumsan sit amet nulla facilisi. Mauris pellentesque pulvinar pellentesque habitant morbi.
Elementum sagittis vitae et leo duis. Dis parturient montes nascetur ridiculus mus mauris vitae. Fames ac turpis egestas sed tempus urna et pharetra pharetra. Pellentesque habitant morbi tristique senectus et netus et malesuada fames. A cras semper auctor neque vitae. In hac habitasse platea dictumst vestibulum rhoncus. Et egestas quis ipsum suspendisse ultrices gravida dictum. Curabitur gravida arcu ac tortor dignissim. In hendrerit gravida rutrum quisque non tellus orci ac. Nunc mattis enim ut tellus elementum sagittis vitae. Nunc faucibus a pellentesque sit amet porttitor eget dolor. Mauris ultrices eros in cursus turpis.
Facilisis mauris sit amet massa vitae tortor condimentum. Et malesuada fames ac turpis egestas. Commodo nulla facilisi nullam vehicula ipsum a arcu cursus. Consequat mauris nunc congue nisi vitae suscipit tellus mauris a. Sit amet massa vitae tortor condimentum lacinia quis vel. Lectus mauris ultrices eros in cursus turpis. Lacus viverra vitae congue eu consequat ac. Sed enim ut sem viverra aliquet eget sit. Ante metus dictum at tempor commodo ullamcorper. Nunc mi ipsum faucibus vitae aliquet nec ullamcorper sit amet. Sit amet dictum sit amet justo donec enim. Elit at imperdiet dui accumsan sit amet nulla facilisi morbi. Semper auctor neque vitae tempus quam. Habitant morbi tristique senectus et netus et malesuada fames. Nibh ipsum consequat nisl vel pretium. Massa placerat duis ultricies lacus sed.
Iaculis urna id volutpat lacus laoreet non curabitur gravida arcu. Lorem sed risus ultricies tristique nulla aliquet enim tortor. Sed vulputate mi sit amet mauris commodo quis. Sed tempus urna et pharetra pharetra massa. Eget nunc scelerisque viverra mauris in aliquam sem fringilla ut. Odio aenean sed adipiscing diam donec adipiscing tristique. Pellentesque elit eget gravida cum. Arcu felis bibendum ut tristique et. Ullamcorper dignissim cras tincidunt lobortis. Vitae suscipit tellus mauris a diam maecenas sed enim. Massa placerat duis ultricies lacus. Elementum nibh tellus molestie nunc non. Elit ullamcorper dignissim cras tincidunt lobortis feugiat. Et molestie ac feugiat sed lectus. Volutpat ac tincidunt vitae semper quis lectus nulla at volutpat. Feugiat nisl pretium fusce id. A arcu cursus vitae congue mauris. Malesuada fames ac turpis egestas integer eget aliquet.
In nisl nisi scelerisque eu ultrices vitae auctor eu. Consectetur a erat nam at lectus urna duis convallis. Nulla posuere sollicitudin aliquam ultrices sagittis orci a scelerisque purus. Eu augue ut lectus arcu bibendum at varius vel pharetra. Proin sed libero enim sed faucibus turpis in eu mi. Velit ut tortor pretium viverra suspendisse. Dictum sit amet justo donec enim diam vulputate. Donec enim diam vulputate ut pharetra. Malesuada fames ac turpis egestas integer eget. Scelerisque eleifend donec pretium vulputate sapien nec. Nunc scelerisque viverra mauris in aliquam sem fringilla. Tristique senectus et netus et malesuada fames. Ultrices in iaculis nunc sed.
Sed felis eget velit aliquet sagittis id. Morbi enim nunc faucibus a pellentesque sit amet. Fermentum odio eu feugiat pretium nibh. Sit amet mattis vulputate enim. Aliquet lectus proin nibh nisl condimentum id venenatis a condimentum. Purus gravida quis blandit turpis cursus in hac habitasse platea. Dictum non consectetur a erat nam at. Congue eu consequat ac felis donec et odio. Porttitor eget dolor morbi non arcu risus. Lacinia quis vel eros donec. Varius morbi enim nunc faucibus a pellentesque sit. In massa tempor nec feugiat nisl pretium fusce id. Amet purus gravida quis blandit turpis cursus in hac. Senectus et netus et malesuada fames. Cursus turpis massa tincidunt dui ut ornare. Non enim praesent elementum facilisis leo vel fringilla est. Cursus in hac habitasse platea dictumst quisque sagittis.
Felis imperdiet proin fermentum leo vel orci porta non. Ultricies lacus sed turpis tincidunt id aliquet. Sodales neque sodales ut etiam sit amet nisl. Lorem ipsum dolor sit amet consectetur adipiscing elit pellentesque. Faucibus pulvinar elementum integer enim neque. Nisl tincidunt eget nullam non nisi est sit amet facilisis. Eu turpis egestas pretium aenean pharetra magna. Pellentesque pulvinar pellentesque habitant morbi tristique senectus et netus et. Sit amet porttitor eget dolor morbi non. Scelerisque viverra mauris in aliquam sem fringilla ut morbi tincidunt.
Lectus mauris ultrices eros in cursus turpis. Suspendisse ultrices gravida dictum fusce. Scelerisque fermentum dui faucibus in ornare quam viverra. Cursus sit amet dictum sit amet justo. Ac turpis egestas maecenas pharetra convallis posuere morbi. Justo nec ultrices dui sapien eget mi. Velit sed ullamcorper morbi tincidunt. In metus vulputate eu scelerisque felis imperdiet proin fermentum. Etiam dignissim diam quis enim lobortis scelerisque. Platea dictumst quisque sagittis purus. Elementum pulvinar etiam non quam. Sit amet tellus cras adipiscing enim eu turpis egestas pretium. Nulla aliquet enim tortor at. Eu tincidunt tortor aliquam nulla facilisi cras fermentum odio. Egestas sed sed risus pretium quam vulputate dignissim suspendisse. Urna et pharetra pharetra massa. Morbi quis commodo odio aenean sed adipiscing.
Dignissim diam quis enim lobortis scelerisque fermentum dui faucibus in. Id interdum velit laoreet id donec. Enim blandit volutpat maecenas volutpat blandit aliquam. Fringilla urna porttitor rhoncus dolor. Velit ut tortor pretium viverra suspendisse potenti nullam ac tortor. Commodo nulla facilisi nullam vehicula ipsum a arcu cursus. Nunc sed blandit libero volutpat sed. Posuere ac ut consequat semper. Est ullamcorper eget nulla facilisi etiam. Tortor id aliquet lectus proin nibh nisl condimentum. Sagittis orci a scelerisque purus semper eget. Vel turpis nunc eget lorem dolor sed viverra ipsum nunc. Neque vitae tempus quam pellentesque nec nam aliquam sem. Scelerisque varius morbi enim nunc. Faucibus scelerisque eleifend donec pretium vulputate sapien. Augue eget arcu dictum varius duis at consectetur lorem. Commodo elit at imperdiet dui accumsan sit. Enim sed faucibus turpis in eu mi bibendum neque egestas.
Nisl nunc mi ipsum faucibus. Adipiscing elit ut aliquam purus sit amet. Velit euismod in pellentesque massa placerat duis ultricies lacus sed. Justo eget magna fermentum iaculis. Magna ac placerat vestibulum lectus mauris ultrices. Ut ornare lectus sit amet est. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Nunc non blandit massa enim nec dui nunc. Nibh sit amet commodo nulla facilisi nullam vehicula ipsum a. Elementum integer enim neque volutpat ac tincidunt vitae semper. Rutrum quisque non tellus orci ac auctor. Leo a diam sollicitudin tempor id eu nisl. Vulputate ut pharetra sit amet aliquam id. Risus nullam eget felis eget nunc. Mi eget mauris pharetra et ultrices neque ornare aenean.
Sit amet venenatis urna cursus eget nunc scelerisque. Lacus viverra vitae congue eu consequat ac. Sit amet nulla facilisi morbi tempus. A lacus vestibulum sed arcu non odio euismod lacinia at. Praesent elementum facilisis leo vel fringilla est. Molestie at elementum eu facilisis sed odio morbi. Vitae aliquet nec ullamcorper sit amet risus nullam eget felis. Erat velit scelerisque in dictum non consectetur. Augue interdum velit euismod in pellentesque massa placerat duis. A lacus vestibulum sed arcu non odio euismod lacinia at. Tellus pellentesque eu tincidunt tortor aliquam. In hac habitasse platea dictumst. Porttitor eget dolor morbi non. Sit amet mattis vulputate enim nulla. Bibendum arcu vitae elementum curabitur vitae nunc sed velit dignissim.
Blandit cursus risus at ultrices mi tempus imperdiet nulla. Tortor vitae purus faucibus ornare suspendisse sed nisi. Sed tempus urna et pharetra pharetra massa massa ultricies mi. Dolor sit amet consectetur adipiscing elit duis tristique. Nullam eget felis eget nunc lobortis mattis aliquam. Eget nullam non nisi est sit amet facilisis magna etiam. Nascetur ridiculus mus mauris vitae ultricies leo integer. Fusce ut placerat orci nulla pellentesque dignissim enim sit amet. Cursus euismod quis viverra nibh. Aliquam id diam maecenas ultricies mi. Cursus mattis molestie a iaculis at erat pellentesque adipiscing commodo. Ultrices gravida dictum fusce ut placerat orci nulla pellentesque dignissim. Nunc congue nisi vitae suscipit tellus mauris a. Faucibus nisl tincidunt eget nullam. Suspendisse sed nisi lacus sed viverra tellus. Neque volutpat ac tincidunt vitae semper. Nec ullamcorper sit amet risus nullam eget felis.
Bibendum at varius vel pharetra vel turpis. Hendrerit dolor magna eget est. Amet luctus venenatis lectus magna fringilla urna porttitor rhoncus. Tincidunt nunc pulvinar sapien et ligula ullamcorper malesuada proin. Fames ac turpis egestas sed tempus urna et. Ac felis donec et odio pellentesque diam. Semper quis lectus nulla at volutpat diam ut. Vitae et leo duis ut diam quam nulla porttitor massa. Quisque id diam vel quam elementum pulvinar etiam non. Cursus metus aliquam eleifend mi in nulla posuere. Nunc consequat interdum varius sit amet mattis vulputate enim nulla.
Nibh ipsum consequat nisl vel pretium lectus quam id leo. Leo vel orci porta non pulvinar neque laoreet suspendisse interdum. Pellentesque massa placerat duis ultricies lacus sed turpis tincidunt id. Sem viverra aliquet eget sit. Quisque sagittis purus sit amet volutpat consequat mauris nunc congue. Nam libero justo laoreet sit amet cursus sit amet dictum. Sed viverra ipsum nunc aliquet bibendum enim. Pretium quam vulputate dignissim suspendisse in est ante in. Massa vitae tortor condimentum lacinia quis. Quam viverra orci sagittis eu volutpat odio facilisis mauris. Aliquet nibh praesent tristique magna sit amet purus. Maecenas pharetra convallis posuere morbi leo urna molestie at elementum. Blandit aliquam etiam erat velit scelerisque in dictum. Eu tincidunt tortor aliquam nulla facilisi cras fermentum odio. Platea dictumst vestibulum rhoncus est pellentesque elit ullamcorper dignissim cras.
Augue lacus viverra vitae congue eu consequat ac. Velit dignissim sodales ut eu sem integer. Erat nam at lectus urna duis convallis. Porttitor massa id neque aliquam vestibulum morbi. Facilisis sed odio morbi quis commodo odio aenean. Vitae aliquet nec ullamcorper sit amet risus nullam eget felis. Lectus sit amet est placerat in egestas erat imperdiet. Mauris cursus mattis molestie a iaculis at erat pellentesque adipiscing. Consectetur a erat nam at lectus. Vestibulum lectus mauris ultrices eros. Hendrerit gravida rutrum quisque non. Lectus arcu bibendum at varius vel pharetra. Posuere sollicitudin aliquam ultrices sagittis orci a scelerisque purus. Pulvinar etiam non quam lacus suspendisse faucibus. Fringilla est ullamcorper eget nulla facilisi etiam dignissim.
Risus nec feugiat in fermentum. Sed vulputate mi sit amet mauris. Posuere sollicitudin aliquam ultrices sagittis. Egestas sed sed risus pretium quam vulputate. Erat nam at lectus urna duis. Egestas diam in arcu cursus euismod quis viverra. Orci eu lobortis elementum nibh. At augue eget arcu dictum varius duis. Est ullamcorper eget nulla facilisi etiam dignissim diam quis. Sit amet tellus cras adipiscing enim eu. Tristique magna sit amet purus. Integer feugiat scelerisque varius morbi enim. Turpis egestas pretium aenean pharetra magna ac. Condimentum lacinia quis vel eros donec. Egestas purus viverra accumsan in nisl nisi. Sociis natoque penatibus et magnis dis parturient. Ullamcorper eget nulla facilisi etiam dignissim diam quis enim. Bibendum enim facilisis gravida neque convallis a cras. Quam elementum pulvinar etiam non.
Libero id faucibus nisl tincidunt eget nullam. Eu tincidunt tortor aliquam nulla facilisi cras fermentum. Enim eu turpis egestas pretium aenean pharetra. Amet nisl purus in mollis nunc sed id. Nunc sed blandit libero volutpat sed cras ornare arcu dui. Justo laoreet sit amet cursus sit amet dictum. Mattis molestie a iaculis at erat pellentesque adipiscing commodo. Sagittis aliquam malesuada bibendum arcu vitae elementum curabitur vitae. Eu lobortis elementum nibh tellus molestie nunc non. Enim tortor at auctor urna nunc. Risus ultricies tristique nulla aliquet enim. Felis donec et odio pellentesque diam volutpat commodo. Adipiscing commodo elit at imperdiet dui accumsan sit amet nulla. Turpis nunc eget lorem dolor sed viverra ipsum nunc aliquet. Vivamus at augue eget arcu.
Rhoncus urna neque viverra justo nec ultrices dui. Proin gravida hendrerit lectus a. Amet aliquam id diam maecenas ultricies mi eget mauris. Tincidunt dui ut ornare lectus sit amet. Nisi quis eleifend quam adipiscing vitae proin. Ac ut consequat semper viverra nam libero. Feugiat in fermentum posuere urna nec tincidunt. Massa sapien faucibus et molestie. Suscipit adipiscing bibendum est ultricies integer quis. Viverra ipsum nunc aliquet bibendum enim facilisis. Laoreet sit amet cursus sit. Facilisis leo vel fringilla est ullamcorper eget. Laoreet suspendisse interdum consectetur libero id faucibus nisl. Tincidunt id aliquet risus feugiat in.
Id cursus metus aliquam eleifend mi. Sagittis aliquam malesuada bibendum arcu vitae elementum curabitur vitae nunc. Neque viverra justo nec ultrices dui sapien eget mi. Sit amet commodo nulla facilisi nullam vehicula ipsum a. Pellentesque elit eget gravida cum sociis natoque penatibus. At imperdiet dui accumsan sit amet nulla. Dolor sed viverra ipsum nunc aliquet bibendum. Cras adipiscing enim eu turpis egestas pretium aenean pharetra. Risus commodo viverra maecenas accumsan lacus vel facilisis volutpat. Tellus molestie nunc non blandit massa. Nec feugiat in fermentum posuere urna nec tincidunt praesent semper. Purus viverra accumsan in nisl nisi scelerisque. Dignissim cras tincidunt lobortis feugiat vivamus at augue eget. Hendrerit dolor magna eget est lorem. Venenatis urna cursus eget nunc scelerisque viverra mauris. Tortor id aliquet lectus proin nibh nisl condimentum id venenatis. Sit amet consectetur adipiscing elit ut aliquam purus sit. Eget velit aliquet sagittis id consectetur purus. Mi eget mauris pharetra et ultrices neque ornare.
Diam maecenas sed enim ut sem viverra aliquet eget. Porttitor rhoncus dolor purus non enim. Elit eget gravida cum sociis. Aenean euismod elementum nisi quis. Nec dui nunc mattis enim ut tellus elementum sagittis. Sed blandit libero volutpat sed cras ornare arcu. Amet est placerat in egestas erat imperdiet. Quis imperdiet massa tincidunt nunc pulvinar sapien et ligula ullamcorper. Nec nam aliquam sem et tortor. Sed nisi lacus sed viverra tellus in hac habitasse. Sed blandit libero volutpat sed cras. Lobortis mattis aliquam faucibus purus in massa tempor nec feugiat.
Nec feugiat nisl pretium fusce id. Tempor orci eu lobortis elementum nibh tellus molestie nunc. Ornare aenean euismod elementum nisi. Et molestie ac feugiat sed lectus vestibulum. Tempus imperdiet nulla malesuada pellentesque elit eget gravida cum sociis. Quam pellentesque nec nam aliquam sem et tortor consequat id. Commodo nulla facilisi nullam vehicula ipsum a arcu cursus. Ipsum consequat nisl vel pretium lectus quam id leo in. Etiam tempor orci eu lobortis elementum nibh tellus. Amet commodo nulla facilisi nullam vehicula ipsum a arcu. Ultrices mi tempus imperdiet nulla malesuada pellentesque. Morbi blandit cursus risus at ultrices mi tempus imperdiet nulla. Sed libero enim sed faucibus turpis in eu mi. Pellentesque nec nam aliquam sem et tortor consequat id. Non tellus orci ac auctor augue.
Orci nulla pellentesque dignissim enim sit amet venenatis. Tincidunt ornare massa eget egestas purus. Phasellus egestas tellus rutrum tellus pellentesque eu tincidunt tortor aliquam. Porta lorem mollis aliquam ut. Netus et malesuada fames ac turpis egestas sed tempus urna. Ligula ullamcorper malesuada proin libero nunc consequat interdum. Sagittis id consectetur purus ut faucibus pulvinar elementum. Auctor elit sed vulputate mi sit. Vitae congue mauris rhoncus aenean vel elit scelerisque mauris. Volutpat est velit egestas dui id ornare arcu odio ut. Ornare lectus sit amet est placerat in egestas.
Nunc sed id semper risus in hendrerit gravida rutrum. A condimentum vitae sapien pellentesque habitant morbi tristique. Id diam vel quam elementum pulvinar etiam non quam lacus. Interdum velit euismod in pellentesque massa placerat. Morbi enim nunc faucibus a pellentesque. Tincidunt arcu non sodales neque. Massa eget egestas purus viverra accumsan in nisl nisi scelerisque. Tristique magna sit amet purus gravida quis. Urna id volutpat lacus laoreet non curabitur gravida arcu ac. Massa sed elementum tempus egestas sed sed. Eros in cursus turpis massa tincidunt. Interdum velit euismod in pellentesque massa placerat duis ultricies lacus. Turpis tincidunt id aliquet risus feugiat in ante metus dictum. Pretium lectus quam id leo in. Diam quis enim lobortis scelerisque fermentum dui. Vulputate dignissim suspendisse in est. Accumsan in nisl nisi scelerisque eu ultrices. Pretium lectus quam id leo in vitae turpis.
Et netus et malesuada fames ac turpis egestas sed tempus. Ante metus dictum at tempor. Lacus viverra vitae congue eu. Egestas sed sed risus pretium quam vulputate dignissim suspendisse in. Sit amet cursus sit amet. Tortor consequat id porta nibh venenatis cras. Turpis egestas pretium aenean pharetra magna ac. Arcu dui vivamus arcu felis bibendum ut tristique. Magna fermentum iaculis eu non diam phasellus vestibulum lorem. Diam maecenas sed enim ut sem viverra.
Sed risus ultricies tristique nulla. Consectetur libero id faucibus nisl. Faucibus a pellentesque sit amet porttitor eget dolor morbi non. Nisi scelerisque eu ultrices vitae. Rhoncus dolor purus non enim praesent. Aliquet bibendum enim facilisis gravida neque convallis a cras semper. Ut sem nulla pharetra diam sit amet nisl. Feugiat sed lectus vestibulum mattis ullamcorper velit sed. Sed egestas egestas fringilla phasellus faucibus scelerisque eleifend donec pretium. Nullam vehicula ipsum a arcu cursus. Tristique senectus et netus et malesuada fames. In nisl nisi scelerisque eu ultrices vitae auctor eu augue. Nullam ac tortor vitae purus faucibus ornare suspendisse sed. Nam at lectus urna duis convallis convallis tellus. Lacus suspendisse faucibus interdum posuere lorem ipsum dolor sit.
Velit sed ullamcorper morbi tincidunt ornare massa. In fermentum et sollicitudin ac orci. Nec ultrices dui sapien eget mi proin sed. Nulla at volutpat diam ut venenatis tellus in. Egestas sed tempus urna et pharetra pharetra. Suspendisse faucibus interdum posuere lorem ipsum dolor. Sit amet tellus cras adipiscing enim eu turpis. Tempor commodo ullamcorper a lacus vestibulum sed arcu. Felis eget velit aliquet sagittis id consectetur. Suspendisse interdum consectetur libero id faucibus nisl tincidunt eget. Malesuada fames ac turpis egestas sed tempus. Arcu cursus vitae congue mauris rhoncus aenean. Tincidunt augue interdum velit euismod in pellentesque. Imperdiet dui accumsan sit amet. Magnis dis parturient montes nascetur ridiculus mus mauris vitae ultricies. Amet nisl suscipit adipiscing bibendum. Ac feugiat sed lectus vestibulum mattis ullamcorper velit sed ullamcorper. Massa vitae tortor condimentum lacinia quis vel.
Ultrices vitae auctor eu augue ut lectus arcu. Erat pellentesque adipiscing commodo elit at imperdiet. Dictum non consectetur a erat nam at lectus. Convallis a cras semper auctor neque vitae tempus quam pellentesque. Purus ut faucibus pulvinar elementum integer. Accumsan lacus vel facilisis volutpat est velit egestas dui. Sem nulla pharetra diam sit amet nisl suscipit. Nec feugiat nisl pretium fusce. Cursus risus at ultrices mi tempus imperdiet nulla malesuada pellentesque. Eget duis at tellus at. Ut consequat semper viverra nam libero justo laoreet sit amet. Tortor pretium viverra suspendisse potenti nullam ac. Egestas quis ipsum suspendisse ultrices gravida dictum fusce ut placerat. Consequat mauris nunc congue nisi vitae suscipit. Aenean et tortor at risus viverra adipiscing at. Consectetur adipiscing elit duis tristique sollicitudin nibh. Nunc vel risus commodo viverra maecenas accumsan lacus vel facilisis. Pellentesque habitant morbi tristique senectus. Eget est lorem ipsum dolor sit amet consectetur. Feugiat in ante metus dictum at tempor.
Ipsum a arcu cursus vitae congue. Nibh praesent tristique magna sit amet purus gravida quis blandit. Elementum integer enim neque volutpat. Tincidunt dui ut ornare lectus sit amet est placerat in. Vulputate eu scelerisque felis imperdiet proin fermentum. Gravida neque convallis a cras semper. Nullam ac tortor vitae purus faucibus. Vitae sapien pellentesque habitant morbi tristique. Feugiat pretium nibh ipsum consequat nisl vel pretium. Amet venenatis urna cursus eget nunc scelerisque viverra. Cursus metus aliquam eleifend mi in nulla posuere sollicitudin aliquam. Sed nisi lacus sed viverra tellus in. Convallis aenean et tortor at risus viverra adipiscing. Purus sit amet luctus venenatis lectus magna fringilla.
Cursus euismod quis viverra nibh cras pulvinar mattis nunc sed. Pharetra vel turpis nunc eget lorem. Morbi leo urna molestie at elementum eu facilisis sed. Quis risus sed vulputate odio ut enim. Suscipit tellus mauris a diam maecenas sed enim ut sem. Lacus vestibulum sed arcu non odio euismod lacinia. Amet venenatis urna cursus eget nunc scelerisque viverra mauris in. Amet volutpat consequat mauris nunc congue nisi vitae suscipit. Integer quis auctor elit sed vulputate mi sit amet. Sollicitudin aliquam ultrices sagittis orci a scelerisque. Volutpat maecenas volutpat blandit aliquam etiam erat. Nulla facilisi nullam vehicula ipsum a arcu cursus. Blandit aliquam etiam erat velit scelerisque in dictum. At imperdiet dui accumsan sit amet. Eros donec ac odio tempor. Ornare aenean euismod elementum nisi quis eleifend quam adipiscing. Tincidunt arcu non sodales neque.
Netus et malesuada fames ac turpis egestas sed. Euismod lacinia at quis risus sed vulputate odio ut. Sed pulvinar proin gravida hendrerit lectus a. Hac habitasse platea dictumst quisque sagittis purus sit. Sodales ut etiam sit amet nisl purus in mollis nunc. Sit amet porttitor eget dolor morbi non arcu risus quis. Scelerisque felis imperdiet proin fermentum. Fringilla urna porttitor rhoncus dolor. Eget mauris pharetra et ultrices neque ornare aenean euismod. Arcu non sodales neque sodales ut etiam sit. Amet venenatis urna cursus eget nunc scelerisque viverra. Sagittis purus sit amet volutpat consequat mauris. Nam at lectus urna duis convallis convallis tellus.
Habitant morbi tristique senectus et netus et malesuada fames. Morbi tristique senectus et netus et. Amet consectetur adipiscing elit duis tristique sollicitudin nibh sit. Ullamcorper morbi tincidunt ornare massa eget egestas purus. Eu sem integer vitae justo eget. Feugiat sed lectus vestibulum mattis ullamcorper. Amet luctus venenatis lectus magna fringilla. Tellus in hac habitasse platea dictumst. Risus in hendrerit gravida rutrum quisque non. Eget est lorem ipsum dolor sit. Mauris commodo quis imperdiet massa tincidunt nunc pulvinar. Et netus et malesuada fames ac turpis. Orci porta non pulvinar neque laoreet suspendisse. Diam in arcu cursus euismod quis viverra. Sed tempus urna et pharetra pharetra massa massa. In massa tempor nec feugiat nisl. Gravida quis blandit turpis cursus in hac habitasse platea. Blandit volutpat maecenas volutpat blandit aliquam etiam. Lobortis feugiat vivamus at augue eget arcu dictum varius. Id consectetur purus ut faucibus pulvinar elementum integer enim.
Consectetur adipiscing elit ut aliquam purus sit amet luctus venenatis. Ipsum a arcu cursus vitae congue. Nunc pulvinar sapien et ligula ullamcorper malesuada proin. Eu mi bibendum neque egestas congue quisque. Ultrices sagittis orci a scelerisque purus. Auctor elit sed vulputate mi sit amet mauris. Maecenas volutpat blandit aliquam etiam erat. Scelerisque felis imperdiet proin fermentum leo. Egestas erat imperdiet sed euismod. Ac turpis egestas integer eget aliquet. Tellus molestie nunc non blandit massa enim nec. Lectus mauris ultrices eros in cursus. Morbi tristique senectus et netus et malesuada fames. Proin gravida hendrerit lectus a. Vel elit scelerisque mauris pellentesque pulvinar pellentesque habitant morbi. Pulvinar mattis nunc sed blandit libero. Enim lobortis scelerisque fermentum dui faucibus in ornare quam.
Vitae auctor eu augue ut lectus arcu bibendum. Mattis aliquam faucibus purus in massa tempor nec. Adipiscing elit duis tristique sollicitudin nibh sit amet commodo. Phasellus vestibulum lorem sed risus. Ultricies tristique nulla aliquet enim tortor at auctor urna nunc. Sed turpis tincidunt id aliquet risus feugiat in ante. Vitae congue eu consequat ac felis donec. Auctor neque vitae tempus quam pellentesque nec nam aliquam sem. Metus vulputate eu scelerisque felis imperdiet. Ultrices neque ornare aenean euismod elementum nisi. Sed augue lacus viverra vitae congue eu. Est ullamcorper eget nulla facilisi.
Morbi quis commodo odio aenean sed adipiscing diam. Libero justo laoreet sit amet cursus sit amet dictum sit. At quis risus sed vulputate. Pellentesque eu tincidunt tortor aliquam nulla facilisi. Diam phasellus vestibulum lorem sed risus ultricies tristique. Amet nisl suscipit adipiscing bibendum est ultricies. In mollis nunc sed id semper risus in hendrerit gravida. Ut placerat orci nulla pellentesque dignissim. Laoreet non curabitur gravida arcu ac tortor dignissim convallis aenean. Felis eget velit aliquet sagittis id consectetur purus ut. Proin fermentum leo vel orci. Laoreet sit amet cursus sit amet dictum sit. Mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus.
Commodo nulla facilisi nullam vehicula ipsum a arcu cursus vitae. Elit ut aliquam purus sit amet luctus venenatis lectus. Orci dapibus ultrices in iaculis nunc sed augue. Mi eget mauris pharetra et. Massa tincidunt nunc pulvinar sapien et ligula ullamcorper malesuada. Phasellus vestibulum lorem sed risus ultricies tristique nulla aliquet. Magna fermentum iaculis eu non diam phasellus vestibulum lorem sed. Est velit egestas dui id. Ut faucibus pulvinar elementum integer enim neque volutpat ac. Felis donec et odio pellentesque diam volutpat commodo.
Suspendisse interdum consectetur libero id faucibus nisl. Nunc mi ipsum faucibus vitae aliquet. Facilisis magna etiam tempor orci eu lobortis elementum nibh. Scelerisque viverra mauris in aliquam sem fringilla. Quis vel eros donec ac odio tempor orci dapibus. Sapien pellentesque habitant morbi tristique senectus et netus et. Viverra nam libero justo laoreet sit amet. Faucibus ornare suspendisse sed nisi. Dolor sed viverra ipsum nunc aliquet bibendum enim facilisis gravida. Vitae proin sagittis nisl rhoncus mattis rhoncus urna. Vel orci porta non pulvinar neque laoreet suspendisse interdum consectetur. Parturient montes nascetur ridiculus mus mauris.
Massa eget egestas purus viverra accumsan in nisl. Pharetra et ultrices neque ornare aenean euismod elementum nisi. Ullamcorper sit amet risus nullam eget. Nunc non blandit massa enim. Vitae et leo duis ut diam quam nulla. Quis commodo odio aenean sed adipiscing diam donec adipiscing. Elit ut aliquam purus sit amet luctus venenatis lectus magna. Ipsum nunc aliquet bibendum enim facilisis gravida neque. Enim ut sem viverra aliquet eget sit. Dolor purus non enim praesent elementum facilisis leo vel fringilla.
Sociis natoque penatibus et magnis dis parturient montes nascetur ridiculus. Egestas integer eget aliquet nibh praesent tristique magna sit. Malesuada fames ac turpis egestas sed. Amet nisl purus in mollis nunc sed. Posuere morbi leo urna molestie at elementum eu facilisis sed. Justo eget magna fermentum iaculis eu non diam. Donec pretium vulputate sapien nec sagittis aliquam malesuada. Iaculis at erat pellentesque adipiscing commodo elit. Feugiat in ante metus dictum. Tristique et egestas quis ipsum suspendisse ultrices gravida dictum fusce. Porta nibh venenatis cras sed felis eget. Sollicitudin nibh sit amet commodo.
Amet cursus sit amet dictum sit. Varius quam quisque id diam vel quam elementum pulvinar. Condimentum lacinia quis vel eros. Lacus vel facilisis volutpat est velit egestas dui. In egestas erat imperdiet sed euismod nisi porta lorem. Proin fermentum leo vel orci porta. Tincidunt arcu non sodales neque sodales ut etiam. Quis auctor elit sed vulputate mi sit amet mauris. Sit amet est placerat in egestas. Pharetra diam sit amet nisl suscipit adipiscing bibendum est. Arcu bibendum at varius vel pharetra vel turpis. Pulvinar mattis nunc sed blandit libero. Ut consequat semper viverra nam libero. Nec feugiat in fermentum posuere. Sit amet est placerat in egestas. Sit amet consectetur adipiscing elit ut aliquam purus. Morbi tristique senectus et netus et malesuada fames ac. Faucibus scelerisque eleifend donec pretium. Amet risus nullam eget felis eget.
Tempor orci eu lobortis elementum nibh tellus molestie nunc non. Facilisis gravida neque convallis a. Faucibus ornare suspendisse sed nisi lacus sed viverra tellus in. Lacus laoreet non curabitur gravida arcu ac. Vivamus arcu felis bibendum ut tristique et egestas. Non nisi est sit amet facilisis magna. Sit amet mauris commodo quis imperdiet massa tincidunt nunc. Tristique senectus et netus et malesuada fames ac. Leo integer malesuada nunc vel risus. A diam sollicitudin tempor id eu nisl nunc mi ipsum. Sociis natoque penatibus et magnis dis parturient montes. Pharetra massa massa ultricies mi quis hendrerit dolor.
Viverra justo nec ultrices dui sapien eget. Ante in nibh mauris cursus mattis molestie a iaculis at. Sit amet mauris commodo quis imperdiet massa tincidunt nunc pulvinar. Turpis massa sed elementum tempus egestas. Elit scelerisque mauris pellentesque pulvinar pellentesque. Vel eros donec ac odio tempor orci dapibus ultrices in. Sem viverra aliquet eget sit amet. Et leo duis ut diam quam. Suspendisse potenti nullam ac tortor vitae. Amet consectetur adipiscing elit duis tristique. Nibh venenatis cras sed felis eget velit aliquet sagittis. Donec ultrices tincidunt arcu non sodales neque sodales ut. Nunc vel risus commodo viverra maecenas accumsan lacus vel facilisis. Nec feugiat nisl pretium fusce id. Feugiat nibh sed pulvinar proin gravida hendrerit. Semper auctor neque vitae tempus quam pellentesque. Consectetur adipiscing elit duis tristique sollicitudin nibh sit amet. Mi eget mauris pharetra et ultrices neque ornare aenean.
Varius duis at consectetur lorem donec massa sapien. Sed lectus vestibulum mattis ullamcorper velit. Pharetra et ultrices neque ornare aenean euismod. Eget velit aliquet sagittis id. Ac tortor dignissim convallis aenean et. In tellus integer feugiat scelerisque. Augue interdum velit euismod in pellentesque massa placerat duis. Malesuada proin libero nunc consequat interdum varius sit. Mattis vulputate enim nulla aliquet porttitor lacus luctus. Vestibulum rhoncus est pellentesque elit ullamcorper dignissim.
Volutpat odio facilisis mauris sit amet massa vitae tortor. Potenti nullam ac tortor vitae purus faucibus ornare suspendisse. Id nibh tortor id aliquet lectus. Elementum eu facilisis sed odio morbi quis commodo odio. Sit amet consectetur adipiscing elit. Eget mi proin sed libero. Dui faucibus in ornare quam viverra. Nunc sed velit dignissim sodales ut eu. Volutpat lacus laoreet non curabitur gravida arcu ac tortor dignissim. Donec adipiscing tristique risus nec feugiat.
Dui ut ornare lectus sit amet est placerat. In hac habitasse platea dictumst vestibulum rhoncus est pellentesque elit. Sed odio morbi quis commodo odio aenean sed adipiscing diam. Semper risus in hendrerit gravida rutrum quisque non tellus orci. Fringilla ut morbi tincidunt augue interdum velit euismod in pellentesque. Facilisi morbi tempus iaculis urna id. Feugiat in ante metus dictum at tempor commodo. Ullamcorper eget nulla facilisi etiam dignissim diam. Neque laoreet suspendisse interdum consectetur libero id. Nisl pretium fusce id velit ut tortor pretium viverra. Curabitur gravida arcu ac tortor dignissim convallis aenean. In nisl nisi scelerisque eu ultrices vitae auctor. Viverra ipsum nunc aliquet bibendum enim facilisis gravida neque. Habitant morbi tristique senectus et netus et malesuada. Pellentesque massa placerat duis ultricies lacus. Augue eget arcu dictum varius duis at consectetur lorem.
Suspendisse interdum consectetur libero id faucibus nisl tincidunt eget. Nascetur ridiculus mus mauris vitae ultricies leo. Orci phasellus egestas tellus rutrum tellus pellentesque eu. Ultrices in iaculis nunc sed. Vitae semper quis lectus nulla at volutpat. Mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus et netus. Gravida in fermentum et sollicitudin ac orci phasellus. Enim facilisis gravida neque convallis a. Ultricies tristique nulla aliquet enim. Nibh nisl condimentum id venenatis a condimentum vitae. Cras fermentum odio eu feugiat pretium nibh ipsum consequat.
Duis tristique sollicitudin nibh sit amet. Egestas integer eget aliquet nibh praesent tristique magna. Tellus id interdum velit laoreet id donec ultrices tincidunt arcu. Consequat semper viverra nam libero justo. Dolor sit amet consectetur adipiscing elit ut. Quis varius quam quisque id diam vel quam elementum pulvinar. Tincidunt tortor aliquam nulla facilisi cras fermentum. Condimentum mattis pellentesque id nibh tortor id aliquet. Consequat ac felis donec et odio pellentesque diam. Consequat ac felis donec et odio pellentesque diam.
A cras semper auctor neque vitae tempus. Volutpat commodo sed egestas egestas. Tristique sollicitudin nibh sit amet commodo nulla facilisi. Laoreet non curabitur gravida arcu ac tortor dignissim. Nunc aliquet bibendum enim facilisis gravida. Nisl tincidunt eget nullam non nisi est sit amet. Morbi tincidunt augue interdum velit euismod in. Id consectetur purus ut faucibus. Tempus iaculis urna id volutpat. Sit amet est placerat in egestas erat imperdiet. Nunc mi ipsum faucibus vitae aliquet nec ullamcorper. Sit amet commodo nulla facilisi. Ornare lectus sit amet est placerat in egestas. Mattis pellentesque id nibh tortor id aliquet lectus proin. Nisl purus in mollis nunc. Nibh mauris cursus mattis molestie. Sed velit dignissim sodales ut eu sem integer vitae.
Sit amet porttitor eget dolor morbi non arcu risus quis. Consequat mauris nunc congue nisi. Feugiat scelerisque varius morbi enim nunc faucibus a. Vel fringilla est ullamcorper eget nulla facilisi etiam dignissim diam. Cras adipiscing enim eu turpis egestas pretium aenean pharetra. Feugiat scelerisque varius morbi enim nunc faucibus a. Netus et malesuada fames ac turpis. Vitae tempus quam pellentesque nec nam aliquam sem et. Mattis vulputate enim nulla aliquet porttitor lacus luctus accumsan tortor. Ultricies mi eget mauris pharetra et. Feugiat sed lectus vestibulum mattis ullamcorper velit. Enim nec dui nunc mattis.
Vulputate odio ut enim blandit. Nullam ac tortor vitae purus faucibus. Pulvinar pellentesque habitant morbi tristique senectus et netus et malesuada. Habitant morbi tristique senectus et. Fames ac turpis egestas sed. Vel eros donec ac odio tempor orci dapibus ultrices. Lorem dolor sed viverra ipsum nunc aliquet. Accumsan sit amet nulla facilisi morbi tempus iaculis urna id. Placerat vestibulum lectus mauris ultrices eros in cursus. Urna duis convallis convallis tellus id interdum velit laoreet.
Aliquam ut porttitor leo a. Nunc congue nisi vitae suscipit tellus mauris a. Tempor id eu nisl nunc mi ipsum faucibus. Sed risus ultricies tristique nulla aliquet enim. Tempus imperdiet nulla malesuada pellentesque elit eget gravida cum. Tristique senectus et netus et malesuada fames ac turpis. Nisl vel pretium lectus quam id leo. Nec sagittis aliquam malesuada bibendum arcu vitae. Turpis massa tincidunt dui ut ornare lectus sit amet. Sed euismod nisi porta lorem mollis aliquam.
Tempor nec feugiat nisl pretium. Enim sit amet venenatis urna cursus eget nunc scelerisque viverra. Sodales neque sodales ut etiam sit. Feugiat scelerisque varius morbi enim nunc faucibus a pellentesque sit. Aliquam purus sit amet luctus venenatis lectus. Iaculis urna id volutpat lacus laoreet non curabitur gravida. At augue eget arcu dictum varius duis at. Mi sit amet mauris commodo quis imperdiet massa tincidunt. At tellus at urna condimentum mattis pellentesque id. Pellentesque elit ullamcorper dignissim cras tincidunt lobortis. Nulla facilisi nullam vehicula ipsum a arcu cursus vitae congue. Pharetra magna ac placerat vestibulum lectus mauris ultrices eros in. Aenean pharetra magna ac placerat vestibulum.
Sagittis purus sit amet volutpat. Vel pretium lectus quam id leo. Turpis nunc eget lorem dolor sed viverra ipsum. Montes nascetur ridiculus mus mauris. Orci nulla pellentesque dignissim enim sit amet venenatis urna cursus. Sit amet est placerat in egestas erat imperdiet. Eleifend quam adipiscing vitae proin sagittis nisl rhoncus. Justo nec ultrices dui sapien eget. Vel quam elementum pulvinar etiam. Lectus quam id leo in vitae turpis massa sed. Pellentesque habitant morbi tristique senectus et netus et malesuada fames. Sed viverra tellus in hac habitasse platea. Diam volutpat commodo sed egestas egestas fringilla phasellus faucibus. Elit ullamcorper dignissim cras tincidunt lobortis feugiat vivamus at augue. Sapien eget mi proin sed. Eget magna fermentum iaculis eu non diam phasellus vestibulum. Lectus quam id leo in vitae. Ut morbi tincidunt augue interdum velit euismod in. Amet nisl purus in mollis nunc sed id semper risus. In eu mi bibendum neque egestas congue quisque.
Amet porttitor eget dolor morbi non arcu risus. Id donec ultrices tincidunt arcu. Sit amet mattis vulputate enim nulla. Duis convallis convallis tellus id interdum velit laoreet id donec. Nunc mattis enim ut tellus. Mauris sit amet massa vitae tortor. Netus et malesuada fames ac. At elementum eu facilisis sed. Lectus sit amet est placerat. Scelerisque varius morbi enim nunc faucibus a pellentesque sit amet. Tempor orci eu lobortis elementum nibh tellus. Morbi tempus iaculis urna id.
Velit scelerisque in dictum non consectetur a. Interdum velit laoreet id donec ultrices tincidunt arcu non sodales. Amet massa vitae tortor condimentum. Viverra adipiscing at in tellus. Congue mauris rhoncus aenean vel elit scelerisque. Lobortis elementum nibh tellus molestie nunc non. Id consectetur purus ut faucibus pulvinar elementum. Imperdiet proin fermentum leo vel orci porta non. Vitae tortor condimentum lacinia quis vel. Quisque sagittis purus sit amet volutpat consequat mauris. Etiam tempor orci eu lobortis elementum nibh tellus. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Pellentesque sit amet porttitor eget dolor morbi non arcu. Augue neque gravida in fermentum et sollicitudin ac. Dis parturient montes nascetur ridiculus mus mauris vitae ultricies. Id consectetur purus ut faucibus pulvinar elementum integer enim neque. Sed egestas egestas fringilla phasellus faucibus scelerisque eleifend. Facilisis leo vel fringilla est ullamcorper eget nulla facilisi.
Convallis convallis tellus id interdum. Volutpat blandit aliquam etiam erat velit scelerisque. Facilisis magna etiam tempor orci eu lobortis elementum nibh tellus. Quis blandit turpis cursus in hac. Proin sed libero enim sed faucibus. Sit amet nisl suscipit adipiscing bibendum. Viverra ipsum nunc aliquet bibendum enim facilisis gravida. Sit amet venenatis urna cursus eget. Justo nec ultrices dui sapien eget mi. Odio ut sem nulla pharetra diam sit amet nisl suscipit.
Ipsum dolor sit amet consectetur adipiscing elit pellentesque habitant. Tristique risus nec feugiat in fermentum. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui. Felis imperdiet proin fermentum leo vel orci. Suscipit tellus mauris a diam maecenas sed enim ut sem. Est pellentesque elit ullamcorper dignissim cras. Dolor morbi non arcu risus. Mattis ullamcorper velit sed ullamcorper morbi tincidunt ornare. Sit amet nisl suscipit adipiscing bibendum est ultricies. Eget dolor morbi non arcu. Morbi blandit cursus risus at ultrices mi. Id aliquet lectus proin nibh.
Curabitur gravida arcu ac tortor dignissim convallis aenean et tortor. Tincidunt id aliquet risus feugiat in ante metus dictum. In est ante in nibh mauris. Ultrices mi tempus imperdiet nulla malesuada pellentesque elit eget. Quam vulputate dignissim suspendisse in est ante in. Cursus eget nunc scelerisque viverra mauris. Consectetur adipiscing elit ut aliquam purus sit amet luctus. Pulvinar elementum integer enim neque volutpat ac. Eu mi bibendum neque egestas congue quisque egestas diam in. Vel fringilla est ullamcorper eget nulla facilisi. Malesuada fames ac turpis egestas maecenas. Viverra ipsum nunc aliquet bibendum enim. Consectetur adipiscing elit pellentesque habitant. Vulputate sapien nec sagittis aliquam malesuada bibendum arcu. Vel risus commodo viverra maecenas. Pharetra diam sit amet nisl. Ut venenatis tellus in metus vulputate. Adipiscing bibendum est ultricies integer. Tempor nec feugiat nisl pretium fusce id velit ut.
Sed risus pretium quam vulputate dignissim. Commodo nulla facilisi nullam vehicula ipsum a arcu. Euismod in pellentesque massa placerat duis ultricies. At ultrices mi tempus imperdiet nulla malesuada pellentesque elit eget. Id semper risus in hendrerit. Dolor sed viverra ipsum nunc. Molestie at elementum eu facilisis sed odio morbi quis. Amet facilisis magna etiam tempor orci eu. Arcu cursus vitae congue mauris rhoncus. Laoreet non curabitur gravida arcu ac tortor. Velit scelerisque in dictum non consectetur a erat nam at. Nunc sed augue lacus viverra vitae congue. Gravida dictum fusce ut placerat orci. Aliquam nulla facilisi cras fermentum odio. Donec adipiscing tristique risus nec feugiat in fermentum posuere urna. Amet nulla facilisi morbi tempus iaculis urna id. Orci phasellus egestas tellus rutrum tellus pellentesque. Sit amet facilisis magna etiam tempor orci eu lobortis. Pretium lectus quam id leo in vitae turpis massa sed.
Fermentum iaculis eu non diam phasellus vestibulum. A diam maecenas sed enim ut. Lectus arcu bibendum at varius vel pharetra vel turpis nunc. Sapien faucibus et molestie ac feugiat sed lectus vestibulum. Vulputate odio ut enim blandit volutpat maecenas. Et molestie ac feugiat sed lectus vestibulum. Scelerisque viverra mauris in aliquam sem. Mauris commodo quis imperdiet massa tincidunt nunc. Vestibulum lorem sed risus ultricies tristique. Volutpat commodo sed egestas egestas fringilla. Elit sed vulputate mi sit amet mauris commodo quis imperdiet. Rhoncus urna neque viverra justo nec ultrices. Ut lectus arcu bibendum at varius vel pharetra vel. Proin fermentum leo vel orci. Morbi non arcu risus quis varius quam.
Habitant morbi tristique senectus et netus et malesuada. Gravida dictum fusce ut placerat orci. Orci ac auctor augue mauris augue neque gravida. Feugiat scelerisque varius morbi enim nunc faucibus a pellentesque sit. Elit scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus. Amet mattis vulputate enim nulla aliquet porttitor. Diam sit amet nisl suscipit adipiscing. Magnis dis parturient montes nascetur ridiculus mus mauris vitae. Nunc mattis enim ut tellus elementum sagittis vitae et. Ut consequat semper viverra nam libero justo. Cras fermentum odio eu feugiat pretium nibh. Viverra justo nec ultrices dui sapien eget. Risus nec feugiat in fermentum posuere urna nec. Feugiat in fermentum posuere urna nec tincidunt praesent semper feugiat.
Vel pretium lectus quam id leo in. Ut tellus elementum sagittis vitae et leo. Neque egestas congue quisque egestas. Viverra aliquet eget sit amet tellus cras adipiscing. Eu ultrices vitae auctor eu augue. Mauris augue neque gravida in fermentum et sollicitudin ac orci. Dui faucibus in ornare quam viverra. Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus et. Iaculis urna id volutpat lacus laoreet non curabitur gravida arcu. Aliquam id diam maecenas ultricies. Integer feugiat scelerisque varius morbi enim nunc faucibus a pellentesque. Gravida in fermentum et sollicitudin ac.
Eu nisl nunc mi ipsum faucibus vitae. Elementum pulvinar etiam non quam lacus suspendisse faucibus. Sed elementum tempus egestas sed sed. Morbi leo urna molestie at elementum eu. Vitae tortor condimentum lacinia quis vel eros donec ac odio. Sed euismod nisi porta lorem. Venenatis cras sed felis eget velit aliquet sagittis. Purus gravida quis blandit turpis cursus in hac. Lacus suspendisse faucibus interdum posuere lorem ipsum. Dolor sit amet consectetur adipiscing. Pellentesque habitant morbi tristique senectus et netus. Nunc vel risus commodo viverra. Ut enim blandit volutpat maecenas volutpat blandit aliquam etiam. Eget egestas purus viverra accumsan in. Leo in vitae turpis massa sed elementum tempus egestas. Leo duis ut diam quam nulla porttitor massa id. Blandit volutpat maecenas volutpat blandit.
Vulputate dignissim suspendisse in est ante in nibh mauris cursus. Velit sed ullamcorper morbi tincidunt ornare massa eget egestas. Morbi tristique senectus et netus et malesuada. Lorem ipsum dolor sit amet. Molestie at elementum eu facilisis sed odio morbi quis. Euismod nisi porta lorem mollis aliquam ut. Lacus vel facilisis volutpat est velit egestas. Tristique risus nec feugiat in fermentum. Habitasse platea dictumst quisque sagittis. Ut diam quam nulla porttitor massa id neque aliquam.
At volutpat diam ut venenatis tellus in metus vulputate. Sed libero enim sed faucibus turpis in. Lacus vestibulum sed arcu non. Mauris commodo quis imperdiet massa tincidunt nunc pulvinar sapien. Ut aliquam purus sit amet luctus venenatis. Tellus pellentesque eu tincidunt tortor aliquam nulla facilisi cras. Vitae semper quis lectus nulla at volutpat. Curabitur gravida arcu ac tortor dignissim convallis aenean. Volutpat est velit egestas dui id ornare arcu odio ut. Quisque non tellus orci ac auctor.
Leo integer malesuada nunc vel risus. Turpis cursus in hac habitasse platea dictumst quisque sagittis purus. Volutpat sed cras ornare arcu. Ipsum nunc aliquet bibendum enim facilisis gravida neque convallis a. Turpis massa tincidunt dui ut ornare lectus sit amet est. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. In dictum non consectetur a erat nam at. Iaculis urna id volutpat lacus laoreet non curabitur. At ultrices mi tempus imperdiet nulla malesuada pellentesque. Adipiscing elit ut aliquam purus sit. Velit ut tortor pretium viverra suspendisse potenti nullam. Cum sociis natoque penatibus et. Tincidunt eget nullam non nisi. Cursus sit amet dictum sit amet justo donec. Massa tincidunt dui ut ornare lectus sit amet est placerat. Aliquet eget sit amet tellus cras adipiscing.
Duis at tellus at urna condimentum mattis pellentesque id. Nulla facilisi nullam vehicula ipsum a arcu cursus vitae congue. Massa vitae tortor condimentum lacinia quis vel. Elementum pulvinar etiam non quam lacus suspendisse faucibus. Phasellus vestibulum lorem sed risus ultricies tristique nulla. A scelerisque purus semper eget duis at. Neque vitae tempus quam pellentesque nec nam aliquam sem et. Nisi scelerisque eu ultrices vitae auctor. Odio morbi quis commodo odio aenean sed adipiscing diam donec. Et tortor consequat id porta nibh. Velit sed ullamcorper morbi tincidunt. Felis bibendum ut tristique et egestas quis ipsum suspendisse. Lacus vestibulum sed arcu non. Amet mattis vulputate enim nulla. Tellus elementum sagittis vitae et leo duis. Amet consectetur adipiscing elit ut aliquam purus sit amet luctus. Quis enim lobortis scelerisque fermentum dui.
Pharetra sit amet aliquam id diam maecenas ultricies mi. Dignissim suspendisse in est ante in nibh. Fringilla ut morbi tincidunt augue interdum velit euismod in. Consectetur lorem donec massa sapien faucibus et molestie. Mattis aliquam faucibus purus in massa tempor nec feugiat nisl. Aliquet nec ullamcorper sit amet risus nullam. Amet risus nullam eget felis eget nunc. Faucibus in ornare quam viverra orci sagittis. Malesuada nunc vel risus commodo viverra. Hendrerit gravida rutrum quisque non tellus orci ac auctor. Tellus orci ac auctor augue mauris augue. Vel pretium lectus quam id leo in vitae turpis.
Mauris a diam maecenas sed enim ut. Consequat ac felis donec et odio pellentesque. Diam in arcu cursus euismod quis viverra. Malesuada fames ac turpis egestas. Senectus et netus et malesuada fames ac. In cursus turpis massa tincidunt dui. At quis risus sed vulputate odio ut enim blandit volutpat. Vitae proin sagittis nisl rhoncus mattis rhoncus urna. Amet nisl purus in mollis nunc sed id. Egestas fringilla phasellus faucibus scelerisque eleifend donec pretium vulputate. Facilisis sed odio morbi quis commodo odio aenean sed adipiscing. Bibendum neque egestas congue quisque egestas. Porta lorem mollis aliquam ut porttitor leo. Porta lorem mollis aliquam ut. Donec et odio pellentesque diam volutpat commodo sed egestas. Venenatis urna cursus eget nunc. Tortor dignissim convallis aenean et tortor at risus viverra. Sit amet mattis vulputate enim nulla. Ultricies mi eget mauris pharetra et ultrices neque.
Tortor at risus viverra adipiscing at in. Molestie a iaculis at erat pellentesque adipiscing. Consectetur libero id faucibus nisl tincidunt eget. Dictum varius duis at consectetur lorem donec. Turpis tincidunt id aliquet risus feugiat. Nibh cras pulvinar mattis nunc. Tortor aliquam nulla facilisi cras fermentum odio. Volutpat consequat mauris nunc congue nisi vitae suscipit. In dictum non consectetur a erat nam at lectus. Faucibus a pellentesque sit amet porttitor eget dolor. Sed turpis tincidunt id aliquet risus feugiat. Euismod elementum nisi quis eleifend. Sodales ut eu sem integer vitae justo eget magna. Erat nam at lectus urna. Eget aliquet nibh praesent tristique magna sit. Id donec ultrices tincidunt arcu non sodales neque. Ipsum suspendisse ultrices gravida dictum fusce ut. Duis convallis convallis tellus id interdum velit laoreet.
Tempus imperdiet nulla malesuada pellentesque elit eget gravida. A condimentum vitae sapien pellentesque habitant morbi tristique senectus et. Vestibulum rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt lobortis. Aliquet risus feugiat in ante metus dictum at. Et netus et malesuada fames ac. Integer enim neque volutpat ac tincidunt vitae semper quis lectus. Ut venenatis tellus in metus vulputate eu scelerisque. Ullamcorper morbi tincidunt ornare massa eget egestas purus viverra. Natoque penatibus et magnis dis parturient montes nascetur. Dignissim convallis aenean et tortor at. Odio aenean sed adipiscing diam donec adipiscing. Libero nunc consequat interdum varius sit. Erat imperdiet sed euismod nisi porta lorem. Enim lobortis scelerisque fermentum dui faucibus in ornare quam. Aliquam malesuada bibendum arcu vitae elementum curabitur vitae nunc.
Purus faucibus ornare suspendisse sed nisi lacus sed viverra. Dui id ornare arcu odio ut sem nulla. Porttitor lacus luctus accumsan tortor posuere. Arcu bibendum at varius vel pharetra vel. Scelerisque eu ultrices vitae auctor eu augue ut lectus arcu. Turpis egestas integer eget aliquet nibh praesent tristique magna. Tristique senectus et netus et malesuada fames. Id leo in vitae turpis massa sed. Egestas maecenas pharetra convallis posuere. Amet consectetur adipiscing elit pellentesque habitant morbi tristique senectus et. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Proin sagittis nisl rhoncus mattis rhoncus urna. Facilisi cras fermentum odio eu feugiat pretium nibh ipsum consequat.
Fames ac turpis egestas sed tempus urna. Sit amet est placerat in egestas erat. Dolor sed viverra ipsum nunc aliquet bibendum enim facilisis. Nunc sed id semper risus in hendrerit gravida rutrum quisque. Justo donec enim diam vulputate ut pharetra. Arcu non odio euismod lacinia at quis. Nunc lobortis mattis aliquam faucibus. Habitasse platea dictumst vestibulum rhoncus est pellentesque elit. Pretium lectus quam id leo. Tellus mauris a diam maecenas sed enim ut. Libero id faucibus nisl tincidunt eget. At erat pellentesque adipiscing commodo elit at imperdiet dui. Tincidunt praesent semper feugiat nibh sed pulvinar proin gravida. Vestibulum sed arcu non odio. Quam vulputate dignissim suspendisse in. Maecenas sed enim ut sem viverra aliquet eget sit amet. Placerat duis ultricies lacus sed turpis. Imperdiet nulla malesuada pellentesque elit eget gravida cum.
Nam aliquam sem et tortor. Ultricies mi quis hendrerit dolor magna eget est. Justo nec ultrices dui sapien eget mi proin. Ac odio tempor orci dapibus ultrices in. Consequat semper viverra nam libero justo. Sed vulputate mi sit amet mauris commodo quis imperdiet. Diam sit amet nisl suscipit adipiscing bibendum. Donec massa sapien faucibus et molestie ac feugiat sed. Augue neque gravida in fermentum et sollicitudin ac orci phasellus. Eget felis eget nunc lobortis mattis aliquam faucibus purus. Feugiat nisl pretium fusce id velit ut tortor pretium. Iaculis at erat pellentesque adipiscing commodo elit at.
Sed tempus urna et pharetra pharetra massa. Est sit amet facilisis magna etiam tempor. Interdum varius sit amet mattis vulputate enim. Lectus quam id leo in vitae turpis massa sed. Elementum integer enim neque volutpat ac. Leo urna molestie at elementum eu facilisis sed odio. Scelerisque mauris pellentesque pulvinar pellentesque habitant. Malesuada nunc vel risus commodo viverra maecenas accumsan lacus vel. Mi ipsum faucibus vitae aliquet nec ullamcorper. Ut consequat semper viverra nam libero justo. In aliquam sem fringilla ut. Id consectetur purus ut faucibus pulvinar elementum integer enim. Penatibus et magnis dis parturient.
Cursus eget nunc scelerisque viverra mauris in. Nam libero justo laoreet sit amet cursus sit amet. Et tortor at risus viverra adipiscing at. Nunc sed velit dignissim sodales ut. Non pulvinar neque laoreet suspendisse. Gravida neque convallis a cras semper auctor. Nec ultrices dui sapien eget mi. Ultrices eros in cursus turpis massa tincidunt dui ut ornare. Risus commodo viverra maecenas accumsan. A condimentum vitae sapien pellentesque. Porttitor lacus luctus accumsan tortor posuere. Quam pellentesque nec nam aliquam sem. Quisque non tellus orci ac auctor augue mauris augue neque. Et molestie ac feugiat sed lectus vestibulum mattis ullamcorper velit. Pellentesque diam volutpat commodo sed egestas egestas fringilla phasellus faucibus. Libero enim sed faucibus turpis in. Lectus sit amet est placerat in egestas erat. Pellentesque id nibh tortor id aliquet lectus proin nibh nisl. Felis eget nunc lobortis mattis aliquam faucibus purus in massa. Augue ut lectus arcu bibendum at.
Penatibus et magnis dis parturient montes. Mauris pharetra et ultrices neque ornare aenean euismod elementum nisi. Vitae ultricies leo integer malesuada. Enim facilisis gravida neque convallis a cras semper. Proin sed libero enim sed faucibus turpis in eu. Ullamcorper malesuada proin libero nunc consequat interdum varius sit. Leo urna molestie at elementum eu facilisis sed. Eget mauris pharetra et ultrices neque. Odio ut sem nulla pharetra. At ultrices mi tempus imperdiet nulla malesuada pellentesque elit. Id diam maecenas ultricies mi eget. Id eu nisl nunc mi ipsum faucibus vitae. At elementum eu facilisis sed odio morbi. Egestas tellus rutrum tellus pellentesque eu. Elementum integer enim neque volutpat ac tincidunt vitae semper quis. Suspendisse faucibus interdum posuere lorem. Quis imperdiet massa tincidunt nunc pulvinar sapien et. Montes nascetur ridiculus mus mauris vitae ultricies leo.
Aliquam faucibus purus in massa tempor. Urna nunc id cursus metus aliquam eleifend mi in nulla. Sit amet porttitor eget dolor morbi non arcu risus quis. Porta nibh venenatis cras sed felis eget velit aliquet. Sem et tortor consequat id. Id semper risus in hendrerit gravida rutrum quisque non tellus. Mattis pellentesque id nibh tortor id. Massa tincidunt nunc pulvinar sapien et ligula ullamcorper malesuada proin. Suspendisse sed nisi lacus sed viverra tellus in hac. Nisi quis eleifend quam adipiscing vitae. Tellus integer feugiat scelerisque varius morbi enim nunc faucibus. Ipsum faucibus vitae aliquet nec ullamcorper sit amet. Quam elementum pulvinar etiam non quam lacus suspendisse faucibus. Ultricies integer quis auctor elit sed.
Sodales neque sodales ut etiam sit amet nisl purus. Sed euismod nisi porta lorem mollis aliquam ut porttitor leo. Mi in nulla posuere sollicitudin. Purus sit amet volutpat consequat. Sapien faucibus et molestie ac. In hac habitasse platea dictumst quisque sagittis. Pellentesque massa placerat duis ultricies lacus sed turpis tincidunt. In eu mi bibendum neque. Quam quisque id diam vel quam. Dolor sed viverra ipsum nunc aliquet bibendum enim facilisis gravida. Nunc sed augue lacus viverra. Est ullamcorper eget nulla facilisi etiam dignissim diam quis enim. Non curabitur gravida arcu ac tortor dignissim. Porttitor eget dolor morbi non arcu risus quis varius.
Nulla pellentesque dignissim enim sit amet venenatis. Accumsan sit amet nulla facilisi morbi tempus iaculis. Turpis in eu mi bibendum neque egestas congue quisque. Est ante in nibh mauris cursus mattis molestie a iaculis. Habitant morbi tristique senectus et netus et malesuada. Facilisi cras fermentum odio eu feugiat pretium nibh. Aliquet lectus proin nibh nisl condimentum id venenatis a. Pellentesque dignissim enim sit amet. Maecenas ultricies mi eget mauris. Dignissim enim sit amet venenatis. Tincidunt lobortis feugiat vivamus at augue eget arcu dictum varius. Pellentesque sit amet porttitor eget dolor morbi. Pharetra pharetra massa massa ultricies mi quis hendrerit. Tortor pretium viverra suspendisse potenti nullam ac tortor vitae purus.
Amet aliquam id diam maecenas. Dignissim enim sit amet venenatis urna cursus eget nunc. Pellentesque adipiscing commodo elit at imperdiet. Etiam dignissim diam quis enim lobortis. Et magnis dis parturient montes. Aliquam vestibulum morbi blandit cursus risus at ultrices mi tempus. Ac orci phasellus egestas tellus rutrum tellus pellentesque. Metus dictum at tempor commodo ullamcorper. Sit amet cursus sit amet dictum sit amet justo. Eu mi bibendum neque egestas congue quisque egestas diam. Ac tortor dignissim convallis aenean et tortor at.
Hac habitasse platea dictumst quisque sagittis purus sit. Nunc pulvinar sapien et ligula ullamcorper malesuada proin libero nunc. Proin libero nunc consequat interdum varius. Cursus vitae congue mauris rhoncus aenean. Elementum pulvinar etiam non quam lacus suspendisse. Pellentesque id nibh tortor id. Diam vel quam elementum pulvinar etiam non. Magna etiam tempor orci eu lobortis elementum nibh tellus. Vitae suscipit tellus mauris a diam maecenas sed enim. Elit pellentesque habitant morbi tristique senectus. Integer malesuada nunc vel risus commodo viverra. Ut diam quam nulla porttitor massa. Sit amet nulla facilisi morbi tempus iaculis urna id. Sed vulputate mi sit amet mauris commodo quis imperdiet massa.
Tortor at risus viverra adipiscing at in. Eget nunc scelerisque viverra mauris. Platea dictumst vestibulum rhoncus est pellentesque elit ullamcorper dignissim. In tellus integer feugiat scelerisque varius morbi. Nec ultrices dui sapien eget mi. Risus commodo viverra maecenas accumsan lacus vel facilisis volutpat. Cras sed felis eget velit aliquet sagittis id consectetur. Convallis aenean et tortor at risus. Duis at consectetur lorem donec massa. Egestas congue quisque egestas diam in. Orci sagittis eu volutpat odio facilisis. Fringilla est ullamcorper eget nulla. Ut tristique et egestas quis ipsum suspendisse. Ornare massa eget egestas purus viverra accumsan. Fermentum posuere urna nec tincidunt praesent semper feugiat nibh sed. Interdum velit euismod in pellentesque massa placerat duis. Libero volutpat sed cras ornare arcu dui vivamus arcu felis.
Purus sit amet volutpat consequat mauris nunc. Neque ornare aenean euismod elementum. Ullamcorper a lacus vestibulum sed arcu non odio euismod lacinia. Risus pretium quam vulputate dignissim suspendisse. Justo nec ultrices dui sapien eget. Scelerisque eu ultrices vitae auctor eu augue ut. Amet nisl purus in mollis nunc sed id semper risus. Sit amet risus nullam eget felis eget nunc lobortis mattis. Venenatis a condimentum vitae sapien pellentesque habitant morbi. Ipsum suspendisse ultrices gravida dictum fusce ut placerat. Viverra vitae congue eu consequat ac felis donec et. Pharetra sit amet aliquam id. In fermentum posuere urna nec tincidunt praesent semper feugiat nibh. Semper auctor neque vitae tempus quam. Erat velit scelerisque in dictum non consectetur. Ut aliquam purus sit amet luctus venenatis.
Diam maecenas sed enim ut sem viverra aliquet. Facilisis magna etiam tempor orci eu lobortis elementum nibh tellus. In vitae turpis massa sed elementum. Dolor sit amet consectetur adipiscing elit duis tristique sollicitudin. Felis imperdiet proin fermentum leo vel orci porta non pulvinar. Ut placerat orci nulla pellentesque dignissim enim sit amet. Amet mauris commodo quis imperdiet massa. Sed velit dignissim sodales ut eu sem integer. Diam vel quam elementum pulvinar. Nulla facilisi etiam dignissim diam quis enim lobortis scelerisque fermentum. Justo laoreet sit amet cursus sit amet. Cursus metus aliquam eleifend mi in nulla posuere.
Auctor eu augue ut lectus arcu bibendum. Nunc non blandit massa enim nec dui nunc. Vitae semper quis lectus nulla at volutpat diam ut. Aliquet bibendum enim facilisis gravida. Semper risus in hendrerit gravida. Quam elementum pulvinar etiam non quam lacus suspendisse faucibus. Pharetra magna ac placerat vestibulum. Ut diam quam nulla porttitor. Arcu dui vivamus arcu felis bibendum ut tristique et. Ut tristique et egestas quis ipsum suspendisse. Mattis vulputate enim nulla aliquet porttitor. Nisl suscipit adipiscing bibendum est ultricies integer quis. Faucibus vitae aliquet nec ullamcorper. Non diam phasellus vestibulum lorem sed risus ultricies tristique nulla. Tristique magna sit amet purus gravida quis blandit.
Ultricies integer quis auctor elit sed vulputate mi. Interdum varius sit amet mattis vulputate enim nulla aliquet. Ullamcorper malesuada proin libero nunc consequat. Egestas integer eget aliquet nibh praesent. Ac turpis egestas integer eget aliquet. Felis imperdiet proin fermentum leo vel orci porta non pulvinar. Pharetra diam sit amet nisl suscipit adipiscing bibendum est. Volutpat diam ut venenatis tellus in metus vulputate. Sapien eget mi proin sed libero enim sed faucibus turpis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus. Egestas egestas fringilla phasellus faucibus scelerisque eleifend. Senectus et netus et malesuada fames ac turpis. Diam vel quam elementum pulvinar etiam non quam. Commodo odio aenean sed adipiscing diam donec adipiscing tristique risus. Leo a diam sollicitudin tempor. Neque aliquam vestibulum morbi blandit cursus risus. Vitae et leo duis ut diam quam nulla porttitor. At elementum eu facilisis sed odio morbi quis commodo odio. Eu volutpat odio facilisis mauris sit amet massa vitae tortor.
Interdum varius sit amet mattis vulputate enim nulla. Pellentesque habitant morbi tristique senectus. Pretium fusce id velit ut tortor. Sit amet nisl purus in mollis nunc sed id. Massa tincidunt dui ut ornare lectus sit amet. Mi bibendum neque egestas congue quisque egestas diam. At tempor commodo ullamcorper a lacus. Et ligula ullamcorper malesuada proin libero nunc consequat interdum varius. Nibh venenatis cras sed felis eget. Auctor augue mauris augue neque gravida in fermentum et. Non enim praesent elementum facilisis. Felis bibendum ut tristique et egestas quis ipsum suspendisse ultrices. Facilisis mauris sit amet massa vitae tortor condimentum.
Erat nam at lectus urna duis convallis. Tristique et egestas quis ipsum suspendisse ultrices gravida. At volutpat diam ut venenatis. Vestibulum mattis ullamcorper velit sed. Odio facilisis mauris sit amet massa vitae tortor. Massa sed elementum tempus egestas sed sed risus. Sed egestas egestas fringilla phasellus faucibus. Sed sed risus pretium quam vulputate. Sed risus ultricies tristique nulla aliquet. In ornare quam viverra orci sagittis. In fermentum et sollicitudin ac orci phasellus egestas tellus. Et netus et malesuada fames. Feugiat nisl pretium fusce id. Rhoncus dolor purus non enim praesent elementum facilisis.
Scelerisque in dictum non consectetur a erat nam. Vivamus at augue eget arcu dictum varius duis at consectetur. Turpis egestas sed tempus urna. Eu mi bibendum neque egestas congue quisque egestas. Rhoncus aenean vel elit scelerisque. Risus pretium quam vulputate dignissim suspendisse in est. Iaculis at erat pellentesque adipiscing commodo elit at imperdiet dui. Proin nibh nisl condimentum id venenatis a condimentum. Eleifend quam adipiscing vitae proin sagittis nisl rhoncus mattis. Felis eget velit aliquet sagittis id. Nibh tortor id aliquet lectus.
Binary file not shown.
@@ -0,0 +1,177 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import XCTest
@testable import StreamKit
final class Salsa20CryptorTests: XCTestCase {
func testForWrongKey() {
let sourceBufLen = 128
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(16)
let iv = genBufferOfLen(16)
XCTAssertThrowsError(try encrypt(sourceBuf, sourceBufLen, key, iv))
}
func testEncryptZeroLenBuffer() throws {
let sourceBufLen = 0
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(8)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
XCTAssertTrue(encryptedBuf.count == 0)
}
func testEncrypt64Bytes() throws {
let sourceBufLen = 64
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(8)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
XCTAssertTrue(encryptedBuf.count == 64)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
func testEncrypt65Bytes() throws {
let sourceBufLen = 65
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(8)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
XCTAssertEqual(65, encryptedBuf.count)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
func testEncrypt128Bytes() throws {
let sourceBufLen = 128
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(8)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
XCTAssertTrue(encryptedBuf.count == 128)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
func testEncryptDecryptZeroLenBuffer() throws {
try encryptDecryptBufferOfLen(0, 32, 64, 64)
}
func testEncryptVariousLenBuffers() throws {
for pow in 0...14 {
let sourceBufLen = 1<<pow
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(16)
let iv = genBufferOfLen(8)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
XCTAssertTrue(encryptedBuf.count == sourceBuf.count)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
}
func testEncryptDecryptVariousLenBuffers() throws {
for pow in 0...14 {
try encryptDecryptBufferOfLen(1<<pow)
}
}
func disabled_testPerformanceEncryptDecrypt1MBFile() throws {
self.measure {
try! encryptDecryptBufferOfLen(1<<20)
}
}
}
extension Salsa20CryptorTests {
func encrypt(_ buffer: UnsafePointer<UInt8>,
_ len: Int,
_ key: [UInt8],
_ iv: [UInt8],
_ chunkSize: Int = Salsa20OutputStream.defaultChunkSize
) throws -> [UInt8] {
let dataOutputStream = BufferOutputStream()
try dataOutputStream.open()
let encryptingStream = Salsa20OutputStream(writingTo: dataOutputStream,
key: key,
iv: iv,
chunkSize: chunkSize)
try encryptingStream.open()
try encryptingStream.write(buffer, length: len)
try encryptingStream.close()
let resultData = dataOutputStream.buffer
try dataOutputStream.close()
return resultData
}
func decrypt(_ buffer: [UInt8],
_ key: [UInt8],
_ iv: [UInt8],
_ chunkSize: Int = Salsa20InputStream.defaultChunkSize,
_ iterBufSize: Int
) throws -> [UInt8] {
let dataInputStream = BufferInputStream(withBuffer: buffer)
try dataInputStream.open()
let decryptingStream = Salsa20InputStream(readingFrom: dataInputStream,
key: key,
iv: iv,
chunkSize: chunkSize)
try decryptingStream.open()
var result = Array<UInt8>()
while decryptingStream.hasBytesAvailable {
var readBuffer = Array<UInt8>(repeating: 0, count: iterBufSize)
let readLen = try decryptingStream.read(&readBuffer, maxLength: iterBufSize)
result.append(contentsOf: readBuffer.prefix(readLen))
}
return result
}
func encryptDecryptBufferOfLen(_ bufLen: Int) throws {
for iterBufSize in [64,65,127,128,129,256] {
for chunkSize in [64,65,127,128,129,256] {
for keySize in [16,32] {
print("\(bufLen) \(keySize) \(chunkSize) \(iterBufSize) ")
try encryptDecryptBufferOfLen(bufLen, keySize, chunkSize, iterBufSize)
}
}
}
}
func encryptDecryptBufferOfLen(_ bufLen: Int,
_ keyLen: Int,
_ chunkSize: Int = Salsa20OutputStream.defaultChunkSize,
_ iterBufSize: Int
) throws {
let sourceBuf = genBufferOfLen(bufLen)
let key = genBufferOfLen(keyLen)
let iv = genBufferOfLen(8)
let encryptedBuf = try encrypt(sourceBuf, bufLen, key, iv, chunkSize)
let decryptedBuf = try decrypt(encryptedBuf, key, iv, chunkSize, iterBufSize)
XCTAssertEqual(bufLen, decryptedBuf.count, "\(bufLen) \(keyLen) \(chunkSize)")
XCTAssertEqual(sourceBuf, decryptedBuf, "\(bufLen) \(keyLen) \(chunkSize)")
}
}
+87
View File
@@ -0,0 +1,87 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import XCTest
import StreamKit
final class StreamKitExtensionsTests: XCTestCase {
static var tmpDir: URL!
override class func setUp() {
super.setUp()
tmpDir = try! createTmpFolder()
}
override class func tearDown() {
super.tearDown()
try! removeTmpFolder(tmpDir)
}
func testEncryptingDecryptingStringUsingSalsa20Streams() throws {
let key = genBufferOfLen(32)
let iv = genBufferOfLen(8)
let encryptedFileURL = genTmpFileURL(Self.tmpDir)
let originString = genRandStr(100)
try encryptStrUsingSalsa20(originString, key, iv, encryptedFileURL)
let decryptedString = try decryptFileUsingSalsa20(encryptedFileURL, key, iv)
XCTAssertEqual(originString, decryptedString)
}
}
extension StreamKitExtensionsTests {
func encryptStrUsingSalsa20(_ str: String, _ key: [UInt8], _ iv: [UInt8], _ outURL: URL) throws {
let outputFileStream = FileOutputStream(with: outURL)!
try outputFileStream.open()
let encryptingStream = Salsa20OutputStream(writingTo: outputFileStream,
key: key,
iv: iv)
try encryptingStream.open()
try encryptingStream.write(str, ofEncoding: .utf8)
try encryptingStream.close()
try outputFileStream.close()
}
func decryptFileUsingSalsa20(_ encryptedFileURL: URL, _ key: [UInt8], _ iv: [UInt8]) throws -> String? {
let inputFileStream = FileInputStream(with: try! FileHandle(forReadingFrom: encryptedFileURL))
try inputFileStream.open()
let decryptingStream = Salsa20InputStream(readingFrom: inputFileStream,
key: key,
iv: iv)
try decryptingStream.open()
let data = try decryptingStream.readToEnd()
decryptingStream.close()
inputFileStream.close()
return String(data: data, encoding: .utf8)
}
}
+526
View File
@@ -0,0 +1,526 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import XCTest
import StreamKit
final class StreamKitTests: XCTestCase {
static var tmpDir: URL!
override class func setUp() {
super.setUp()
tmpDir = try! createTmpFolder()
}
override class func tearDown() {
super.tearDown()
try! removeTmpFolder(tmpDir)
}
func testEncryptingDecrypting1MBFileUsingSalsa20Streams() throws {
let key = genBufferOfLen(32)
let iv = genBufferOfLen(8)
let originalFileURL = fileURL("1MB")!
let encryptedFileURL = try encryptFileUsingSalsa20(originalFileURL, key, iv)
let decryptedFileURL = try decryptFileUsingSalsa20(encryptedFileURL, key, iv)
XCTAssertNotEqual(md5(originalFileURL), md5(encryptedFileURL))
XCTAssertEqual(md5(originalFileURL), md5(decryptedFileURL))
}
func testCompressEncryptDecryptDecompress1MBFileUsingSalsa20Stream() throws {
let key = genBufferOfLen(32)
let iv = genBufferOfLen(8)
let originalFileURL = fileURL("1MB")!
let encryptedFileURL = try compressAndEncryptFileUsingSalsa20(originalFileURL, key, iv)
let decryptedFileURL = try decryptAndDecompressFileUsingSalsa20(encryptedFileURL, key, iv)
XCTAssertNotEqual(md5(originalFileURL), md5(encryptedFileURL))
XCTAssertEqual(md5(originalFileURL), md5(decryptedFileURL))
}
func testEncryptingDecrypting1MBFileUsingChaCha20Streams() throws {
let key = genBufferOfLen(32)
let iv = genBufferOfLen(12)
let originalFileURL = fileURL("1MB")!
let encryptedFileURL = try encryptFileUsingChaCha20(originalFileURL, key, iv)
let decryptedFileURL = try decryptFileUsingChaCha20(encryptedFileURL, key, iv)
XCTAssertNotEqual(md5(originalFileURL), md5(encryptedFileURL))
XCTAssertEqual(md5(originalFileURL), md5(decryptedFileURL))
}
func testCompressEncryptDecryptDecompress1MBFileUsingChaCha20Stream() throws {
let key = genBufferOfLen(32)
let iv = genBufferOfLen(12)
let originalFileURL = fileURL("1MB")!
let encryptedFileURL = try compressAndEncryptFileUsingChaCha20(originalFileURL, key, iv)
let decryptedFileURL = try decryptAndDecompressFileUsingChaCha20(encryptedFileURL, key, iv)
XCTAssertNotEqual(md5(originalFileURL), md5(encryptedFileURL))
XCTAssertEqual(md5(originalFileURL), md5(decryptedFileURL))
}
func testEncryptingDecrypting1MBFileUsingAesStream() throws {
let key = genBufferOfLen(32)
let iv = genBufferOfLen(16)
let originalFileURL = fileURL("1MB")!
let encryptedFileURL = try encryptFileUsingAes(originalFileURL, key, iv)
let decryptedFileURL = try decryptFileUsingAes(encryptedFileURL, key, iv)
XCTAssertNotEqual(md5(originalFileURL), md5(encryptedFileURL))
XCTAssertEqual(md5(originalFileURL), md5(decryptedFileURL))
}
func testCompressEncryptDecryptDecompress1MBFileUsingAesStream() throws {
let key = genBufferOfLen(32)
let iv = genBufferOfLen(16)
let originalFileURL = fileURL("1MB")!
let encryptedFileURL = try compressAndEncryptFileUsingAes(originalFileURL, key, iv)
let decryptedFileURL = try decryptAndDecompressFileUsingAes(encryptedFileURL, key, iv)
XCTAssertNotEqual(md5(originalFileURL), md5(encryptedFileURL))
XCTAssertEqual(md5(originalFileURL), md5(decryptedFileURL))
}
func testDecompressing() throws {
let originalFileURL = fileURL("PlainText")!
let compressedFileURL = fileURL("PlainText","gz")!
let decompressedFileURL = try decompressFileUsingGzip(compressedFileURL)
XCTAssertEqual(md5(originalFileURL), md5(decompressedFileURL))
}
func testCompressDecompress1MBFileUsingGzipStream() throws {
let originalFileURL = fileURL("1MB")!
let compressedFileURL = try compressFileUsingGzip(originalFileURL)
let decompressedFileURL = try decompressFileUsingGzip(compressedFileURL)
XCTAssertNotEqual(md5(originalFileURL), md5(compressedFileURL))
XCTAssertEqual(md5(originalFileURL), md5(decompressedFileURL))
}
}
extension StreamKitTests {
func encryptFileUsingSalsa20(_ originalFileURL: URL, _ key: [UInt8], _ iv: [UInt8]) throws -> URL {
let inputFileStream = FileInputStream(with: try! FileHandle(forReadingFrom: originalFileURL))
try inputFileStream.open()
let encryptedFileURL = createTmpFileURL(Self.tmpDir)
let outputFileStream = FileOutputStream(with: try! FileHandle(forWritingTo: encryptedFileURL))
try outputFileStream.open()
let encryptingStream = Salsa20OutputStream(writingTo: outputFileStream,
key: key,
iv: iv)
try encryptingStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while inputFileStream.hasBytesAvailable {
let readLen = inputFileStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try encryptingStream.write(tmpBuffer, length: readLen)
}
try encryptingStream.close()
try outputFileStream.close()
inputFileStream.close()
return encryptedFileURL
}
func decryptAndDecompressFileUsingSalsa20(_ encryptedFileURL: URL, _ key: [UInt8], _ iv: [UInt8]) throws -> URL {
let inputFileStream = FileInputStream(with: try! FileHandle(forReadingFrom: encryptedFileURL))
try inputFileStream.open()
let decryptedFileURL = createTmpFileURL(Self.tmpDir)
let outputFileStream = FileOutputStream(with: try! FileHandle(forWritingTo: decryptedFileURL))
try outputFileStream.open()
let decryptingStream = Salsa20InputStream(readingFrom: inputFileStream,
key: key,
iv: iv)
try decryptingStream.open()
let decompressingStream = GzipInputStream(readingFrom: decryptingStream)
try decompressingStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while decryptingStream.hasBytesAvailable {
let readLen = try decompressingStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try outputFileStream.write(tmpBuffer, length: readLen)
}
decompressingStream.close()
decryptingStream.close()
try outputFileStream.close()
inputFileStream.close()
return decryptedFileURL
}
func compressAndEncryptFileUsingSalsa20(_ originalFileURL: URL, _ key: [UInt8], _ iv: [UInt8]) throws -> URL {
let inputFileStream = FileInputStream(with: try! FileHandle(forReadingFrom: originalFileURL))
try inputFileStream.open()
inputFileStream.close()
let encryptedFileURL = createTmpFileURL(Self.tmpDir)
let outputFileStream = FileOutputStream(with: try! FileHandle(forWritingTo: encryptedFileURL))
try outputFileStream.open()
let encryptingStream = Salsa20OutputStream(writingTo: outputFileStream,
key: key,
iv: iv)
try encryptingStream.open()
let compressingStream = GzipOutputStream(writingTo: encryptingStream)
try compressingStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while inputFileStream.hasBytesAvailable {
let readLen = inputFileStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try compressingStream.write(tmpBuffer, length: readLen)
}
try compressingStream.close()
try encryptingStream.close()
try outputFileStream.close()
inputFileStream.close()
return encryptedFileURL
}
func decryptFileUsingSalsa20(_ encryptedFileURL: URL, _ key: [UInt8], _ iv: [UInt8]) throws -> URL {
let inputFileStream = FileInputStream(with: try! FileHandle(forReadingFrom: encryptedFileURL))
try inputFileStream.open()
let decryptedFileURL = createTmpFileURL(Self.tmpDir)
let outputFileStream = FileOutputStream(with: try! FileHandle(forWritingTo: decryptedFileURL))
try outputFileStream.open()
let decryptingStream = Salsa20InputStream(readingFrom: inputFileStream,
key: key,
iv: iv)
try decryptingStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while decryptingStream.hasBytesAvailable {
let readLen = try decryptingStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try outputFileStream.write(tmpBuffer, length: readLen)
}
decryptingStream.close()
try outputFileStream.close()
inputFileStream.close()
return decryptedFileURL
}
func encryptFileUsingChaCha20(_ originalFileURL: URL, _ key: [UInt8], _ iv: [UInt8]) throws -> URL {
let inputFileStream = FileInputStream(with: try! FileHandle(forReadingFrom: originalFileURL))
try inputFileStream.open()
let encryptedFileURL = createTmpFileURL(Self.tmpDir)
let outputFileStream = FileOutputStream(with: try! FileHandle(forWritingTo: encryptedFileURL))
try outputFileStream.open()
let encryptingStream = ChaCha20OutputStream(writingTo: outputFileStream,
key: key,
iv: iv)
try encryptingStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while inputFileStream.hasBytesAvailable {
let readLen = inputFileStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try encryptingStream.write(tmpBuffer, length: readLen)
}
try encryptingStream.close()
try outputFileStream.close()
inputFileStream.close()
return encryptedFileURL
}
func decryptFileUsingChaCha20(_ encryptedFileURL: URL, _ key: [UInt8], _ iv: [UInt8]) throws -> URL {
let inputFileStream = FileInputStream(with: try! FileHandle(forReadingFrom: encryptedFileURL))
try inputFileStream.open()
let decryptedFileURL = createTmpFileURL(Self.tmpDir)
let outputFileStream = FileOutputStream(with: try! FileHandle(forWritingTo: decryptedFileURL))
try outputFileStream.open()
let decryptingStream = ChaCha20InputStream(readingFrom: inputFileStream,
key: key,
iv: iv)
try decryptingStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while decryptingStream.hasBytesAvailable {
let readLen = try decryptingStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try outputFileStream.write(tmpBuffer, length: readLen)
}
decryptingStream.close()
try outputFileStream.close()
inputFileStream.close()
return decryptedFileURL
}
func compressAndEncryptFileUsingChaCha20(_ originalFileURL: URL, _ key: [UInt8], _ iv: [UInt8]) throws -> URL {
let inputFileStream = FileInputStream(with: try! FileHandle(forReadingFrom: originalFileURL))
try inputFileStream.open()
inputFileStream.close()
let encryptedFileURL = createTmpFileURL(Self.tmpDir)
let outputFileStream = FileOutputStream(with: try! FileHandle(forWritingTo: encryptedFileURL))
try outputFileStream.open()
let encryptingStream = ChaCha20OutputStream(writingTo: outputFileStream,
key: key,
iv: iv)
try encryptingStream.open()
let compressingStream = GzipOutputStream(writingTo: encryptingStream)
try compressingStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while inputFileStream.hasBytesAvailable {
let readLen = inputFileStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try compressingStream.write(tmpBuffer, length: readLen)
}
try compressingStream.close()
try encryptingStream.close()
try outputFileStream.close()
inputFileStream.close()
return encryptedFileURL
}
func decryptAndDecompressFileUsingChaCha20(_ encryptedFileURL: URL, _ key: [UInt8], _ iv: [UInt8]) throws -> URL {
let inputFileStream = FileInputStream(with: try! FileHandle(forReadingFrom: encryptedFileURL))
try inputFileStream.open()
let decryptedFileURL = createTmpFileURL(Self.tmpDir)
let outputFileStream = FileOutputStream(with: try! FileHandle(forWritingTo: decryptedFileURL))
try outputFileStream.open()
let decryptingStream = ChaCha20InputStream(readingFrom: inputFileStream,
key: key,
iv: iv)
try decryptingStream.open()
let decompressingStream = GzipInputStream(readingFrom: decryptingStream)
try decompressingStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while decryptingStream.hasBytesAvailable {
let readLen = try decompressingStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try outputFileStream.write(tmpBuffer, length: readLen)
}
decompressingStream.close()
decryptingStream.close()
try outputFileStream.close()
inputFileStream.close()
return decryptedFileURL
}
func encryptFileUsingAes(_ originalFileURL: URL, _ key: [UInt8], _ iv: [UInt8]) throws -> URL {
let inputFileStream = FileInputStream(with: try! FileHandle(forReadingFrom: originalFileURL))
try inputFileStream.open()
let encryptedFileURL = createTmpFileURL(Self.tmpDir)
let outputFileStream = FileOutputStream(with: try! FileHandle(forWritingTo: encryptedFileURL))
try outputFileStream.open()
let encryptingStream = AesOutputStream(writingTo: outputFileStream,
key: key,
iv: iv)
try encryptingStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while inputFileStream.hasBytesAvailable {
let readLen = inputFileStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try encryptingStream.write(tmpBuffer, length: readLen)
}
try encryptingStream.close()
try outputFileStream.close()
inputFileStream.close()
return encryptedFileURL
}
func compressAndEncryptFileUsingAes(_ originalFileURL: URL, _ key: [UInt8], _ iv: [UInt8]) throws -> URL {
let inputFileStream = FileInputStream(with: try! FileHandle(forReadingFrom: originalFileURL))
try inputFileStream.open()
inputFileStream.close()
let encryptedFileURL = createTmpFileURL(Self.tmpDir)
let outputFileStream = FileOutputStream(with: try! FileHandle(forWritingTo: encryptedFileURL))
try outputFileStream.open()
let encryptingStream = AesOutputStream(writingTo: outputFileStream,
key: key,
iv: iv)
try encryptingStream.open()
let compressingStream = GzipOutputStream(writingTo: encryptingStream)
try compressingStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while inputFileStream.hasBytesAvailable {
let readLen = inputFileStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try compressingStream.write(tmpBuffer, length: readLen)
}
try compressingStream.close()
try encryptingStream.close()
try outputFileStream.close()
inputFileStream.close()
return encryptedFileURL
}
func decryptFileUsingAes(_ encryptedFileURL: URL, _ key: [UInt8], _ iv: [UInt8]) throws -> URL {
let inputFileStream = FileInputStream(with: try! FileHandle(forReadingFrom: encryptedFileURL))
try inputFileStream.open()
let decryptedFileURL = createTmpFileURL(Self.tmpDir)
let outputFileStream = FileOutputStream(with: try! FileHandle(forWritingTo: decryptedFileURL))
try outputFileStream.open()
let decryptingStream = AesInputStream(readingFrom: inputFileStream,
key: key,
iv: iv)
try decryptingStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while decryptingStream.hasBytesAvailable {
let readLen = try decryptingStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try outputFileStream.write(tmpBuffer, length: readLen)
}
decryptingStream.close()
try outputFileStream.close()
inputFileStream.close()
return decryptedFileURL
}
func decryptAndDecompressFileUsingAes(_ encryptedFileURL: URL, _ key: [UInt8], _ iv: [UInt8]) throws -> URL {
let inputFileStream = FileInputStream(with: try! FileHandle(forReadingFrom: encryptedFileURL))
try inputFileStream.open()
let decryptedFileURL = createTmpFileURL(Self.tmpDir)
let outputFileStream = FileOutputStream(with: try! FileHandle(forWritingTo: decryptedFileURL))
try outputFileStream.open()
let decryptingStream = AesInputStream(readingFrom: inputFileStream,
key: key,
iv: iv)
try decryptingStream.open()
let decompressingStream = GzipInputStream(readingFrom: decryptingStream)
try decompressingStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while decryptingStream.hasBytesAvailable {
let readLen = try decompressingStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try outputFileStream.write(tmpBuffer, length: readLen)
}
decryptingStream.close()
try outputFileStream.close()
inputFileStream.close()
return decryptedFileURL
}
func compressFileUsingGzip(_ originalFileURL: URL) throws -> URL {
let inputFileStream = FileInputStream(with: try! FileHandle(forReadingFrom: originalFileURL))
try inputFileStream.open()
let compressedFileURL = createTmpFileURL(Self.tmpDir)
let outputFileStream = FileOutputStream(with: try! FileHandle(forWritingTo: compressedFileURL))
try outputFileStream.open()
let compressingStream = GzipOutputStream(writingTo: outputFileStream)
try compressingStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while inputFileStream.hasBytesAvailable {
let readLen = inputFileStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try compressingStream.write(tmpBuffer, length: readLen)
}
try compressingStream.close()
try outputFileStream.close()
inputFileStream.close()
return compressedFileURL
}
func decompressFileUsingGzip(_ compressedFileURL: URL) throws -> URL {
let inputFileStream = FileInputStream(with: try! FileHandle(forReadingFrom: compressedFileURL))
try inputFileStream.open()
let decompressedFileURL = createTmpFileURL(Self.tmpDir)
let outputFileStream = FileOutputStream(with: try! FileHandle(forWritingTo: decompressedFileURL))
try outputFileStream.open()
let decompressingStream = GzipInputStream(readingFrom: inputFileStream)
try decompressingStream.open()
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while decompressingStream.hasBytesAvailable {
let readLen = try decompressingStream.read(&tmpBuffer, maxLength: tmpBufferLen)
try outputFileStream.write(tmpBuffer, length: readLen)
}
decompressingStream.close()
try outputFileStream.close()
inputFileStream.close()
return decompressedFileURL
}
}
@@ -0,0 +1,86 @@
//
// MIT License
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import XCTest
import Foundation
extension XCTestCase {
func genBufferOfLen(_ len: Int) -> [UInt8] {
if len == 0 { return [] }
return (0..<len).map { _ in UInt8.random(in: 0...UInt8.max) }
}
class private var tmpFolderURL: URL {
let url = FileManager.default.temporaryDirectory
return url
}
class private var tmpFolderStr: String {
return tmpFolderURL.path
}
class func genRandStr(_ length: Int) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return String((0..<length).map{ _ in letters.randomElement()! })
}
func genRandStr(_ length: Int) -> String {
return Self.genRandStr(length)
}
class func createTmpFolder() throws -> URL {
let randStr = genRandStr(32)
let pathURL = tmpFolderURL.appendingPathComponent(randStr)
let fm = FileManager.default
let path = pathURL.path
try fm.createDirectory(atPath: path, withIntermediateDirectories: true)
return pathURL
}
class func removeTmpFolder(_ url: URL) throws {
let fm = FileManager.default
try fm.removeItem(at: url)
}
func genTmpFileURL(_ folder: URL) -> URL {
let fileName = genRandStr(32)
let filePath = folder.appendingPathComponent(fileName)
FileManager.default.createFile(atPath: filePath.path, contents: nil)
return filePath
}
func createTmpFileURL(_ folder: URL) -> URL {
let filePath = genTmpFileURL(folder)
FileManager.default.createFile(atPath: filePath.path, contents: nil)
return filePath
}
func fileURL(_ file: String, _ ext: String? = nil) -> URL? {
let url = Bundle.module.url(forResource: file, withExtension: ext)!
return url
}
}
@@ -0,0 +1,265 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import XCTest
import StreamKit
final class TwoFishStreamTests: XCTestCase {
func testForWrongKey() {
let sourceBufLen = 128
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(33)
let iv = genBufferOfLen(16)
XCTAssertThrowsError(try encrypt(sourceBuf, sourceBufLen, key, iv))
}
func testForWrongIV() {
let sourceBufLen = 128
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(13)
XCTAssertThrowsError(try encrypt(sourceBuf, sourceBufLen, key, iv))
}
func testEncryptZeroLenBuffer() throws {
let sourceBufLen = 0
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(16)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
XCTAssertEqual(16, encryptedBuf.count)
}
func testEncrypt16Bytes() throws {
let sourceBufLen = 16
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(16)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
XCTAssertEqual(32, encryptedBuf.count)
}
func testEncrypt16BytesWithChunk16Bytes() throws {
let sourceBufLen = 16
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(16)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv, 16)
XCTAssertEqual(32, encryptedBuf.count)
}
func testEncrypt63Bytes() throws {
let sourceBufLen = 63
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(16)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
XCTAssertEqual(64, encryptedBuf.count)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
func testEncrypt64Bytes() throws {
let sourceBufLen = 64
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(16)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
XCTAssertEqual(80, encryptedBuf.count)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
func testEncrypt65Bytes() throws {
let sourceBufLen = 65
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(16)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
XCTAssertEqual(80, encryptedBuf.count)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
func testEncrypt127Bytes() throws {
let sourceBufLen = 127
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(16)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv, 127)
XCTAssertEqual(128, encryptedBuf.count)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
func testEncrypt128Bytes() throws {
let sourceBufLen = 128
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(16)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv, 128)
XCTAssertEqual(144, encryptedBuf.count)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
func testEncrypt256Bytes() throws {
let sourceBufLen = 256
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(16)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv, 256)
XCTAssertEqual(272, encryptedBuf.count)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
func testEncryptDecryptZeroLenBuffer() throws {
try encryptDecryptBufferOfLen(0, 32)
}
func testEncryptDecrypt16Bytes() throws {
try encryptDecryptBufferOfLen(16,32)
}
func testEncryptDecrypt16BytesWithChunk16Bytes() throws {
try encryptDecryptBufferOfLen(16,32,16)
}
func testEncryptDecrypt17BytesWithChunk16Bytes() throws {
try encryptDecryptBufferOfLen(17,32)
}
func testEncryptDecrypt32BytesWithChunk16Bytes() throws {
try encryptDecryptBufferOfLen(32,32,16)
}
func testEncryptDecrypt64Bytes() throws {
try encryptDecryptBufferOfLen(64,32)
}
func testEncryptDecrypt65Bytes() throws {
try encryptDecryptBufferOfLen(65,32)
}
func testEncryptDecrypt192Bytes() throws {
try encryptDecryptBufferOfLen(192,32)
}
func testEncryptDecrypt1024Bytes_2() throws {
try encryptDecryptBufferOfLen(127,32)
}
func testEncryptDecrypt128Bytes() throws {
try encryptDecryptBufferOfLen(128,32)
}
func testEncryptVariousLenBuffers() throws {
for pow in 0...14 {
let sourceBufLen = 1<<pow
let sourceBuf = genBufferOfLen(sourceBufLen)
let key = genBufferOfLen(32)
let iv = genBufferOfLen(16)
let encryptedBuf = try encrypt(sourceBuf, sourceBufLen, key, iv)
let expected = ((sourceBufLen/16)+1)*16
XCTAssertEqual(expected, encryptedBuf.count)
XCTAssertNotEqual(sourceBuf, encryptedBuf)
}
}
func testEncryptDecryptVariousLenBuffers() throws {
for pow in 0...14 {
try encryptDecryptBufferOfLen(1<<pow)
}
}
func disabled_testPerformanceEncryptDecrypt1MBFile() throws {
self.measure {
try! encryptDecryptBufferOfLen(1<<20)
}
}
}
extension TwoFishStreamTests {
func encrypt(_ buffer: UnsafePointer<UInt8>,
_ len: Int,
_ key: [UInt8],
_ iv: [UInt8],
_ chunkSize: Int = TwoFishOutputStream.defaultChunkSize
) throws -> [UInt8] {
let dataOutputStream = BufferOutputStream()
try dataOutputStream.open()
let encryptingStream = TwoFishOutputStream(writingTo: dataOutputStream,
key: key,
iv: iv,
chunkSize: chunkSize)
try encryptingStream.open()
try encryptingStream.write(buffer, length: len)
try encryptingStream.close()
let resultData = dataOutputStream.buffer
try dataOutputStream.close()
return resultData
}
func decrypt(_ buffer: [UInt8],
_ key: [UInt8],
_ iv: [UInt8],
_ chunkSize: Int = TwoFishInputStream.defaultChunkSize
) throws -> [UInt8] {
let dataInputStream = BufferInputStream(withBuffer: buffer)
try dataInputStream.open()
let decryptingStream = TwoFishInputStream(readingFrom: dataInputStream,
key: key, iv: iv,
chunkSize: chunkSize)
try decryptingStream.open()
var decryptedBytes = [UInt8]()
let tmpBufferLen = 1<<16
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
while decryptingStream.hasBytesAvailable {
let readLen = try decryptingStream.read(&tmpBuffer, maxLength: tmpBufferLen)
decryptedBytes.append(contentsOf: tmpBuffer.prefix(readLen))
}
return decryptedBytes
}
func encryptDecryptBufferOfLen(_ bufLen: Int) throws {
for keyLen in 1...32 {
for chunkSize in [64,65,127,128,129,512,1023,1024] {
try encryptDecryptBufferOfLen(bufLen, keyLen, chunkSize)
}
}
}
func encryptDecryptBufferOfLen(_ bufLen: Int,
_ keyLen: Int,
_ chunkSize: Int = TwoFishOutputStream.defaultChunkSize
) throws {
let sourceBuf = genBufferOfLen(bufLen)
let key = genBufferOfLen(keyLen)
let iv = genBufferOfLen(16)
let encryptedBuf = try encrypt(sourceBuf, bufLen, key, iv, chunkSize)
let decryptedBuf = try decrypt(encryptedBuf, key, iv, chunkSize)
XCTAssertEqual(bufLen, decryptedBuf.count, "\(bufLen) \(chunkSize)")
XCTAssertEqual(sourceBuf, decryptedBuf, "\(bufLen) \(chunkSize)")
}
}
+34
View File
@@ -0,0 +1,34 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2023 Ihar Katkavets
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import XCTest
import StreamKit
final class UtilsTests: XCTestCase {
func testCalculatingMD5() {
let inFileURL = fileURL("1MB")!
XCTAssertEqual([0xc7,0x2d,0xa9,0x5c,0xaa,0xf6,0x25,0xdb,0xb0,0x21,0xd7,0x2f,0x8c,0x42,0x72,0x2e], md5(inFileURL))
}
}
+46
View File
@@ -0,0 +1,46 @@
# TwoFish Streams
Twofish is a 128-bit block cipher that accepts a variable-length `key` up to 256 bits.</br>
`key` can be any length up to 256 bits</br>
`iv` must be 128 bits length
```swift
//let key: [UInt8] = Array(repeating: 0, count: 9) // for 72 bits length key
//let key: [UInt8] = Array(repeating: 0, count: 24) // for 192 bits length key
//let key: [UInt8] = Array(repeating: 0, count: 31) // for 248 bits length key
let key: [UInt8] = Array(repeating: 0, count: 32) // 256 bits length key
let iv: [UInt8] = Array(repeating: 0, count: 16) // 128 bits length initilization vector
```
## Create output(encrypting) stream
```swift
let encryptingStream = TwoFishOutputStream(writingTo: anotherOutputStream,
key: key,
iv: iv,
chunkSize: chunkSize)
try encryptingStream.open()
try encryptingStream.write(buffer, length: len)
try encryptingStream.close()
```
## Create input(decrypting) stream
```swift
let decryptingStream = TwoFishInputStream(readingFrom: anotherInputStream,
key: key,
iv: iv,
chunkSize: chunkSize)
try decryptingStream.open()
var decryptedBytes = [UInt8]()
while decryptingStream.hasBytesAvailable {
let tmpBufferLen = 1<<16 // 65KB buffer
var tmpBuffer = Array<UInt8>(repeating: 0, count: tmpBufferLen)
let readLen = try decryptingStream.read(&tmpBuffer, maxLength: tmpBufferLen)
decryptedBytes.append(contentsOf: tmpBuffer.prefix(readLen))
}
let decryptedData = Data(decryptedBytes)
```
# The information taken from
https://www.schneier.com/wp-content/uploads/2016/02/paper-twofish-paper.pdf