151 lines
4.5 KiB
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
|
|
}
|
|
}
|