Implement AES-CTR encryption

This commit is contained in:
Davide De Rosa 2018-09-20 10:58:24 +02:00
parent a430beb35f
commit 3ec4a7d292
6 changed files with 287 additions and 5 deletions

View File

@ -44,6 +44,10 @@
0E39BCE9214B2AB60035E9DE /* ControlPacket.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E39BCE6214B2AB60035E9DE /* ControlPacket.h */; };
0E39BCEA214B2AB60035E9DE /* ControlPacket.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E39BCE7214B2AB60035E9DE /* ControlPacket.m */; };
0E39BCEB214B2AB60035E9DE /* ControlPacket.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E39BCE7214B2AB60035E9DE /* ControlPacket.m */; };
0E3B15C72152B05E00984B17 /* CryptoCTR.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E3B15C52152B05E00984B17 /* CryptoCTR.h */; };
0E3B15C82152B05E00984B17 /* CryptoCTR.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E3B15C52152B05E00984B17 /* CryptoCTR.h */; };
0E3B15C92152B05E00984B17 /* CryptoCTR.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B15C62152B05E00984B17 /* CryptoCTR.m */; };
0E3B15CA2152B05E00984B17 /* CryptoCTR.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B15C62152B05E00984B17 /* CryptoCTR.m */; };
0E3E0F212108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */; };
0E3E0F222108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */; };
0E58F1302138AC2F00A49F27 /* DNSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58F12F2138AC2F00A49F27 /* DNSTests.swift */; };
@ -218,6 +222,8 @@
0E3251C51F95770D00C108D9 /* TunnelKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TunnelKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
0E39BCE6214B2AB60035E9DE /* ControlPacket.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ControlPacket.h; sourceTree = "<group>"; };
0E39BCE7214B2AB60035E9DE /* ControlPacket.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ControlPacket.m; sourceTree = "<group>"; };
0E3B15C52152B05E00984B17 /* CryptoCTR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoCTR.h; sourceTree = "<group>"; };
0E3B15C62152B05E00984B17 /* CryptoCTR.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CryptoCTR.m; sourceTree = "<group>"; };
0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+PushReply.swift"; sourceTree = "<group>"; };
0E58F12F2138AC2F00A49F27 /* DNSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSTests.swift; sourceTree = "<group>"; };
0E6479DD212EAC96008E6888 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -459,6 +465,8 @@
0EFEB4322006D3C800F81029 /* CryptoBox.m */,
0E07596D20EF79B400F38FD8 /* CryptoCBC.h */,
0E07595C20EF6D1400F38FD8 /* CryptoCBC.m */,
0E3B15C52152B05E00984B17 /* CryptoCTR.h */,
0E3B15C62152B05E00984B17 /* CryptoCTR.m */,
0E07596120EF733F00F38FD8 /* CryptoMacros.h */,
0EFEB4432006D3C800F81029 /* Data+Manipulation.swift */,
0EFEB4352006D3C800F81029 /* DataPath.h */,
@ -559,6 +567,7 @@
0EFEB4592006D3C800F81029 /* Allocation.h in Headers */,
0EFEB4582006D3C800F81029 /* MSS.h in Headers */,
0E245D6C2137F73600B012A2 /* CompressionFramingNative.h in Headers */,
0E3B15C72152B05E00984B17 /* CryptoCTR.h in Headers */,
0EFEB4602006D3C800F81029 /* DataPath.h in Headers */,
0E39BCE8214B2AB60035E9DE /* ControlPacket.h in Headers */,
0E07597E20F0060E00F38FD8 /* CryptoAEAD.h in Headers */,
@ -581,6 +590,7 @@
0EEC49E320B5F7F6008FEB91 /* DataPath.h in Headers */,
0EF5CF282141E183004FF1BD /* CompressionFramingNative.h in Headers */,
0EEC49E820B5F7F6008FEB91 /* ReplayProtector.h in Headers */,
0E3B15C82152B05E00984B17 /* CryptoCTR.h in Headers */,
0EEC49E920B5F7F6008FEB91 /* TLSBox.h in Headers */,
0E39BCE9214B2AB60035E9DE /* ControlPacket.h in Headers */,
0E07597F20F0060E00F38FD8 /* CryptoAEAD.h in Headers */,
@ -890,6 +900,7 @@
0EE3B3E421471C3A0027AB17 /* StaticKey.swift in Sources */,
0EFEB4622006D3C800F81029 /* SecureRandom.swift in Sources */,
0EFEB45D2006D3C800F81029 /* CryptoBox.m in Sources */,
0E3B15C92152B05E00984B17 /* CryptoCTR.m in Sources */,
0EBBF2FA2085061600E36B40 /* NETCPInterface.swift in Sources */,
0E0C2125212ED29D008AB282 /* SessionError.swift in Sources */,
0E12B2A821456C0200B4BAE9 /* ControlChannel.swift in Sources */,
@ -948,6 +959,7 @@
0EE3B3E521471C3A0027AB17 /* StaticKey.swift in Sources */,
0EBBF3012085196000E36B40 /* NWTCPConnectionState+Description.swift in Sources */,
0EFEB4962006D7F300F81029 /* ProtocolMacros.swift in Sources */,
0E3B15CA2152B05E00984B17 /* CryptoCTR.m in Sources */,
0EFEB48A2006D7C400F81029 /* TunnelKitProvider.swift in Sources */,
0E0C2126212ED29D008AB282 /* SessionError.swift in Sources */,
0E12B2A921456C0200B4BAE9 /* ControlChannel.swift in Sources */,

