0

I am trying to decrypt data with Swift CryptoKit that I have encrypted with php using openssl_encrypt() chacha20-poly1305. Encrypting works, but when decrypting I get the error: incorrectParameterSize (Swift: error 1, line 10).

PHP encryption using openssl_encrypt:

<?php
$plaintext = "Hello World";
$key = base64_decode("O7IaTssF6RKgc84b8daAHojiveFY4vyQ4zKRZv3APKc=");
$nonce = base64_decode("MmTNTi+MmTNTi+fB");
$encrypted = 
openssl_encrypt(
    $plaintext,
    'chacha20-poly1305',
    $key,
    0,
    $nonce
);

echo $encrypted;

Swift decryption using CryptoKit

import CryptoKit
import Foundation
let encoded_data = "OG54KzRFKytSSmQ5d0M4PQ=="
let encoded_nonce = "MmTNTi+MmTNTi+fB"
let encoded_key = "O7IaTssF6RKgc84b8daAHojiveFY4vyQ4zKRZv3APKc="
let data = Data(base64Encoded: encoded_data)!
let nonce = Data(base64Encoded: encoded_nonce)!
let key = SymmetricKey(data: Data(base64Encoded: encoded_key)!)
do{
    let box = try ChaChaPoly.SealedBox(combined: data.base64EncodedData())
    do{
        let decryptedData = try ChaChaPoly.open(box,using: key)
        let decryptedString = String(decoding: decryptedData, as: UTF8.self)
        print("Decrypted string: \(decryptedString)")
    }catch{
        print("error 2: \(error)")
    }
}catch{
    print("error 1: \(error)")
}

I also tried using sodium_crypto_aead_chacha20poly1305_encrypt() to encrypt, which resulted in the error "authenticationFailure" (Swift: error 2, line 12).

PHP encryption using sodium_crypto_aead_chacha20poly1305_encrypt:

<?php

$plaintext = "Hello World";
$key = base64_decode("O7IaTssF6RKgc84b8daAHojiveFY4vyQ4zKRZv3APKc=");
$nonce = random_bytes(SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES);
$encrypted = sodium_crypto_aead_chacha20poly1305_encrypt($plaintext,"",$nonce, $key);

echo "Data:" . base64_encode($encrypted) . "\n";
echo "Nonce:" . base64_encode($nonce);
  • You do not use the key in the 1st PHP code, but pass a `NULL` instead. Also, `ChaCha20-Poly1305` applies a 96 bit (12 bytes), which is why the PHP code gives an appropriate warning (probably the Base64 decoding is missing). Furthermore, the PHP code Base64 encodes the ciphertext twice (implicitly because the 4th parameter passed to `openssl_encrypt()` is `0`, and once explicitly). – Topaco Apr 11 '23 at 15:24
  • Thanks, my bad, should actually look like this. (edited it, hopefully correct.) But there is still the same error. – I don't know how to code Apr 11 '23 at 16:06
  • Future readers might find it easier to reconcile post and comments if you don't change the original post, but append your changes to the end of the original post. Anyway, there seems to be a problem with the tag generation for `chacha20-poly1305`, see this issue ([#76935](https://bugs.php.net/bug.php?id=76935)). There, another library is given, which can be used alternatively, including a usage example. – Topaco Apr 11 '23 at 16:08
  • You mean the error is in the PHP file? Because the encryption and decryption using PHP works fine. I just get an error when decrypting using swift. – I don't know how to code Apr 11 '23 at 17:18
  • Yes, see my answer for more details and a PHP/Sodium based solution. – Topaco Apr 11 '23 at 19:33

2 Answers2

0

In a real-world scenario, you would need to validate the data, nonce, and key before attempting to decrypt the data to ensure that they have not been modified or corrupted.

import CryptoKit
import Foundation

let encodedData = "OG54KzRFKytSSmQ5d0M4PQ=="
let encodedNonce = "MmTNTi+MmTNTi+fB"
let encodedKey = "O7IaTssF6RKgc84b8daAHojiveFY4vyQ4zKRZv3APKc="

guard let data = Data(base64Encoded: encodedData),
      let nonce = Data(base64Encoded: encodedNonce),
      let key = SymmetricKey(data: Data(base64Encoded: encodedKey)) else {
    print("Invalid data, nonce, or key")
    return
}

do {
    let box = try ChaChaPoly.SealedBox(combined: data)
    let decryptedData = try ChaChaPoly.open(box, using: key)
    let decryptedString = String(decoding: decryptedData, as: UTF8.self)
    print("Decrypted string: \(decryptedString)")
} catch {
    print("Decryption failed: \(error)")
}
memo
  • 305
  • 1
  • 3
  • 10
0

ChaCha20-Poly1305 is authenticated encryption, i.e. during encryption a tag is generated in addition to the ciphertext, which is required for authentication during decryption.
The PHP/OpenSSL code does not generate this tag due to a bug, which is therefore missing when decrypting with the Swift code, so decryption fails (PHP/OpenSSL internally decryption works). More details about the bug are described in #76935.


PHP/Sodium, on the other hand, works:

$plaintext = 'secret message';
$key = base64_decode('XohImNooBHFR0OVvjcYpJ3NgPQ1qq73WKhHvch0VQtg=');
$nonce = base64_decode('F6u88lkmmz8Rrjvf'); // for testing only, otherwise: random_bytes(SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES);
$nonceCtTag = $nonce . sodium_crypto_aead_chacha20poly1305_ietf_encrypt($plaintext, '', $nonce, $key);
print(base64_encode($nonceCtTag) . PHP_EOL); // F6u88lkmmz8RrjvfJ22r4vrKmWuY8a2DNpjUKZVUi9Wp9QjZVgheBihn

The test data is from here, i.e. was generated with CryptoKit's ChaCha20-Poly1305, so decryption with your Swift code should give the original plaintext (the key corresponds to the SHA256 hash of password).

Note that sodium_crypto_aead_chacha20poly1305_ietf_encrypt() returns as result the concatenation of ciphertext and tag, so that $nonceCtTag corresponds to the combined representation (nonce|ciphertext|tag) to be passed in SealedBox(combined: ...).


The code above uses sodium_crypto_aead_chacha20poly1305_ietf_encrypt() with a 12 bytes nonce instead of sodium_crypto_aead_chacha20poly1305_encrypt() with an 8 bytes nonce (see here for more details regarding the different variants).

Topaco
  • 40,594
  • 4
  • 35
  • 62