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 Function("getRandomValues", getRandomValues) 19 20 Function("randomUUID") { 21 UUID().uuidString 22 } 23 } 24 } 25 26 private func getRandomBase64String(length: Int) throws -> String { 27 var bytes = [UInt8](repeating: 0, count: length) 28 let status = SecRandomCopyBytes(kSecRandomDefault, length, &bytes) 29 30 guard status == errSecSuccess else { 31 throw FailedGeneratingRandomBytesException(status) 32 } 33 return Data(bytes).base64EncodedString() 34 } 35 36 private func digestString(algorithm: DigestAlgorithm, str: String, options: DigestOptions) throws -> String { 37 guard let data = str.data(using: .utf8) else { 38 throw LossyConversionException() 39 } 40 41 let length = Int(algorithm.digestLength) 42 var digest = [UInt8](repeating: 0, count: length) 43 44 data.withUnsafeBytes { bytes in 45 let _ = algorithm.digest(bytes.baseAddress, UInt32(data.count), &digest) 46 } 47 48 switch options.encoding { 49 case .hex: 50 return digest.reduce("") { $0 + String(format: "%02x", $1) } 51 case .base64: 52 return Data(digest).base64EncodedString() 53 } 54 } 55 56 private func getRandomValues(array: TypedArray) throws -> TypedArray { 57 let status = SecRandomCopyBytes( 58 kSecRandomDefault, 59 array.byteLength, 60 array.rawPointer 61 ) 62 63 guard status == errSecSuccess else { 64 throw FailedGeneratingRandomBytesException(status) 65 } 66 return array 67 } 68 69 private class LossyConversionException: Exception { 70 override var reason: String { 71 "Unable to convert given string without losing some information" 72 } 73 } 74 75 private class FailedGeneratingRandomBytesException: GenericException<OSStatus> { 76 override var reason: String { 77 "Generating random bytes has failed with OSStatus code: \(param)" 78 } 79 } 80