1 // Copyright 2022-present 650 Industries. All rights reserved. 2 3 import CommonCrypto 4 import ABI49_0_0ExpoModulesCore 5 6 public class CryptoModule: Module { 7 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 28 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 38 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 58 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 71 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