tunnelkit/PIATunnel/Sources/Core/Authenticator.swift

151 lines
4.5 KiB
Swift

//
// Authenticator.swift
// PIATunnel
//
// Created by Davide De Rosa on 2/9/17.
// Copyright © 2018 London Trust Media. All rights reserved.
//
import Foundation
import SwiftyBeaver
import __PIATunnelNative
private let log = SwiftyBeaver.self
fileprivate extension ZeroingData {
fileprivate func appendSized(_ buf: ZeroingData) {
append(Z(UInt16(buf.count).bigEndian))
append(buf)
}
}
class Authenticator {
private var controlBuffer: ZeroingData
private(set) var preMaster: ZeroingData
private(set) var random1: ZeroingData
private(set) var random2: ZeroingData
private(set) var serverRandom1: ZeroingData?
private(set) var serverRandom2: ZeroingData?
let username: ZeroingData
let password: ZeroingData
init(_ username: String, _ password: String) throws {
preMaster = try SecureRandom.safeData(length: CoreConfiguration.preMasterLength)
random1 = try SecureRandom.safeData(length: CoreConfiguration.randomLength)
random2 = try SecureRandom.safeData(length: CoreConfiguration.randomLength)
// XXX: not 100% secure, can't erase input username/password
self.username = Z(username, nullTerminated: true)
self.password = Z(password, nullTerminated: true)
controlBuffer = Z()
}
// MARK: Authentication request
// Ruby: on_tls_connect
func putAuth(into: TLSBox) throws {
let raw = Z(ProtocolMacros.tlsPrefix)
// local keys
raw.append(preMaster)
raw.append(random1)
raw.append(random2)
// opts
raw.appendSized(Z(UInt8(0)))
// credentials
raw.appendSized(username)
raw.appendSized(password)
// peer info
raw.appendSized(Z(CoreConfiguration.peerInfo))
if CoreConfiguration.logsSensitiveData {
log.debug("TLS.auth: Put plaintext (\(raw.count) bytes): \(raw.toHex())")
} else {
log.debug("TLS.auth: Put plaintext (\(raw.count) bytes)")
}
try into.putRawPlainText(raw.bytes, length: raw.count)
}
// MARK: Server replies
func appendControlData(_ data: ZeroingData) {
controlBuffer.append(data)
}
func parseAuthReply() throws -> Bool {
let prefixLength = ProtocolMacros.tlsPrefix.count
// TLS prefix + random (x2) + opts length [+ opts]
guard (controlBuffer.count >= prefixLength + 2 * CoreConfiguration.randomLength + 2) else {
return false
}
let prefix = controlBuffer.withOffset(0, count: prefixLength)
guard prefix.isEqual(to: ProtocolMacros.tlsPrefix) else {
throw SessionError.wrongControlDataPrefix
}
var offset = ProtocolMacros.tlsPrefix.count
let serverRandom1 = controlBuffer.withOffset(offset, count: CoreConfiguration.randomLength)
offset += CoreConfiguration.randomLength
let serverRandom2 = controlBuffer.withOffset(offset, count: CoreConfiguration.randomLength)
offset += CoreConfiguration.randomLength
let serverOptsLength = Int(controlBuffer.networkUInt16Value(fromOffset: offset))
offset += 2
guard controlBuffer.count >= offset + serverOptsLength else {
return false
}
let serverOpts = controlBuffer.withOffset(offset, count: serverOptsLength)
offset += serverOptsLength
if CoreConfiguration.logsSensitiveData {
log.debug("TLS.auth: Parsed server random: [\(serverRandom1.toHex()), \(serverRandom2.toHex())]")
} else {
log.debug("TLS.auth: Parsed server random")
}
if let serverOptsString = serverOpts.nullTerminatedString(fromOffset: 0) {
log.debug("TLS.auth: Parsed server opts: \"\(serverOptsString)\"")
}
self.serverRandom1 = serverRandom1
self.serverRandom2 = serverRandom2
controlBuffer.remove(untilOffset: offset)
return true
}
func parseMessages() -> [String] {
var messages = [String]()
var offset = 0
while true {
guard let msg = controlBuffer.nullTerminatedString(fromOffset: offset) else {
break
}
messages.append(msg)
offset += msg.count + 1
}
controlBuffer.remove(untilOffset: offset)
return messages
}
}