objective-ctink

How to do this in Tink's Objective-c?


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);

Solution

  • 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)