Kotlin Native (iOS), using CValuesRef with CCCrypt - ios

I am working on an AES256 encryption algorithm in a Kotlin Multiplatform project targeting iOS.
I checked some of the existing libraries that implement this in pure Kotlin (such as krypto), but none of them fit the requirements that I have from the rest of the code (which already has implementations in JVM and JS, so cannot be changed).
Coming from an iOS background, I decided to use CommonCrypto. I adapted and ported the code from here but I am stuck on how to pass a ULong by reference to the CCCrypt function and later retrieve its value.
I read very carefully the Kotlin docs on the C Interop and the Objective-C Interop, but I couldn't find any example that would explain how to deal with my case.
In particular, my issue is with the numBytesEncrypted variable (see code below). I need to pass that by reference to the CCCrypt function and then read its value to instantiate the resulting NSData with the correct length. In Objective-C/Swift, I would prefix the variable with & when calling the function.
However, Kotlin doesn't support the & operator. If I understood correctly from the docs, the replacement in Native is CValueRef (docs), so I used the cValue shorthand to get a ref of the correct type (which should be size_t, aka. ULong).
I tried to instantiate the CValueRef in two ways, both seem to be working as far as type-checking goes:
val numBytesEncrypted = cValue<ULongVar>()
// or
val numBytesEncrypted = cValue<ULongVarOf<size_t>>()
And then I used this code to get the value after the function has been executed:
numBytesEncrypted.getPointer(MemScope()).pointed.value // <- this always returns 0.
I tested the rest of the algorithm and it works perfectly (the encrypted value is the expected one and decryption also works), but without knowing the length of the encrypted/decrypted data, I cannot instantiate the NSData (and the resulting ByteArray) correctly.
Here's the full function code, including an extension I wrote (based on code found here) to convert NSData to ByteArray:
private fun process(operation: CCOperation, key: ByteArray, initVector: ByteArray, data: ByteArray): ByteArray = memScoped {
bzero(key.refTo(0), key.size.convert())
val dataLength = data.size
val buffSize = (dataLength + kCCBlockSizeAES128.toInt()).convert<size_t>()
val buff = platform.posix.malloc(buffSize)
val numBytesEncrypted = cValue<ULongVarOf<size_t>>() // <- or the other way above
val status = CCCrypt(
operation,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
key.refTo(0), kCCKeySizeAES256.convert(),
initVector.refTo(0),
data.refTo(0), data.size.convert(),
buff, buffSize,
numBytesEncrypted
)
if (status == kCCSuccess) {
return NSData.dataWithBytesNoCopy(
buff,
numBytesEncrypted.getPointer(MemScope()).pointed.value // <- this always returns the original value of the variable or 0.
)
.toByteArray()
}
free(buff)
return byteArrayOfUnsigned(0)
}
fun NSData.toByteArray(): ByteArray = ByteArray(this#toByteArray.length.toInt()).apply {
usePinned {
memcpy(it.addressOf(0), this#toByteArray.bytes, this#toByteArray.length)
}
}
Also, here's the unit test that I'm using to verify the code:
#Test
fun testAes256() {
val initVector = ByteArray(16) { _ -> 0x00 }
val key = ByteArray(32) { _ -> 0x00 }
val data = ByteArray(1) { _ -> 0x01 }
val expectedEncrypted = byteArrayOfUnsigned(0xFE, 0x2D, 0xE0, 0xEE, 0xF3, 0x2A, 0x05, 0x10, 0xDC, 0x31, 0x2E, 0xD7, 0x7D, 0x12, 0x93, 0xEB)
val encrypted = process(kCCEncrypt, key, initVector, data)
val decrypted = process(kCCDecrypt, key, initVector, encrypted)
assertArraysEquals(expectedEncrypted, encrypted)
assertArraysEquals(data, decrypted)
}
private fun assertArraysEquals(expected: ByteArray, actual: ByteArray) {
if (expected.size != actual.size) asserter.fail("size is different. Expected: ${expected.size}, actual: ${actual.size}")
(expected.indices).forEach { pos ->
if (expected[pos] != actual[pos]) asserter.fail("entry at pos $pos different \n expected: ${debug(expected)} \n actual : ${debug(actual)}")
}
}
Thank you in advance!

Change
val numBytesEncrypted = cValue<ULongVarOf<size_t>>()
to
val numBytes = cValue<ULongVar>().ptr
And you may use it in your code
NSData.dataWithBytesNoCopy(
buff,
numBytes.pointed.value
).toByteArray()
In the method signature described that dataOutMoved is kotlinx.cinterop.CValuesRef type.
I found that getPointer always alloc memory.
public abstract class CValues<T : CVariable> : CValuesRef<T>() {
public override fun getPointer(scope: AutofreeScope): CPointer<T> {
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
}
val numBytes = cValue<ULongVar>()
println("${numBytes.ptr.rawValue} ${numBytes.ptr.rawValue} ${numBytes.ptr.rawValue}")
0x7fa4a593bc18 0x7fa4a593ae28 0x7fa4a593b5d8
val numBytes = cValue<ULongVar>().ptr
println("${numBytes.rawValue} ${numBytes.rawValue} ${numBytes.rawValue}")
0x7fd5a287bfd8 0x7fd5a287bfd8 0x7fd5a287bfd8

Related

How to do encryption like android's "PBEWithMD5AndDES" in flutter dart?

I'm trying to recreate an existing mobile apps into flutter but struggling in the "PBEWithMD5AndDES" encryption on android which I can't seem to find similar way in dart.
This is so far what I've tried to achieve the same using Flutter_Des.dart, Password_Hash.dart and Crypto.dart library but still can't get the same output.
encryptPassword(String keyStr, String passwordStr) async {
if (keyStr.length == 0 || passwordStr.length == 0) {
return "";
}
var generator = new PBKDF2(hashAlgorithm: md5);
String saltStr = generateSaltBase64String();
var hash = generator.generateBase64Key(keyStr, saltStr, round, keyLength);
var encryptBase64 = await FlutterDes.encryptToBase64(passwordStr, hash.toString());
return encryptBase64;
}
Below is what I have currently on Android.
KeySpec keySpec = new PBEKeySpec(str.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
ecipher = Cipher.getInstance("PBEWithMD5AndDES");
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
byte[] utf8 = password.getBytes("UTF8");
byte[] enc = ecipher.doFinal(utf8);
enc = Base64.encode(enc, Base64.DEFAULT);
return new String(enc);
I'm expecting the same output as android so my backend able to decrypt it.
PBEWithMD5AndDES uses PBKDF1 to generate the key material (not PBKDF2). This gives you 128 bits of key material that you then use as two 64 bit halves as the key and the IV for DES.
Derive the key and IV as follows - I've plugged in some arbitrary values for iteration, password and salt and confirmed against JCrypto.
int iterations = 31;
List<int> salt = [0x21, 0x21, 0xf0, 0x55, 0xc3, 0x9f, 0x5a, 0x75];
List<int> password = utf8.encode('test');
List<int> saltedKey = password + salt;
Digest d = md5.convert(saltedKey);
for (int i = 1; i < iterations; i++) {
d = md5.convert(d.bytes);
}
print(d);
List<int> key = d.bytes.sublist(0, 8);
List<int> iv = d.bytes.sublist(8, 16);
print(key);
print(iv);
I can't find a Dart implementation of DES which takes a key and IV as bytes. triple_des wants them as strings - i.e. it's dumbed down. Pointy castle doesn't do DES. FlutterDes also seems to want strings. You might be able to modify triple_des to take binary keys and IVs. Or use a different cipher.
Solved by using flutter's methodchannel and call platform specific code to do the encryption and its working now. Thanks

Writing a native messaging host in GJS

I'm trying to write a native messaging host for a chrome/firefox extension in GJS (since it will rely on code already written in GJS) but encountering some hurdles. I'm using chrome-gnome-shell as a rough template since it also uses GLib/Gio instrospection and GApplication, but it has the advantage of python struct that I don't have.
Quickly, native messaging hosts exchange messages through stdin/stdout which are an Int32 (4-bytes) length following by a string of utf-8 encoded JSON.
chrome-gnome-shell uses GLib.IOChannel with set_encoding('utf-8') and struct to handle int32 bytes. I've had trouble using that class in GJS and don't have struct so have been trying Gio.UnixInputStream wrapped in Gio.DataInputStream (and output counterparts), with put_int32()/read_int32() and put_string()/read_string().
Apparently I'm mightily confused about what I'm doing. If I call Gio.DataInputStream.read_int32() it returns a number 369098752, so I'm guessing the int32 is not being converted to a regular Number. If I call Gio.DataInputStream.read_bytes(4, null).unref_to_array() to get a ByteArray; ByteArray.toString() returns '\u0016' while ByteArray[0] returns '22' which appears to be the actual length.
Some pointers on reading/writing int32's to a datastream and would be much appreciated.
chrome-gnome-shell references:
on_input()
send_message()
I don't know if this is the best way to solve this, but here's what I came up with.
Two functions using the ByteArray import (modified from somewhere on SO):
const ByteArray = imports.byteArray;
function fromInt32 (byteArray) {
var value = 0;
for (var i = byteArray.length - 1; i >= 0; i--) {
value = (value * 256) + byteArray[i];
}
return value;
};
function toInt32 (num) {
var byteArray = [0, 0, 0, 0];
for (var index_ = 0; index_ < byteArray.length; index_++) {
var byte = num & 0xff;
byteArray [index_] = byte;
num = (num - byte) / 256 ;
}
return ByteArray.fromArray(byteArray);
};
For receiving/sending:
const Gio = imports.gi.Gio;
// Receiving
let stdin = new Gio.DataInputStream({
base_stream: new Gio.UnixInputStream({ fd: 0 })
});
let int32 = stdin.read_bytes(4, null).toArray();
let length = fromInt32(int32);
let data = stdin.read_bytes(length, null).toArray().toString();
let message = JSON.parse(data);
// Sending
let stdout = new Gio.DataOutputStream({
base_stream: new Gio.UnixOutputStream({ fd: 1 })
});
let data = JSON.stringify(message);
let int32 = toInt32(data.length);
stdout.write(int32, null);
stdout.put_string(data, null);
Of course, you should wrap these in try-catch as appropriate and you'll probably want to connect a source to the input (you can use the Gio.UnixInputStream):
let source = stdin.base_stream.create_source(null);
source.set_callback(onReceiveFunc);
source.attach(null);
You may be able to use Gio.DataOutputStream.put_int32() and Gio.DataInputStream.read_int32() the same way as you use read_bytes() and put_string().

RSA encryption between Swift and C#

I need to build the Apple client part to an existing server that is written in .Net/C#. The client should run on OS X and iOS. Part of the communication is done using encrypted data, the C# source being something like
string privateKey = "xyz…=" // Base64 encoded
string publicKey = "abc…=" // Base64 encoded
byte[] decrypt(byte[] encryptedBytes)
{
CspParameters cspParams = new CspParameters { ProviderType = 1 /* PROV_RSA_FULL */ };
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(cspParams);
rsaProvider.ImportCspBlob(Convert.FromBase64String(privateKey));
return rsaProvider.Decrypt(encryptedBytes, false);
}
This looks quite simple, but I cannot find a way to implement the encrypt counterpart in Swift.
The publicKey data consists solely of Modulus and Exponent numbers. I can extract the Modulus number from the public key with rsaProvider.ExportParameters(false).Modulus, the Exponent being [1, 0, 1] as usual.
How can I use the Modulus number (or the publicKey string constant itself) in my Swift application on the OS X / iOS client side, and how do I encrypt a plaintext there that can be decrypted on the C# server side?
I understand that Apple is not very fond of introducing cryptographic keys in such a low-level fashion, but there seems to be a possibility to import such a key into the keychain and then use it for encryption.
I have the building blocks (SecItem…(), SecKeyEncrypt(), etc.) but I cannot get it up and running.
Something like this should work on iOS.
For OS X, you need a slightly different approach (SecItemImport instead of SecItemAdd).
import UIKit
import Security
let publicKey = "MIIBCgKCAQEAxWp6GqUOG3xuMhaE0Eeb0eOqbPHE2lRQ53qg2A1rInWdBTVtQaU82Yurv6rFoz++jswiHf3VBy3plhalF+1CTruuzSqVUjpeWTGFppoIym8andVtGLP5mN56Ks7z8VxwQ4MvmM5lGqw3YX6NWVNirWTGdJsqiplmhkAZXFAY43ivwTFSbQ4Uhx7SA0PK537V6je5MJ9edaWpKc1HoGH/bZG9/qrunv2Wam0w9qb8/TOsNvxdgBFs9WZaU0amkNb4h94y9ZrJKYsRGTngDAZ/uA+WK5ZM+Dz3GelsDUErvlUlswLyhQKYPPGn+QlVbMF4drUZ6piZWPmvpY2a/iyRcwIDAQAB"
let keyData = NSData(base64EncodedString: publicKey, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
var dictionary: [NSString: AnyObject] = [
kSecClass: kSecClassKey,
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPublic,
kSecAttrApplicationTag: "mypubkeyappspecifictag",
kSecValueData: keyData!,
kSecReturnRef: true
];
var err = SecItemAdd(dictionary, nil);
if ((err != noErr) && (err != errSecDuplicateItem)) {
print("error loading public key");
}
var keyRef: AnyObject?;
err = SecItemCopyMatching(dictionary, &keyRef);
if (err == noErr) {
if let keyRef = keyRef as! SecKeyRef? {
let plaintext = "12345";
let plaintextLen = plaintext.lengthOfBytesUsingEncoding(NSUTF8StringEncoding);
let plaintextBytes = [UInt8](plaintext.utf8);
var encryptedLen: Int = SecKeyGetBlockSize(keyRef);
var encryptedBytes = [UInt8](count: encryptedLen, repeatedValue: 0);
err = SecKeyEncrypt(keyRef, SecPadding.PKCS1, plaintextBytes, plaintextLen, &encryptedBytes, &encryptedLen);
if (err != noErr) {
print(encryptedBytes);
}
}
}
SecItemDelete(dictionary);
Please note that public key for iOS should be stripped of ASN1 preamble, as specified here.
Finally, I found the right combination. Alex Skalozub's answer was a great help in this process.
Here's my process step by step:
It starts with the public key string that was given on the server side. This had been generated previously this way: (C# .Net)
CspParameters cspParams = new CspParameters { ProviderType = 1 /* PROV_RSA_FULL */ };
// generate key pair with given size
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(4096, cspParams);
string publicKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(false));
string privateKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(true));
These keys are stored internally as string constants and are then, after extracting them from their base64 representation, imported when the server is initializing:
CspParameters cspParams = new CspParameters { ProviderType = 1 /* PROV_RSA_FULL */ };
// no key generation this time:
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(cspParams);
rsaProvider.ImportCspBlob(Convert.FromBase64String(publicKey));
Since these "CSP Blobs" are a proprietary Microsoft Cryptographic API structure, I had to use them and extract the low-level RSA numbers (n and e for the public key; d for the private key was not really needed here): (C# .Net)
RSAParameters pub = rsaProvider.ExportParameters(false);
string n = Convert.ToBase64String(pub.Modulus);
string e = Convert.ToBase64String(pub.Exponent);
RSAParameters priv = rsaProvider.ExportParameters(true);
string d = Convert.ToBase64String(priv.D);
Next step is building an Apple-compatible (i.e. non standard conforming) key string from the numbers. I used Python with the PyCrypto package:
import base64
from Crypto.Util import asn1
def b64ToNum(b64str):
byteStr = base64.b64decode(b64str)
num = 0L
for digit in byteStr:
num = num * 256 + ord(digit)
return num
n_b64 = "..." # copied from C# output
e_b64 = "..." # copied from C# output
n = b64ToNum(n_b64)
e = b64ToNum(e_b64)
seq = asn1.DerSequence()
seq[:] = [ n, e ] ## Standard would be [ 0, n, e ] !!!
print s.encode("base64").replace("\n", "")
This produces a base64-encoded public key string that can be used for insertion in the iOS keychain, as shown in Alex' code (SecItemAdd, SecItemCopyMatching).
I also verified that the C# code can successfully decrypt data I encrypt on the iOS side.

Swift/iOS: How to use address (reference, pointer) in swift?

Like what you can do with inout parameters, or like what you can do with * and & in C++. For example:
#include <iostream>
using namespace std;
int main ()
{
int firstvalue, secondvalue;
int * mypointer;
mypointer = &firstvalue;
*mypointer = 10;
mypointer = &secondvalue;
*mypointer = 20;
cout << "firstvalue is " << firstvalue << '\n';
cout << "secondvalue is " << secondvalue << '\n';
return 0;
}
and the result is:
firstvalue is 10
secondvalue is 20
Can I do something similar to this in Swift?
As a general rule, no. Swift, like most modern programming languages, does not give you direct access to pointers most of the time.
There are special pointer types you can use if you need them. https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html. However, if you find yourself wanting to use these, except in special cases, I suspect that you are still thinking in C/Objective-C/C++ terms.
The inout parameter allows you to pass by reference not value.
Example:
func test1(inout a : Int) { a = 5 }
func test2(a : Int) { a = 5 }
Here the 1st function (because of the inout parameter) will modify whatever is passed in to it.
The second function will receive a copy of the variable so when you are done
var a = 4;
var b = 4;
test1(&a);
test2(b);
print(a);
print(b):
will print out
5
4
Because swift gives you full access to C stuff you can use the type unsafePointer and unsafeMutablePointer for doing funky stuff.
Check out this post for more details: http://chris.eidhof.nl/posts/swift-c-interop.html
When I want to manipulate individual bytes in a NSData type i do the following:
var rawData: NSMutableData
/* Using unsafeMutablePointers allows for raw data manipulation */
var ptr: UnsafeMutablePointer<UInt8>; // = UnsafePointer<UInt8>(rawData.bytes)
var bytes: UnsafeMutableBufferPointer<UInt8>; // = UnsafeBufferPointer<UInt8>
self.rawData = NSMutableData(data: initData)
ptr = UnsafeMutablePointer<UInt8>(rawData.mutableBytes)
bytes = UnsafeMutableBufferPointer<UInt8>(start: ptr, count: rawData.length)
Then i can access individual bytes with:
bytes[i]
this uses pointers.

Generate base64 url-encoded X.509 format 2048-bit RSA public key with Swift?

Working in Apple Swift for iOS. I have to generate this for the backend as it's a secure app.
I'm new to security and certificates and have been searching for a day now with no results.
How can I generate base64 url-encoded X.509 format 2048-bit RSA public key with swift?
Any help is highly appreciated.
There's a library for handling public-private key pairs in Swift that I recently created called Heimdall, which allows you to easily export the X.509 formatted Base64 string of the public key.
To comply with SO rules, I will also include the implementation in this answer (so that it is self-explanatory)
public func X509PublicKey() -> NSString? {
// Fetch the key, so that key = NSData of the public key
let result = NSMutableData()
let encodingLength: Int = {
if key.length + 1 < 128 {
return 1
} else {
return ((key.length + 1) / 256) + 2
}
}()
let OID: [CUnsignedChar] = [0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00]
var builder: [CUnsignedChar] = []
// ASN.1 SEQUENCE
builder.append(0x30)
// Overall size, made of OID + bitstring encoding + actual key
let size = OID.count + 2 + encodingLength + key.length
let encodedSize = encodeLength(size)
builder.extend(encodedSize)
result.appendBytes(builder, length: builder.count)
result.appendBytes(OID, length: OID.count)
builder.removeAll(keepCapacity: false)
builder.append(0x03)
builder.extend(encodeLength(key.length + 1))
builder.append(0x00)
result.appendBytes(builder, length: builder.count)
// Actual key bytes
result.appendData(key)
// Convert to Base64 and make safe for URLs
var string = result.base64EncodedStringWithOptions(.allZeros)
string = string.stringByReplacingOccurrencesOfString("/", withString: "_")
string = string.stringByReplacingOccurrencesOfString("+", withString: "-")
return string
}
public func encodeLength(length: Int) -> [CUnsignedChar] {
if length < 128 {
return [CUnsignedChar(length)];
}
var i = (length / 256) + 1
var len = length
var result: [CUnsignedChar] = [CUnsignedChar(i + 0x80)]
for (var j = 0; j < i; j++) {
result.insert(CUnsignedChar(len & 0xFF), atIndex: 1)
len = len >> 8
}
return result
}
I have removed the data fetching code, refer to either source of Heimdall or Jeff Hay's answer
If the public key is already in your keychain, you can look up the public key and return the data as base64 with something similar to the following:
// Create dictionary to specify attributes for the key we're
// searching for. Swift will automatically bridge native values
// to to right types for the SecItemCopyMatching() call.
var queryAttrs = [NSString:AnyObject]()
queryAttrs[kSecClass] = kSecClassKey
queryAttrs[kSecAttrApplicationTag] = publicKeyTag
queryAttrs[kSecAttrKeyType] = kSecAttrKeyTypeRSA
queryAttrs[kSecReturnData] = true
var publicKeyBits = Unmanaged<AnyObject>?()
SecItemCopyMatching(queryAttrs, &publicKeyBits)
// Work around a compiler bug with Unmanaged<AnyObject> types
// the following two lines should simply be
// let publicKeyData : NSData = publicKeyRef!.takeRetainedValue() as NSData
let opaqueBits = publicKeyBits?.toOpaque()
let publicKeyData = Unmanaged<NSData>.fromOpaque(opaqueBits).takeUnretainedValue()
let publicKeyBase64 = publicKeyData.base64EncodedData(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
If you need to generate the keypair and store it in the keychain, use something along these lines:
// Create dictionaries to specify key attributes. Swift will
// automatically bridge native values to to right types for the
// SecKeyGeneratePair() call.
var pairAttrs = [NSString:AnyObject]()
var privateAttrs = [NSString:AnyObject]()
var publicAttrs = [NSString:AnyObject]()
privateAttrs[kSecAttrIsPermanent] = true
privateAttrs[kSecAttrApplicationTag] = privateKeyTag
publicAttrs[kSecAttrIsPermanent] = true
publicAttrs[kSecAttrApplicationTag] = publicKeyTag
pairAttrs[kSecAttrKeyType] = kSecAttrKeyTypeRSA
pairAttrs[kSecAttrKeySizeInBits] = 2048
pairAttrs[(kSecPrivateKeyAttrs.takeUnretainedValue() as! String)] = privateAttrs
pairAttrs[(kSecPublicKeyAttrs.takeUnretainedValue() as! String)] = publicAttrs
var publicKeyPtr = Unmanaged<SecKey>?()
var privateKeyPtr = Unmanaged<SecKey>?()
let status = SecKeyGeneratePair(pairAttrs, &publicKeyPtr, &privateKeyPtr)
Note: publicKeyTag and privateKeyTag are strings used to identify the key in the keystore. Apple recommends reverse-dns notation (com.company.key.xxx), but as long as they are unique, all should be good.
Similar question with answer: Generate key pair on iphone and print to log as NSString
Although the answer there is in Objective-C, Apple reference shows that the functions (especially the most important one, SecKeyGeneratePair) can also be called directly from Swift (as long as you can do the type conversions from all those UnsafeMutablePointers etc).

Resources