It seems so much simpler to encrypt using java than obj-c with Tink. Given a known 32-bytes XChaCha20Poly1305
key
, and a 16-bytes authenticated data (aad
), how can the same be done in objective-c?
Java:
import com.google.crypto.tink.subtle.XChaCha20Poly1305;
XChaCha20Poly1305 xChaCha20Poly1305 = new XChaCha20Poly1305(key);
byte[] encryptedData = xChaCha20Poly1305.encrypt(plainData, aad);
Long story short, the following java and obj-c codes work together.
Java:
import com.google.crypto.tink.aead.AeadConfig;
import com.google.crypto.tink.*;
byte[] plainData = ...
byte[] key = ...//32 bytes
byte[] aad = ...
AeadConfig.register();
byte[] prefix = {0x1a,0x20};
byte[] fullKey = new byte[prefix.length + key.length];
System.arraycopy(prefix, 0, fullKey, 0, prefix.length);
System.arraycopy(key, 0, fullKey, prefix.length, key.length);
String fullKeyBase64 = new String(com.groups.network.aes.Base64.encode(fullKey), "UTF-8");
String jsonKey = "{\"primaryKeyId\":1635322858,\"key\":[{\"keyData\":{\"typeUrl\":\"type.googleapis.com/google.crypto.tink.XChaCha20Poly1305Key\",\"value\":\""+fullKeyBase64+"\",\"keyMaterialType\":\"SYMMETRIC\"},\"status\":\"ENABLED\",\"keyId\":1635322858,\"outputPrefixType\":\"TINK\"}]}";
KeysetHandle keysetHandle = CleartextKeysetHandle.read(
JsonKeysetReader.withString(jsonKey));
Aead aead = keysetHandle.getPrimitive(Aead.class);
byte[] encrypted = aead.encrypt(plainData, aad);
Objective C:
NSData* encryptedBytes = ...
NSData* aad = ...
NSData* key = ...//32-bytes
NSError *error = nil;
TINKAeadConfig *aeadConfig = [[TINKAeadConfig alloc] initWithError:&error];
if (!aeadConfig || error) {
//handle error
}
if (![TINKConfig registerConfig:aeadConfig error:&error]) {
//handle error
}
NSString* prefix = @"1a20";
NSMutableData* prefixData = [[NSMutableData alloc] init];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for(i=0; i<[prefix length]/2;i++){
byte_chars[0] = [prefix characterAtIndex:i*2];
byte_chars[1] = [prefix characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[prefixData appendBytes:&whole_byte length:1];
}
NSData* originalKeyData = [key dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData* finalKey = [prefixData mutableCopy];
[finalKey appendData:originalKeyData];
NSString* jsonTmp = [NSString stringWithFormat:@"{\"primaryKeyId\":1635322858,\"key\":[{\"keyData\":{\"typeUrl\":\"type.googleapis.com/google.crypto.tink.XChaCha20Poly1305Key\",\"value\":\"%@\",\"keyMaterialType\":\"SYMMETRIC\"},\"status\":\"ENABLED\",\"keyId\":1635322858,\"outputPrefixType\":\"TINK\"}]}", [finalKey base64Encoding]];
NSData* jsonKeyData = [jsonTmp dataUsingEncoding:NSUTF8StringEncoding];
TINKJSONKeysetReader *reader = [[TINKJSONKeysetReader alloc] initWithSerializedKeyset:jsonKeyData error:&error];
if (!reader || error) {
//handle error
}
TINKKeysetHandle *handle = [[TINKKeysetHandle alloc] initCleartextKeysetHandleWithKeysetReader:reader error:&error];
if (!handle || error) {
//handle error
}
id<TINKAead> aead = [TINKAeadFactory primitiveWithKeysetHandle:handle error:&error];
if (!aead || error) {
//handle error
}
NSData *aadData = [aad dataUsingEncoding:NSUTF8StringEncoding];
NSData *plaintext = [aead decrypt:encryptedBytes withAdditionalData:aadData error:&error];
if (!plaintext || error) {
//handle error
}
Pay attention to the {0x1a,0x20}. This changes according to the type of encryption you use (here it's fine for XChacha20-Poly1305). This can be "discovered" with the Tinkey tool, which outputs a JSON key template if you run something like java -jar tinkey_deploy.jar create-keyset --key-template XCHACHA20_POLY1305
(the output base64 key needs to then be transformed to bytes and you'll need to figure which first 2 bytes correspond to which encryption)