1 // Copyright 2022-present 650 Industries. All rights reserved.
2
3 import CommonCrypto
4 import ABI49_0_0ExpoModulesCore
5
6 public class CryptoModule: Module {
definitionnull7 public func definition() -> ModuleDefinition {
8 Name("ExpoCrypto")
9
10 AsyncFunction("digestStringAsync", digestString)
11
12 Function("digestString", digestString)
13
14 AsyncFunction("getRandomBase64StringAsync", getRandomBase64String)
15
16 Function("getRandomBase64String", getRandomBase64String)
17
18 Function("getRandomValues", getRandomValues)
19
20 Function("digest", digest)
21
22 Function("randomUUID") {
23 UUID().uuidString.lowercased()
24 }
25 }
26 }
27
getRandomBase64Stringnull28 private func getRandomBase64String(length: Int) throws -> String {
29 var bytes = [UInt8](repeating: 0, count: length)
30 let status = SecRandomCopyBytes(kSecRandomDefault, length, &bytes)
31
32 guard status == errSecSuccess else {
33 throw FailedGeneratingRandomBytesException(status)
34 }
35 return Data(bytes).base64EncodedString()
36 }
37
digestStringnull38 private func digestString(algorithm: DigestAlgorithm, str: String, options: DigestOptions) throws -> String {
39 guard let data = str.data(using: .utf8) else {
40 throw LossyConversionException()
41 }
42
43 let length = Int(algorithm.digestLength)
44 var digest = [UInt8](repeating: 0, count: length)
45
46 data.withUnsafeBytes { bytes in
47 let _ = algorithm.digest(bytes.baseAddress, UInt32(data.count), &digest)
48 }
49
50 switch options.encoding {
51 case .hex:
52 return digest.reduce("") { $0 + String(format: "%02x", $1) }
53 case .base64:
54 return Data(digest).base64EncodedString()
55 }
56 }
57
getRandomValuesnull58 private func getRandomValues(array: TypedArray) throws -> TypedArray {
59 let status = SecRandomCopyBytes(
60 kSecRandomDefault,
61 array.byteLength,
62 array.rawPointer
63 )
64
65 guard status == errSecSuccess else {
66 throw FailedGeneratingRandomBytesException(status)
67 }
68 return array
69 }
70
digestnull71 private func digest(algorithm: DigestAlgorithm, output: TypedArray, data: TypedArray) {
72 let length = Int(algorithm.digestLength)
73 let outputPtr = output.rawPointer.assumingMemoryBound(to: UInt8.self)
74 algorithm.digest(data.rawPointer, UInt32(data.byteLength), outputPtr)
75 }
76
77 private class LossyConversionException: Exception {
78 override var reason: String {
79 "Unable to convert given string without losing some information"
80 }
81 }
82
83 private class FailedGeneratingRandomBytesException: GenericException<OSStatus> {
84 override var reason: String {
85 "Generating random bytes has failed with OSStatus code: \(param)"
86 }
87 }
88