1 // Copyright 2022-present 650 Industries. All rights reserved. 2 3 import CommonCrypto 4 import ExpoModulesCore 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 } 19 20 private func getRandomBase64String(length: Int) throws -> String { 21 var bytes = [UInt8](repeating: 0, count: length) 22 let status = SecRandomCopyBytes(kSecRandomDefault, length, &bytes) 23 24 guard status == errSecSuccess else { 25 throw FailedGeneratingRandomBytesException(status) 26 } 27 return Data(bytes).base64EncodedString() 28 } 29 30 private func digestString(algorithm: DigestAlgorithm, str: String, options: DigestOptions) throws -> String { 31 guard let data = str.data(using: .utf8) else { 32 throw LossyConversionException() 33 } 34 35 let length = Int(algorithm.digestLength) 36 var digest = [UInt8](repeating: 0, count: length) 37 38 data.withUnsafeBytes { bytes in 39 let _ = algorithm.digest(bytes.baseAddress, UInt32(data.count), &digest) 40 } 41 42 switch options.encoding { 43 case .hex: 44 return digest.reduce("") { $0 + String(format: "%02x", $1) } 45 case .base64: 46 return Data(digest).base64EncodedString() 47 } 48 } 49 50 private class LossyConversionException: Exception { 51 override var reason: String { 52 "Unable to convert given string without losing some information" 53 } 54 } 55 56 private class FailedGeneratingRandomBytesException: GenericException<OSStatus> { 57 override var reason: String { 58 "Generating random bytes has failed with OSStatus code: \(param)" 59 } 60 } 61