View File

@ -45,6 +45,7 @@
#import "CryptoCBC.h"
#import "CryptoAEAD.h"
#import "CryptoCTR.h"
@interface CryptoBox ()
@ -122,6 +123,11 @@
self.encrypter = gcm;
self.decrypter = gcm;
}
else if ([self.cipherAlgorithm hasSuffix:@"-ctr"]) {
CryptoCTR *ctr = [[CryptoCTR alloc] initWithCipherName:self.cipherAlgorithm digestName:self.digestAlgorithm];
self.encrypter = ctr;
self.decrypter = ctr;
}
// not supported
else {
if (error) {

View File

@ -113,11 +113,6 @@ const NSInteger CryptoCBCMaxHMACLength = 100;
self.digest = NULL;
}
- (int)extraLength
{
return 0;
}
- (NSInteger)encryptionCapacityWithLength:(NSInteger)length
{
return safe_crypto_capacity(length, self.digestLength + self.cipherIVLength);

View File

@ -0,0 +1,41 @@
//
// CryptoCTR.h
// TunnelKit
//
// Created by Davide De Rosa on 9/18/18.
// Copyright (c) 2018 Davide De Rosa. All rights reserved.
//
// https://github.com/keeshux
//
// This file is part of TunnelKit.
//
// TunnelKit is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// TunnelKit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
#import <Foundation/Foundation.h>
#import "Crypto.h"
#import "DataPathCrypto.h"
NS_ASSUME_NONNULL_BEGIN
extern const NSInteger CryptoCTRADLength;
@interface CryptoCTR : NSObject <Encrypter, Decrypter>
- (instancetype)initWithCipherName:(nullable NSString *)cipherName digestName:(NSString *)digestName;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,211 @@
//
// CryptoCTR.m
// TunnelKit
//
// Created by Davide De Rosa on 9/18/18.
// Copyright (c) 2018 Davide De Rosa. All rights reserved.
//
// https://github.com/keeshux
//
// This file is part of TunnelKit.
//
// TunnelKit is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// TunnelKit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
#import <openssl/evp.h>
#import <openssl/hmac.h>
#import <openssl/rand.h>
#import "CryptoCTR.h"
#import "CryptoMacros.h"
#import "PacketMacros.h"
#import "Allocation.h"
#import "Errors.h"
const NSInteger CryptoCTRADLength = PacketOpcodeLength + PacketSessionIdLength + PacketReplayIdLength + PacketReplayTimestampLength;
const NSInteger CryptoCTRTagLength = 32;
@interface CryptoCTR ()
@property (nonatomic, unsafe_unretained) const EVP_CIPHER *cipher;
@property (nonatomic, unsafe_unretained) const EVP_MD *digest;
@property (nonatomic, assign) int cipherKeyLength;
@property (nonatomic, assign) int cipherIVLength;
@property (nonatomic, assign) int hmacKeyLength;
@property (nonatomic, assign) int digestLength;
@property (nonatomic, unsafe_unretained) EVP_CIPHER_CTX *cipherCtxEnc;
@property (nonatomic, unsafe_unretained) EVP_CIPHER_CTX *cipherCtxDec;
@property (nonatomic, unsafe_unretained) HMAC_CTX *hmacCtxEnc;
@property (nonatomic, unsafe_unretained) HMAC_CTX *hmacCtxDec;
@property (nonatomic, unsafe_unretained) uint8_t *bufferDecHMAC;
@end
@implementation CryptoCTR
- (instancetype)initWithCipherName:(NSString *)cipherName digestName:(NSString *)digestName
{
NSParameterAssert(cipherName && [[cipherName uppercaseString] hasSuffix:@"CTR"]);
NSParameterAssert(digestName);
self = [super init];
if (self) {
self.cipher = EVP_get_cipherbyname([cipherName cStringUsingEncoding:NSASCIIStringEncoding]);
NSAssert(self.cipher, @"Unknown cipher '%@'", cipherName);
self.digest = EVP_get_digestbyname([digestName cStringUsingEncoding:NSASCIIStringEncoding]);
NSAssert(self.digest, @"Unknown digest '%@'", digestName);
self.cipherKeyLength = EVP_CIPHER_key_length(self.cipher);
self.cipherIVLength = EVP_CIPHER_iv_length(self.cipher);
// as seen in OpenVPN's crypto_openssl.c:md_kt_size()
self.hmacKeyLength = EVP_MD_size(self.digest);
self.digestLength = EVP_MD_size(self.digest);
NSAssert(self.digestLength == CryptoCTRTagLength, @"Expected digest size to be tag length (%ld)", CryptoCTRTagLength);
self.cipherCtxEnc = EVP_CIPHER_CTX_new();
self.cipherCtxDec = EVP_CIPHER_CTX_new();
self.hmacCtxEnc = HMAC_CTX_new();
self.hmacCtxDec = HMAC_CTX_new();
self.bufferDecHMAC = allocate_safely(self.digestLength);
}
return self;
}
- (void)dealloc
{
EVP_CIPHER_CTX_free(self.cipherCtxEnc);
EVP_CIPHER_CTX_free(self.cipherCtxDec);
HMAC_CTX_free(self.hmacCtxEnc);
HMAC_CTX_free(self.hmacCtxDec);
bzero(self.bufferDecHMAC, self.digestLength);
free(self.bufferDecHMAC);
self.cipher = NULL;
self.digest = NULL;
}
- (NSInteger)encryptionCapacityWithLength:(NSInteger)length
{
return safe_crypto_capacity(length, PacketOpcodeLength + PacketSessionIdLength + PacketReplayIdLength + PacketReplayTimestampLength + CryptoCTRTagLength);
}
#pragma mark Encrypter
- (void)configureEncryptionWithCipherKey:(ZeroingData *)cipherKey hmacKey:(ZeroingData *)hmacKey
{
NSParameterAssert(hmacKey);
NSParameterAssert(hmacKey.count >= self.hmacKeyLength);
NSParameterAssert(cipherKey.count >= self.cipherKeyLength);
EVP_CIPHER_CTX_reset(self.cipherCtxEnc);
EVP_CipherInit(self.cipherCtxEnc, self.cipher, cipherKey.bytes, NULL, 1);
HMAC_CTX_reset(self.hmacCtxEnc);
HMAC_Init_ex(self.hmacCtxEnc, hmacKey.bytes, self.hmacKeyLength, self.digest, NULL);
}
- (BOOL)encryptBytes:(const uint8_t *)bytes length:(NSInteger)length dest:(uint8_t *)dest destLength:(NSInteger *)destLength flags:(const CryptoFlags * _Nullable)flags error:(NSError * _Nullable __autoreleasing * _Nullable)error
{
NSParameterAssert(flags);
uint8_t *outEncrypted = dest + CryptoCTRTagLength;
int l1 = 0, l2 = 0;
unsigned int l3 = 0;
int code = 1;
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Init_ex(self.hmacCtxEnc, NULL, 0, NULL, NULL);
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Update(self.hmacCtxEnc, flags->ad, flags->adLength);
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Update(self.hmacCtxEnc, bytes, length);
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Final(self.hmacCtxEnc, dest, &l3);
NSAssert(l3 == CryptoCTRTagLength, @"Incorrect digest size");
TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherInit(self.cipherCtxEnc, NULL, NULL, dest, -1);
TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherUpdate(self.cipherCtxEnc, outEncrypted, &l1, bytes, (int)length);
TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal(self.cipherCtxEnc, outEncrypted + l1, &l2);
*destLength = CryptoCTRTagLength + l1 + l2;
TUNNEL_CRYPTO_RETURN_STATUS(code)
}
- (id<DataPathEncrypter>)dataPathEncrypter
{
[NSException raise:NSInvalidArgumentException format:@"DataPathEncryption not supported"];
return nil;
}
#pragma mark Decrypter
- (void)configureDecryptionWithCipherKey:(ZeroingData *)cipherKey hmacKey:(ZeroingData *)hmacKey
{
NSParameterAssert(hmacKey);
NSParameterAssert(hmacKey.count >= self.hmacKeyLength);
NSParameterAssert(cipherKey.count >= self.cipherKeyLength);
EVP_CIPHER_CTX_reset(self.cipherCtxDec);
EVP_CipherInit(self.cipherCtxDec, self.cipher, cipherKey.bytes, NULL, 0);
HMAC_CTX_reset(self.hmacCtxDec);
HMAC_Init_ex(self.hmacCtxDec, hmacKey.bytes, self.hmacKeyLength, self.digest, NULL);
}
- (BOOL)decryptBytes:(const uint8_t *)bytes length:(NSInteger)length dest:(uint8_t *)dest destLength:(NSInteger *)destLength flags:(const CryptoFlags * _Nullable)flags error:(NSError * _Nullable __autoreleasing * _Nullable)error
{
NSParameterAssert(flags);
NSAssert(self.cipher, @"No cipher provided");
const uint8_t *iv = bytes;
const uint8_t *encrypted = bytes + CryptoCTRTagLength;
int l1 = 0, l2 = 0;
unsigned int l3 = 0;
int code = 1;
TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherInit(self.cipherCtxDec, NULL, NULL, iv, -1);
TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherUpdate(self.cipherCtxDec, dest, &l1, encrypted, (int)length - CryptoCTRTagLength);
TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal(self.cipherCtxDec, dest + l1, &l2);
*destLength = l1 + l2;
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Init_ex(self.hmacCtxDec, NULL, 0, NULL, NULL);
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Update(self.hmacCtxDec, flags->ad, flags->adLength);
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Update(self.hmacCtxDec, dest, *destLength);
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Final(self.hmacCtxDec, self.bufferDecHMAC, &l3);
NSAssert(l3 == CryptoCTRTagLength, @"Incorrect digest size");
if (TUNNEL_CRYPTO_SUCCESS(code) && CRYPTO_memcmp(self.bufferDecHMAC, bytes, CryptoCTRTagLength) != 0) {
if (error) {
*error = TunnelKitErrorWithCode(TunnelKitErrorCodeCryptoBoxHMAC);
}
return NO;
}
TUNNEL_CRYPTO_RETURN_STATUS(code)
}
- (BOOL)verifyBytes:(const uint8_t *)bytes length:(NSInteger)length flags:(const CryptoFlags * _Nullable)flags error:(NSError * _Nullable __autoreleasing * _Nullable)error
{
[NSException raise:NSInvalidArgumentException format:@"Verification not supported"];
return NO;
}
- (id<DataPathDecrypter>)dataPathDecrypter
{
[NSException raise:NSInvalidArgumentException format:@"DataPathEncryption not supported"];
return nil;
}
@end

View File

@ -96,6 +96,23 @@ class EncryptionTests: XCTestCase {
XCTAssertEqual(md5, exp)
}
func testCTR() {
let (client, server) = clientServer("aes-256-ctr", "sha256")
let original = Data(hex: "0000000000")
let ad: [UInt8] = [UInt8](Data(hex: "38afa8f1162096081e000000015ba35373"))
var flags = CryptoFlags(iv: nil, ivLength: 0, ad: ad, adLength: ad.count)
// let expEncrypted = Data(hex: "319bb8e7f8f7930cc4625079dd32a6ef9540c2fc001c53f909f712037ae9818af840b88714")
let encrypted = try! client.encrypter().encryptData(original, flags: &flags)
print(encrypted.toHex())
// XCTAssertEqual(encrypted, expEncrypted)
let decrypted = try! server.decrypter().decryptData(encrypted, flags: &flags)
print(decrypted.toHex())
XCTAssertEqual(decrypted, original)
}
private func clientServer(_ c: String?, _ d: String?) -> (CryptoBox, CryptoBox) {
let client = CryptoBox(cipherAlgorithm: c, digestAlgorithm: d)
let server = CryptoBox(cipherAlgorithm: c, digestAlgorithm: d)