18 Mar 2021
Swift CryptoKit and Ruby/Python
I spent days figuring out how to decrypt ChaChaPoly encrypted data with
Swift CryptoKit using other languages. What should have taken me minutes took
me hours. As a time savior, here is how you can decrypt it using Ruby or
Python. I ended up reading the source code of
swift-crypto
to understand what’s the combined sealbox was doing.
Use the following in Xcode Playground to encrypt a string:
import UIKit
import CryptoKit
let str = "Hello, playground"
let strData = str . data ( using : . utf8 ) !
let key = SymmetricKey ( size : . bits256 )
let keyString = key . withUnsafeBytes { Data ( $0 ) } . base64EncodedString ()
do {
let sealbox = try ChaChaPoly . seal ( strData , using : key )
print ( "Key: \( keyString ) " )
print ( "Combined: \( sealbox . combined . base64EncodedString () ) " )
} catch { }
The output when running it on my computer (you obviously will get a different
result):
Key: j6tifPZTjUtGoz+1RJkO8dOMlu48MUUSlwACw/fCBw0=
Combined: OWFsadrLrBc6ak+6TiYhAI6JKvoQzVMpnRdJ6iE5vEiAhadrCu6EcEQiAs7G
You can decrypt it using Ruby with:
#!/usr/bin/env ruby
require "openssl"
require "base64"
key = Base64 . decode64 "j6tifPZTjUtGoz+1RJkO8dOMlu48MUUSlwACw/fCBw0="
combined = Base64 . decode64 "OWFsadrLrBc6ak+6TiYhAI6JKvoQzVMpnRdJ6iE5vEiAhadrCu6EcEQiAs7G"
text = "Hello, playground"
combinedTag = combined [ - 16 ..- 1 ]
combinedNonce = combined [ 0 .. 11 ]
combinedCipherText = combined [ 12 .. ( combined . size - 17 )]
decipher = OpenSSL :: Cipher . new ( "chacha20-poly1305" ). decrypt
decipher . key = key
decipher . iv = combinedNonce
decipher . auth_tag = combinedTag
decrypted = decipher . update ( combinedCipherText ) + decipher . final
if decrypted == text
puts "OK!"
end
And you can decrypt it using Python:
#!/usr/bin/env python3
# Installation:
# pip install pycryptodome
import json
import Crypto
from base64 import b64decode
from Crypto.Cipher import ChaCha20_Poly1305
key = b64decode ( "j6tifPZTjUtGoz+1RJkO8dOMlu48MUUSlwACw/fCBw0=" )
combined = b64decode ( "OWFsadrLrBc6ak+6TiYhAI6JKvoQzVMpnRdJ6iE5vEiAhadrCu6EcEQiAs7G" )
text = "Hello, playground"
combinedNonce = combined [: 12 ]
combinedTag = combined [: - 16 ]
combinedCipher = combined [ 12 : - 16 ]
decrypted = ChaCha20_Poly1305 . new ( key = key , nonce = combinedNonce ). decrypt ( combinedCipher ). decode ()
if decrypted == text :
print ( "OK!" )
I hope this post saved you some time. Thanks to
@FredericJacobs for the help.