Skip to content

Commit

Permalink
Expand crypto module, improve fetch/request
Browse files Browse the repository at this point in the history
  • Loading branch information
theolampert committed Nov 3, 2023
1 parent ae20f78 commit aabbce5
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 15 deletions.
62 changes: 62 additions & 0 deletions Sources/ECMASwift/API/Crypto/Crypto.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,69 @@ import CommonCrypto

// https://developer.mozilla.org/en-US/docs/Web/API/Crypto

@objc protocol SubtleCryptoExports: JSExport {
func digest(_ algorithm: String, _ data: [UInt8]) -> [UInt8]?
func encrypt(_ algorithm: String, _ key: [UInt8], _ iv: [UInt8], _ data: [UInt8]) -> [UInt8]?
func decrypt(_ algorithm: String, _ key: [UInt8], _ iv: [UInt8], _ data: [UInt8]) -> [UInt8]?
}

@objc class SubtleCrypto: NSObject, SubtleCryptoExports {
func digest(_ algorithm: String, _ data: [UInt8]) -> [UInt8]? {
guard algorithm == "SHA-256" else { return nil }
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
_ = data.withUnsafeBytes {
CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash)
}
return hash
}

func encrypt(_ algorithm: String, _ key: [UInt8], _ iv: [UInt8], _ data: [UInt8]) -> [UInt8]? {
guard algorithm == "AES-GCM", key.count == kCCKeySizeAES128 else { return nil }
var buffer = [UInt8](repeating: 0, count: data.count + kCCBlockSizeAES128)
var numBytesEncrypted: size_t = 0

let cryptStatus = CCCrypt(CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES),
CCOptions(kCCOptionPKCS7Padding),
key, key.count,
iv,
data, data.count,
&buffer, buffer.count,
&numBytesEncrypted)

if cryptStatus == kCCSuccess {
return Array(buffer.prefix(numBytesEncrypted))
}
return nil
}

func decrypt(_ algorithm: String, _ key: [UInt8], _ iv: [UInt8], _ data: [UInt8]) -> [UInt8]? {
guard algorithm == "AES-GCM", key.count == kCCKeySizeAES128 else { return nil }
var buffer = [UInt8](repeating: 0, count: data.count + kCCBlockSizeAES128)
var numBytesDecrypted: size_t = 0

let cryptStatus = CCCrypt(CCOperation(kCCDecrypt),
CCAlgorithm(kCCAlgorithmAES),
CCOptions(kCCOptionPKCS7Padding),
key, key.count,
iv,
data, data.count,
&buffer, buffer.count,
&numBytesDecrypted)

if cryptStatus == kCCSuccess {
return Array(buffer.prefix(numBytesDecrypted))
}
return nil
}
}


@objc protocol CryptoExports: JSExport {
func getRandomValues(_ array: [UInt]) -> [UInt]
func randomUUID() -> String

var subtle: SubtleCryptoExports { get }
}

@objc class Crypto: NSObject, CryptoExports {
Expand All @@ -32,6 +92,8 @@ import CommonCrypto
func randomUUID() -> String {
return UUID().uuidString
}

lazy var subtle: SubtleCryptoExports = SubtleCrypto()
}

struct CryptoAPI {
Expand Down
6 changes: 5 additions & 1 deletion Sources/ECMASwift/API/Fetch/Fetch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ public class FetchAPI {
var request = URLRequest(url: url)

if let options {
if let body = options.forProperty("body"), let body = try? Body.createFrom(body) {
let headers = options.toDictionary()["headers"] as? [String: String]
headers?.forEach { (key, value) in
request.setValue(value, forHTTPHeaderField: key)
}
if let body = options.forProperty("body"), let body = Body.createFrom(body) {
request.httpBody = body.data()
}
if let method = options.forProperty("method").toString() {
Expand Down
32 changes: 18 additions & 14 deletions Sources/ECMASwift/API/Fetch/Request.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@ import JavaScriptCore

// https://developer.mozilla.org/en-US/docs/Web/API/Request

extension JSValue {
func toType<T>(_ type: T.Type) -> T? {
return self.toObject() as? T
}
}

// TODO: Probably eventually needs to model a ReadableStream properly
enum Body: Codable {
enum Body {
case blob(Data)
case arrayBuffer([Data])
case typedArray([UInt])
Expand All @@ -13,15 +19,13 @@ enum Body: Codable {
case urlSearchParams(URLSearchParams)
case string(String)

static func createFrom(_ value: Any) throws -> Body? {
switch value {
case let value as URLSearchParams:
return .urlSearchParams(value)
case let value as String:
return .string(value)
default:
return nil
static func createFrom(_ jsValue: JSValue) -> Body? {
if let searchParamsValue = jsValue.toType(URLSearchParams.self) {
return .urlSearchParams(searchParamsValue)
} else if let stringValue = jsValue.toString() {
return .string(stringValue)
}
return nil
}

func data() -> Data? {
Expand Down Expand Up @@ -89,13 +93,13 @@ enum Body: Codable {

weak var context: JSContext?

init(url: String, options: [AnyHashable: Any]? = nil) {
init(url: String, options: JSValue? = nil) {
self.url = url

if let options {
self.method = options["method"] as? String
if let body = options["body"] {
self.body = try? .createFrom(body)
self.method = options.forProperty("method").toString()
if let body = options.forProperty("body") {
self.body = Body.createFrom(body)
}
}
}
Expand Down Expand Up @@ -164,7 +168,7 @@ enum Body: Codable {
struct RequestAPI {
func registerAPIInto(context: JSContext) {
let requestClass: @convention(block) (String, JSValue?) -> Request = { url, options in
let request = Request(url: url, options: options?.toDictionary())
let request = Request(url: url, options: options)
request.context = context
return request
}
Expand Down

0 comments on commit aabbce5

Please sign in to comment.