Implement and test crypt serializer
This commit is contained in:
parent
3ec4a7d292
commit
55e0aa5c5a
|
@ -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 */,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
#import "Allocation.h"
|
||||
#import "Errors.h"
|
||||
|
||||
const NSInteger CryptoCTRADLength = PacketOpcodeLength + PacketSessionIdLength + PacketReplayIdLength + PacketReplayTimestampLength;
|
||||
const NSInteger CryptoCTRTagLength = 32;
|
||||
|
||||
@interface CryptoCTR ()
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue