wip
This commit is contained in:
parent
b3b3b3aef7
commit
0a57e0648f
|
@ -8,4 +8,5 @@ enum AppError: Error {
|
||||||
case invalidContentType
|
case invalidContentType
|
||||||
case invalidLocalName
|
case invalidLocalName
|
||||||
case featureNotSupported
|
case featureNotSupported
|
||||||
|
case securityError
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,20 +109,32 @@ extension Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
func uploadFile(_ localURL: URL) async throws -> String {
|
func uploadFile(_ localURL: URL) async throws -> String {
|
||||||
|
// get data from file
|
||||||
guard let data = try? Data(contentsOf: localURL) else {
|
guard let data = try? Data(contentsOf: localURL) else {
|
||||||
throw AppError.noData
|
throw AppError.noData
|
||||||
}
|
}
|
||||||
let httpModule = connection.module(HttpFileUploadModule.self)
|
|
||||||
|
|
||||||
|
// encode data with AES_GSM
|
||||||
|
guard let iv = try? AESGSMEngine.generateIV(), let key = try? AESGSMEngine.generateKey() else {
|
||||||
|
throw AppError.securityError
|
||||||
|
}
|
||||||
|
var encodedData = Data()
|
||||||
|
var tag = Data()
|
||||||
|
guard AESGSMEngine.shared.encrypt(iv: iv, key: key, message: data, output: &encodedData, tag: &tag) else {
|
||||||
|
throw AppError.securityError
|
||||||
|
}
|
||||||
|
|
||||||
|
// upload
|
||||||
|
let httpModule = connection.module(HttpFileUploadModule.self)
|
||||||
let components = try await httpModule.findHttpUploadComponents()
|
let components = try await httpModule.findHttpUploadComponents()
|
||||||
guard let component = components.first(where: { $0.maxSize > data.count }) else {
|
guard let component = components.first(where: { $0.maxSize > encodedData.count }) else {
|
||||||
throw AppError.fileTooBig
|
throw AppError.fileTooBig
|
||||||
}
|
}
|
||||||
|
|
||||||
let slot = try await httpModule.requestUploadSlot(
|
let slot = try await httpModule.requestUploadSlot(
|
||||||
componentJid: component.jid,
|
componentJid: component.jid,
|
||||||
filename: localURL.lastPathComponent,
|
filename: localURL.lastPathComponent,
|
||||||
size: data.count,
|
size: encodedData.count,
|
||||||
contentType: localURL.mimeType
|
contentType: localURL.mimeType
|
||||||
)
|
)
|
||||||
var request = URLRequest(url: slot.putUri)
|
var request = URLRequest(url: slot.putUri)
|
||||||
|
@ -130,13 +142,21 @@ extension Client {
|
||||||
request.addValue(value, forHTTPHeaderField: key)
|
request.addValue(value, forHTTPHeaderField: key)
|
||||||
}
|
}
|
||||||
request.httpMethod = "PUT"
|
request.httpMethod = "PUT"
|
||||||
request.httpBody = data
|
request.httpBody = encodedData
|
||||||
request.addValue(String(data.count), forHTTPHeaderField: "Content-Length")
|
request.addValue(String(encodedData.count), forHTTPHeaderField: "Content-Length")
|
||||||
request.addValue(localURL.mimeType, forHTTPHeaderField: "Content-Type")
|
request.addValue(localURL.mimeType, forHTTPHeaderField: "Content-Type")
|
||||||
let (_, response) = try await URLSession.shared.data(for: request)
|
let (_, response) = try await URLSession.shared.data(for: request)
|
||||||
switch response {
|
switch response {
|
||||||
case let httpResponse as HTTPURLResponse where httpResponse.statusCode == 201:
|
case let httpResponse as HTTPURLResponse where httpResponse.statusCode == 201:
|
||||||
return slot.getUri.absoluteString
|
guard var parts = URLComponents(url: slot.getUri, resolvingAgainstBaseURL: true) else {
|
||||||
|
throw URLError(.badServerResponse)
|
||||||
|
}
|
||||||
|
parts.scheme = "aesgcm"
|
||||||
|
parts.fragment = (iv + key).map { String(format: "%02x", $0) }.joined()
|
||||||
|
guard let shareUrl = parts.url else {
|
||||||
|
throw URLError(.badServerResponse)
|
||||||
|
}
|
||||||
|
return shareUrl.absoluteString
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw URLError(.badServerResponse)
|
throw URLError(.badServerResponse)
|
||||||
|
@ -169,6 +189,7 @@ private extension Client {
|
||||||
switch error {
|
switch error {
|
||||||
case .noSession:
|
case .noSession:
|
||||||
errorMessage = NSLocalizedString("There is no trusted device to send message to", comment: "message encryption failure")
|
errorMessage = NSLocalizedString("There is no trusted device to send message to", comment: "message encryption failure")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,8 @@ final class AESGSMEngine: AES_GCM_Engine {
|
||||||
let symmetricKey = SymmetricKey(data: key)
|
let symmetricKey = SymmetricKey(data: key)
|
||||||
let sealedBox = try AES.GCM.seal(message, using: symmetricKey, nonce: AES.GCM.Nonce(data: iv))
|
let sealedBox = try AES.GCM.seal(message, using: symmetricKey, nonce: AES.GCM.Nonce(data: iv))
|
||||||
|
|
||||||
if let output = output {
|
if let output = output, let data = sealedBox.combined {
|
||||||
output.pointee = sealedBox.ciphertext
|
output.pointee = data
|
||||||
}
|
}
|
||||||
if let tag = tag {
|
if let tag = tag {
|
||||||
tag.pointee = sealedBox.tag
|
tag.pointee = sealedBox.tag
|
||||||
|
@ -44,4 +44,18 @@ final class AESGSMEngine: AES_GCM_Engine {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func generateIV() throws -> Data {
|
||||||
|
var bytes = [Int8](repeating: 0, count: 12)
|
||||||
|
let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
|
||||||
|
if status != errSecSuccess {
|
||||||
|
throw AppError.securityError
|
||||||
|
}
|
||||||
|
return Data(bytes: bytes, count: bytes.count)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func generateKey() throws -> Data {
|
||||||
|
let key = SymmetricKey(size: .bits256)
|
||||||
|
return key.withUnsafeBytes { Data($0) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue