108 lines
2.5 KiB
Swift
108 lines
2.5 KiB
Swift
import Foundation
|
|
import CryptoKit
|
|
import NIOCore
|
|
import Vapor
|
|
|
|
struct Message: Equatable, Hashable {
|
|
|
|
static var length: Int {
|
|
SHA256Digest.byteCount + Content.length
|
|
}
|
|
|
|
struct Content: Equatable, Hashable {
|
|
|
|
let time: UInt32
|
|
|
|
let id: UInt32
|
|
|
|
init(time: UInt32, id: UInt32) {
|
|
self.time = time
|
|
self.id = id
|
|
}
|
|
|
|
init(decodeFrom data: Data) {
|
|
self.time = UInt32(data: data[data.startIndex..<data.startIndex+4])
|
|
self.id = UInt32(data: data[data.startIndex+4..<data.startIndex+8])
|
|
}
|
|
|
|
static var length: Int {
|
|
MemoryLayout<UInt32>.size * 2
|
|
}
|
|
|
|
func authenticate(using key: SymmetricKey) -> Message {
|
|
let mac = HMAC<SHA256>.authenticationCode(for: encoded, using: key)
|
|
return .init(mac: Data(mac.map { $0 }), content: self)
|
|
}
|
|
|
|
func authenticateAndSerialize(using key: SymmetricKey) -> Data {
|
|
let encoded = self.encoded
|
|
let mac = HMAC<SHA256>.authenticationCode(for: encoded, using: key)
|
|
return Data(mac.map { $0 }) + encoded
|
|
}
|
|
|
|
var encoded: Data {
|
|
time.encoded + id.encoded
|
|
}
|
|
|
|
var bytes: [UInt8] {
|
|
time.bytes + id.bytes
|
|
}
|
|
}
|
|
|
|
let mac: Data
|
|
|
|
let content: Content
|
|
|
|
init(mac: Data, content: Content) {
|
|
self.mac = mac
|
|
self.content = content
|
|
}
|
|
|
|
init?(decodeFrom buffer: ByteBuffer) {
|
|
guard let data = buffer.getData(at: 0, length: Message.length) else {
|
|
return nil
|
|
}
|
|
self.init(decodeFrom: data)
|
|
}
|
|
|
|
private init(decodeFrom data: Data) {
|
|
let count = SHA256Digest.byteCount
|
|
self.mac = data[data.startIndex..<data.startIndex+count]
|
|
self.content = .init(decodeFrom: data.advanced(by: count))
|
|
}
|
|
|
|
func isValid(using key: SymmetricKey) -> Bool {
|
|
HMAC<SHA256>.isValidAuthenticationCode(mac, authenticating: content.encoded, using: key)
|
|
}
|
|
|
|
var encoded: Data {
|
|
mac + content.encoded
|
|
}
|
|
|
|
var bytes: [UInt8] {
|
|
Array(mac) + content.bytes
|
|
}
|
|
}
|
|
|
|
extension UInt32 {
|
|
|
|
init(data: Data) {
|
|
self = data
|
|
.reversed()
|
|
.enumerated()
|
|
.map { UInt32($0.element) << ($0.offset * 8) }
|
|
.reduce(0, +)
|
|
|
|
}
|
|
|
|
var encoded: Data {
|
|
.init(bytes)
|
|
}
|
|
|
|
var bytes: [UInt8] {
|
|
(0..<4).reversed().map {
|
|
UInt8((self >> ($0*8)) & 0xFF)
|
|
}
|
|
}
|
|
}
|