Matt Nelson
04/24/2023, 8:35 PMjvm
internal actual fun verifyOrThrow(data: ByteArray, signature: ByteArray, pubKey: ByteArray) {
try {
val key = KeyFactory.getInstance("RSA").generatePublic(X509EncodedKeySpec(pubKey))
val sig = Signature.getInstance("SHA256withRSA")
sig.initVerify(key)
sig.update(data)
if (!sig.verify(signature)) {
throw IllegalArgumentException("Failed to verify message")
}
} catch (t: Throwable) {
if (t is IllegalArgumentException) {
throw t
} else {
throw IllegalArgumentException("Failed to verify message", t)
}
}
}
darwin (iOS, tvOS, watchOS)
internal actual fun verifyOrThrow(data: ByteArray, signature: ByteArray, pubKey: ByteArray) {
// TODO
}
Matt Nelson
04/24/2023, 9:42 PMCFData
instead of NSData
import kotlinx.cinterop.*
import platform.CoreFoundation.*
import platform.Security.*
@OptIn(UnsafeNumber::class)
@Throws(IllegalArgumentException::class)
internal actual fun verifyOrThrow(data: ByteArray, pubKey: ByteArray, signature: ByteArray) {
val allocator = CFAllocatorGetDefault()
?: throw IllegalArgumentException("Failed to retrieve CFAllocator")
val allocated = mutableSetOf<CPointer<*>>()
val result = try {
val attributes = CFDictionaryCreateMutable(allocator, 2, null, null)
?.also { allocated.add(it) }
?: throw IllegalArgumentException("Failed to create CFDictionary")
CFDictionaryAddValue(attributes, kSecAttrKeyType, kSecAttrKeyTypeRSA)
CFDictionaryAddValue(attributes, kSecAttrKeyClass, kSecAttrKeyClassPublic)
val pubKeyData = pubKey.toCFData(allocator).also { allocated.add(it) }
val keyRef: SecKeyRef = SecKeyCreateWithData(pubKeyData, attributes, null)
?: throw IllegalArgumentException("Failed to create SecKeyRef for public key")
val messageData = data.toCFData(allocator).also { allocated.add(it) }
val signatureData = signature.toCFData(allocator).also { allocated.add(it) }
SecKeyVerifySignature(
keyRef,
kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256,
messageData,
signatureData,
null
)
} catch (t: Throwable) {
if (t is IllegalArgumentException) {
throw t
} else {
throw IllegalArgumentException("Failed to verify signature", t)
}
} finally {
for (ptr in allocated) {
CFAllocatorDeallocate(allocator, ptr)
}
}
if (!result) {
throw IllegalArgumentException("Failed to verify signature")
}
}
@OptIn(UnsafeNumber::class)
@Suppress("NOTHING_TO_INLINE")
private inline fun ByteArray.toCFData(allocator: CFAllocatorRef): CFDataRef = usePinned {
CFDataCreate(allocator, it.addressOf(0).reinterpret(), size.toLong().convert())
} ?: throw IllegalArgumentException("Failed to convert bytes to CFData")