Implement and test crypt serializer

This commit is contained in:
Davide De Rosa 2018-10-19 16:52:37 +02:00
parent 3ec4a7d292
commit 55e0aa5c5a
7 changed files with 141 additions and 4 deletions

View File

@ -50,6 +50,7 @@
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 */; };
0E50D57521634E0A00FC87A8 /* ControlChannelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */; };
0E58F1302138AC2F00A49F27 /* DNSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58F12F2138AC2F00A49F27 /* DNSTests.swift */; };
0E749F622178911D00BB2701 /* pia-2048.pem in Resources */ = {isa = PBXBuildFile; fileRef = 0E749F612178911C00BB2701 /* pia-2048.pem */; };
0E749F5F2178885500BB2701 /* SessionProxy+PIA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E749F5E2178885500BB2701 /* SessionProxy+PIA.swift */; };
@ -225,6 +226,7 @@
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>"; };
0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlChannelTests.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>"; };
0E6479E0212EACD6008E6888 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -345,6 +347,7 @@
children = (
0E11089E1F77B9E800A92462 /* Info.plist */,
0E85A259202CC5AE0059E9F9 /* AppExtensionTests.swift */,
0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */,
0EB2B45420F0BB53004233D7 /* DataManipulationTests.swift */,
0EE7A7A020F664AB00B42E6A /* DataPathEncryptionTests.swift */,
0EB2B46020F0C0A4004233D7 /* DataPathPerformanceTests.swift */,
@ -859,6 +862,7 @@
0EB2B45720F0BD16004233D7 /* RandomTests.swift in Sources */,
0EB2B45920F0BD9A004233D7 /* LinkTests.swift in Sources */,
0EB2B45520F0BB53004233D7 /* DataManipulationTests.swift in Sources */,
0E50D57521634E0A00FC87A8 /* ControlChannelTests.swift in Sources */,
0EB2B45320F0BB44004233D7 /* EncryptionTests.swift in Sources */,
0EB2B45B20F0BE4C004233D7 /* TestUtils.swift in Sources */,
0E58F1302138AC2F00A49F27 /* DNSTests.swift in Sources */,

View File

@ -68,6 +68,10 @@ class ControlChannel {
self.init(serializer: try AuthSerializer(withKey: key, digest: digest))
}
convenience init(withCryptKey key: StaticKey) throws {
self.init(serializer: try CryptSerializer(withKey: key))
}
private init(serializer: ControlChannelSerializer) {
self.serializer = serializer
sessionId = nil

View File

@ -203,3 +203,80 @@ extension ControlChannel {
}
}
}
extension ControlChannel {
class CryptSerializer: ControlChannelSerializer {
private let encrypter: Encrypter
private let decrypter: Decrypter
private let headerLength: Int
private var adLength: Int
private let tagLength: Int
private var currentReplayId: BidirectionalState<UInt32>
private let plain: PlainSerializer
init(withKey key: StaticKey) throws {
let crypto = CryptoBox(cipherAlgorithm: "AES-256-CTR", digestAlgorithm: "SHA256")
try crypto.configure(
withCipherEncKey: key.cipherEncryptKey,
cipherDecKey: key.cipherDecryptKey,
hmacEncKey: key.hmacSendKey,
hmacDecKey: key.hmacReceiveKey
)
encrypter = crypto.encrypter()
decrypter = crypto.decrypter()
headerLength = PacketOpcodeLength + PacketSessionIdLength
adLength = headerLength + PacketReplayIdLength + PacketReplayTimestampLength
tagLength = 32
currentReplayId = BidirectionalState(withResetValue: 1)
plain = PlainSerializer()
}
func reset() {
currentReplayId.reset()
}
func serialize(packet: ControlPacket) throws -> Data {
return try serialize(packet: packet, timestamp: UInt32(Date().timeIntervalSince1970))
}
func serialize(packet: ControlPacket, timestamp: UInt32) throws -> Data {
let data = try packet.serialized(with: encrypter, replayId: currentReplayId.outbound, timestamp: timestamp, adLength: adLength)
currentReplayId.outbound += 1
return data
}
// XXX: start/end are ignored, parses whole packet
func deserialize(data packet: Data, start: Int, end: Int?) throws -> ControlPacket {
let end = end ?? packet.count
// data starts with (ad=(header + sessionId + replayId) + tag)
guard end >= start + adLength + tagLength else {
throw ControlChannelError("Missing AD+TAG")
}
let encryptedCount = packet.count - adLength
var decryptedPacket = Data(count: decrypter.encryptionCapacity(withLength: encryptedCount))
var decryptedCount = 0
try packet.withUnsafeBytes { (src: UnsafePointer<UInt8>) in
var flags = CryptoFlags(iv: nil, ivLength: 0, ad: src, adLength: adLength)
try decryptedPacket.withUnsafeMutableBytes { (dest: UnsafeMutablePointer<UInt8>) in
try decrypter.decryptBytes(src + flags.adLength, length: encryptedCount, dest: dest + headerLength, destLength: &decryptedCount, flags: &flags)
memcpy(dest, src, headerLength)
}
}
decryptedPacket.count = headerLength + decryptedCount
// TODO: validate replay packet id
return try plain.deserialize(data: decryptedPacket, start: 0, end: nil)
}
}
}

