import CryptoKit import ExpoModulesCore internal struct SealedDataConfig: Record { @Field var ivLength: Int = DEFAULT_IV_LENGTH @Field var tagLength: Int = DEFAULT_TAG_LENGTH } final class SealedData: SharedObject { private var inner: AES.GCM.SealedBox init(sealedBox: AES.GCM.SealedBox) { inner = sealedBox } init(ivLength: Int, tagLength: Int, content: Data) throws { // SealedBox(combined:) works only if IV length is 12 (default) // otherwise this needs to be done manually if ivLength == DEFAULT_IV_LENGTH { inner = try AES.GCM.SealedBox(combined: content) } else { let nonce = try AES.GCM.Nonce(data: content.prefix(ivLength)) inner = try AES.GCM.SealedBox( nonce: nonce, ciphertext: content.dropFirst(ivLength).dropLast(tagLength), tag: content.suffix(tagLength) ) } } init(iv: Data, ciphertext: Data, tag: Data) throws { let nonce = try AES.GCM.Nonce(data: iv) inner = try AES.GCM.SealedBox( nonce: nonce, ciphertext: ciphertext, tag: tag ) } convenience init(iv: Data, ciphertextWithTag: Data, tagLength: Int) throws { try self.init( iv: iv, ciphertext: ciphertextWithTag.dropLast(tagLength), tag: ciphertextWithTag.suffix(tagLength) ) } var combined: Data { // combined works only if IV length is 12 bytes if let combined = inner.combined { return combined } return self.iv + inner.ciphertext + self.tag } var iv: Data { Data(inner.nonce) } var tag: Data { inner.tag } func ciphertext(withTag: Bool) -> Data { if withTag { return inner.ciphertext + inner.tag } return inner.ciphertext } var nativeValue: AES.GCM.SealedBox { inner } override func getAdditionalMemoryPressure() -> Int { self.combined.count } }