Kotlin Native (iOS),使用 CValuesRef 和 CCCrypt

Posted

技术标签:

【中文标题】Kotlin Native (iOS),使用 CValuesRef 和 CCCrypt【英文标题】:Kotlin Native (iOS), using CValuesRef with CCCrypt 【发布时间】:2021-09-24 13:12:39 【问题描述】:

我正在一个针对 ios 的 Kotlin 多平台项目中研究 AES256 加密算法。

我检查了一些在纯 Kotlin 中实现此功能的现有库(例如 krypto),但它们都不符合我对其余代码的要求(已经在 J​​VM 和 JS 中实现,所以无法更改)。

来自 iOS 背景,我决定使用CommonCrypto。我从here 改编并移植了代码,但我一直坚持如何通过引用CCCrypt 函数来传递ULong,然后再检索它的值。

我非常仔细地阅读了 C Interop 和 Objective-C Interop 上的 Kotlin 文档,但我找不到任何可以解释如何处理我的案例的示例。

特别是,我的问题是 numBytesEncrypted 变量(请参阅下面的代码)。我需要通过引用CCCrypt 函数来传递它,然后读取它的值来实例化具有正确长度的结果NSData在 Objective-C/Swift 中,调用函数时,我会在变量前面加上 &

但是,Kotlin 不支持 & 运算符。如果我从文档中正确理解,Native 中的替换是 CValueRef (docs),所以我使用 cValue 简写来获取正确类型的引用(应该是 size_t,又名。@987654338 @)。

我尝试以两种方式实例化 CValueRef,就类型检查而言,两者似乎都有效:

val numBytesEncrypted = cValue<ULongVar>()
// or
val numBytesEncrypted = cValue<ULongVarOf<size_t>>()

然后我用这段代码来获取函数执行后的值:

numBytesEncrypted.getPointer(MemScope()).pointed.value // <- this always returns 0.

我测试了算法的其余部分,它运行良好(加密值是预期值,解密也有效),但在不知道加密/解密数据的长度的情况下,我无法实例化 NSData(以及正确生成ByteArray)。

这是完整的函数代码,包括我编写的扩展(基于找到的代码 here)将 NSData 转换为 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)
    

另外,这是我用来验证代码的单元测试:

@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)")
    

提前谢谢你!

【问题讨论】:

【参考方案1】:

改变

val numBytesEncrypted = cValue<ULongVarOf<size_t>>()

val numBytes = cValue<ULongVar>().ptr

你可以在你的代码中使用它

 NSData.dataWithBytesNoCopy(
                buff,
                numBytes.pointed.value
            ).toByteArray()

在描述的方法签名中,dataOutMoved 是 kotlinx.cinterop.CValuesRef 类型。

我发现getPointer总是分配内存。

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

【讨论】:

以上是关于Kotlin Native (iOS),使用 CValuesRef 和 CCCrypt的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin Native (iOS),使用 CValuesRef 和 CCCrypt

开发iOS应用,Kotlin Native是不是够格?

一睹为快!Kotlin 开发 iOS 的新利器:Kotlin/Native 插件

如何使用 Kotlin/native 生成​​依赖于另一个的 .framework?

Kotlin 一统天下?Kotlin/Native 开始支持 iOS 和 Web 开发

Kotlin Native 项目中 iOS 上的 libPhonenumber(来自 Google)