View File

@ -62,10 +62,14 @@ NS_ASSUME_NONNULL_BEGIN
@interface ControlPacket (Authentication)
//- (NSInteger)capacityWithAuthenticator:(id<Encrypter>)auth;
//- (BOOL)serializeTo:(uint8_t *)to authenticatingWith:(id<Encrypter>)auth replayId:(uint32_t)replayId timestamp:(uint32_t)timestamp error:(NSError * _Nullable __autoreleasing *)error;
- (nullable NSData *)serializedWithAuthenticator:(id<Encrypter>)auth replayId:(uint32_t)replayId timestamp:(uint32_t)timestamp error:(NSError * _Nullable __autoreleasing *)error;
@end
@interface ControlPacket (Encryption)
- (nullable NSData *)serializedWithEncrypter:(id<Encrypter>)encrypter replayId:(uint32_t)replayId timestamp:(uint32_t)timestamp adLength:(NSInteger)adLength error:(NSError * _Nullable __autoreleasing *)error;
@end
NS_ASSUME_NONNULL_END

View File

@ -176,3 +176,52 @@
}
@end
@implementation ControlPacket (Encryption)
- (NSInteger)capacityWithEncrypter:(id<Encrypter>)encrypter
{
return PacketOpcodeLength + PacketSessionIdLength + PacketReplayIdLength + PacketReplayTimestampLength + [encrypter encryptionCapacityWithLength:self.capacity];
}
- (BOOL)serializeTo:(uint8_t *)to encryptingWith:(nonnull id<Encrypter>)encrypter replayId:(uint32_t)replayId timestamp:(uint32_t)timestamp length:(NSInteger *)length adLength:(NSInteger)adLength error:(NSError *__autoreleasing _Nullable * _Nullable)error
{
uint8_t *ptr;
ptr = to;
ptr += PacketHeaderSet(to, self.code, self.key, self.sessionId.bytes);
*(uint32_t *)ptr = CFSwapInt32HostToBig(replayId);
ptr += PacketReplayIdLength;
*(uint32_t *)ptr = CFSwapInt32HostToBig(timestamp);
ptr += PacketReplayTimestampLength;
NSAssert2(ptr - to == adLength, @"Incorrect AD bytes (%ld != %ld)", ptr - to, (long)adLength);
NSMutableData *msg = [[NSMutableData alloc] initWithLength:self.rawCapacity];
ptr = msg.mutableBytes;
ptr += [self rawSerializeTo:ptr];
CryptoFlags flags;
flags.ad = to;
flags.adLength = adLength;
NSInteger encryptedMsgLength;
if (![encrypter encryptBytes:msg.bytes length:msg.length dest:(to + adLength) destLength:&encryptedMsgLength flags:&flags error:error]) {
return NO;
}
*length = adLength + encryptedMsgLength;
return YES;
}
- (NSData *)serializedWithEncrypter:(id<Encrypter>)encrypter replayId:(uint32_t)replayId timestamp:(uint32_t)timestamp adLength:(NSInteger)adLength error:(NSError *__autoreleasing _Nullable * _Nullable)error
{
NSMutableData *data = [[NSMutableData alloc] initWithLength:[self capacityWithEncrypter:encrypter]];
NSInteger length;
if (![self serializeTo:data.mutableBytes encryptingWith:encrypter replayId:replayId timestamp:timestamp length:&length adLength:adLength error:error]) {
return nil;
}
data.length = length;
return data;
}
@end

View File

@ -33,7 +33,6 @@
#import "Allocation.h"
#import "Errors.h"
const NSInteger CryptoCTRADLength = PacketOpcodeLength + PacketSessionIdLength + PacketReplayIdLength + PacketReplayTimestampLength;
const NSInteger CryptoCTRTagLength = 32;
@interface CryptoCTR ()

View File

@ -58,7 +58,7 @@ class ControlChannelTests: XCTestCase {
let data = hmac + subject
print(data.toHex())
XCTAssertNoThrow(try server.decrypter().verifyData(data, extra: nil))
XCTAssertNoThrow(try server.decrypter().verifyData(data, flags: nil))
}
// 38 // HARD_RESET