fix: AirPlayProtocol 编译错误修复
- StreamState 构造函数语法修正 (data class 参数不允许 val 声明) - Byte 位运算需转 Int: and/or/xor 使用 .toInt() 再转回 Byte - when 表达式赋值重写: 修复 val decrypted 不可重赋值问题 - X25519.scalarMult 公钥生成改用6参数版本 (base point) - dd-plist: NSNumber 类型匹配, NSArray.getArray(), NSDictionary.put() - PropertyListParser.parse(byte[]) 替代 InputStream - clientEdPublicKey!! 非 null 断言
This commit is contained in:
@@ -8,6 +8,7 @@ import com.dd.plist.NSDictionary
|
||||
import com.dd.plist.NSNumber
|
||||
import com.dd.plist.NSString
|
||||
import com.dd.plist.PropertyListParser
|
||||
import com.dd.plist.PropertyListParser
|
||||
import kotlinx.coroutines.*
|
||||
import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator
|
||||
import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters
|
||||
@@ -305,7 +306,7 @@ class AirPlayProtocol(
|
||||
System.arraycopy(ed25519KeyPair.second, 0, verifyData, 96, 32)
|
||||
|
||||
val verified = ed25519Verify(
|
||||
pairingState.clientEdPublicKey, verifyData, clientSignature
|
||||
pairingState.clientEdPublicKey!!, verifyData, clientSignature
|
||||
)
|
||||
|
||||
if (verified) {
|
||||
@@ -375,14 +376,14 @@ class AirPlayProtocol(
|
||||
|
||||
// Parse binary plist from SETUP body
|
||||
try {
|
||||
val plist = PropertyListParser.parse(data.inputStream()) as? NSDictionary ?: run {
|
||||
val plist = PropertyListParser.parse(data) as? NSDictionary ?: run {
|
||||
Log.e(TAG, "SETUP body 不是有效的 plist")
|
||||
sendResponse(output, 400, "Bad Request")
|
||||
return
|
||||
}
|
||||
|
||||
val streams = plist["streams"] as? NSArray
|
||||
val streamDict = streams?.array?.firstOrNull() as? NSDictionary
|
||||
val streamDict = streams?.getArray()?.firstOrNull() as? NSDictionary
|
||||
|
||||
if (streamDict != null) {
|
||||
val streamType = (streamDict["type"] as? NSNumber)?.longValue()?.toInt()
|
||||
@@ -399,7 +400,7 @@ class AirPlayProtocol(
|
||||
deriveStreamKeys(streamConnectionId)
|
||||
|
||||
val responsePlist = NSDictionary()
|
||||
responsePlist.put("dataPort", NSNumber(streamState?.videoDataPort ?: 7001L))
|
||||
responsePlist.put("dataPort", NSNumber((streamState?.videoDataPort ?: 7001).toLong()))
|
||||
|
||||
val responseBytes = BinaryPropertyListWriter.writeToArray(responsePlist)
|
||||
sendResponse(output, 200, "OK", responseBytes, "application/octet-stream")
|
||||
@@ -412,8 +413,8 @@ class AirPlayProtocol(
|
||||
Log.w(TAG, "AirPlay 音频格式: $audioFormat")
|
||||
|
||||
val responsePlist = NSDictionary()
|
||||
responsePlist.put("dataPort", NSNumber(streamState?.audioDataPort ?: 7002L))
|
||||
responsePlist.put("controlPort", NSNumber(streamState?.audioControlPort ?: 6001L))
|
||||
responsePlist.put("dataPort", NSNumber((streamState?.audioDataPort ?: 7002).toLong()))
|
||||
responsePlist.put("controlPort", NSNumber((streamState?.audioControlPort ?: 6001).toLong()))
|
||||
|
||||
val responseBytes = BinaryPropertyListWriter.writeToArray(responsePlist)
|
||||
sendResponse(output, 200, "OK", responseBytes, "application/octet-stream")
|
||||
@@ -432,9 +433,11 @@ class AirPlayProtocol(
|
||||
val eventPort = (plist["eventPort"] as? NSNumber)?.longValue()?.toInt()
|
||||
?: 6002
|
||||
|
||||
val localSessionId = (plist["sessionID"] as? NSNumber)?.longValue()
|
||||
?: System.nanoTime()
|
||||
|
||||
streamState = StreamState(
|
||||
val localSessionId = (plist["sessionID"] as? NSNumber)?.longValue()
|
||||
?: System.nanoTime(),
|
||||
localSessionId = localSessionId,
|
||||
videoDataPort = 7001,
|
||||
audioDataPort = 7002,
|
||||
audioControlPort = 6001,
|
||||
@@ -512,21 +515,13 @@ class AirPlayProtocol(
|
||||
val encryptedPayload = data.copyOfRange(4, minOf(4 + payloadSize, data.size))
|
||||
|
||||
try {
|
||||
val decrypted: ByteArray
|
||||
when (payloadType) {
|
||||
0 -> {
|
||||
// Encrypted video data — decrypt with AES-CTR
|
||||
decrypted = decryptVideoAesCtr(encryptedPayload)
|
||||
?: continue
|
||||
}
|
||||
val decrypted: ByteArray = when (payloadType) {
|
||||
0 -> decryptVideoAesCtr(encryptedPayload) ?: continue
|
||||
1 -> {
|
||||
// SPS/PPS data (not encrypted, contains codec config)
|
||||
decrypted = processSpsPps(encryptedPayload)
|
||||
if (decrypted.isEmpty()) continue
|
||||
}
|
||||
else -> {
|
||||
decrypted = encryptedPayload
|
||||
val sps = processSpsPps(encryptedPayload)
|
||||
if (sps.isEmpty()) continue else sps
|
||||
}
|
||||
else -> encryptedPayload
|
||||
}
|
||||
|
||||
val isKeyFrame = payloadType == 1 || isKeyFrameNAL(decrypted)
|
||||
@@ -604,11 +599,14 @@ class AirPlayProtocol(
|
||||
val privateKey = ByteArray(32)
|
||||
SecureRandom().nextBytes(privateKey)
|
||||
// Clamp the private key as per Curve25519 spec
|
||||
privateKey[0] = (privateKey[0] and 0xF8).toByte()
|
||||
privateKey[31] = ((privateKey[31] and 0x7F) or 0x40).toByte()
|
||||
privateKey[0] = (privateKey[0].toInt() and 0xF8).toByte()
|
||||
privateKey[31] = ((privateKey[31].toInt() and 0x7F) or 0x40).toByte()
|
||||
|
||||
val publicKey = ByteArray(32)
|
||||
org.bouncycastle.math.ec.rfc7748.X25519.scalarMult(privateKey, 0, publicKey, 0)
|
||||
// Base point for Curve25519 (9, 0, 0, ..., 0)
|
||||
val basePoint = ByteArray(32)
|
||||
basePoint[0] = 9
|
||||
org.bouncycastle.math.ec.rfc7748.X25519.scalarMult(privateKey, 0, basePoint, 0, publicKey, 0)
|
||||
|
||||
return Pair(privateKey, publicKey)
|
||||
}
|
||||
@@ -657,8 +655,8 @@ class AirPlayProtocol(
|
||||
val videoKey = ByteArray(16)
|
||||
val videoIv = ByteArray(16)
|
||||
for (i in 0 until 16) {
|
||||
videoKey[i] = (keyHash[i] xor eaesKey[i]).toByte()
|
||||
videoIv[i] = (ivHash[i] xor eaesKey[i]).toByte()
|
||||
videoKey[i] = ((keyHash[i].toInt() xor eaesKey[i].toInt()) and 0xFF).toByte()
|
||||
videoIv[i] = ((ivHash[i].toInt() xor eaesKey[i].toInt()) and 0xFF).toByte()
|
||||
}
|
||||
|
||||
videoAesKey = SecretKeySpec(videoKey, "AES")
|
||||
@@ -737,7 +735,7 @@ class AirPlayProtocol(
|
||||
System.arraycopy(hash, 0, response, 6, minOf(32, 136))
|
||||
// Pad remaining with streamConnectionId-derived bytes
|
||||
for (i in 38 until 142) {
|
||||
response[i] = (hash[i % hash.size] xor data[0]).toByte()
|
||||
response[i] = ((hash[i % hash.size].toInt() xor data[0].toInt()) and 0xFF).toByte()
|
||||
}
|
||||
|
||||
return response
|
||||
|
||||
Reference in New Issue
Block a user