- TXCore/SSH: SSHWire(string/mpint 编码)、ECDSAConv(DER→SSH 签名/P256 blob)、 HostKey(opensshFingerprint/evaluate/HostTriple/KnownHostsStore),+10 单测(含真实指纹向量、高位 DER) - TXTransport: SSHConfig.hostKeyVerifier 闭包 + SSHError.hostKeyMismatch; SSHSession 握手后 libssh2_session_hostkey 取 blob 交 verifier,拒绝则断开抛错 - app: 文件后端 KnownHostsStore(未签名 app 无 keychain 权限 SecItem -34018,host key 是 公钥非机密,沙盒文件对 TOFU 足够)+ TOFU verifier + firstUse 横幅 + mismatch alert(信任/取消) - 验证(192.168.9.199):首次信任 pin+接受连上;假 pin→不符→断开+告警,本次指纹与 ssh-keygen 逐字符一致 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
61 lines
2.8 KiB
Swift
61 lines
2.8 KiB
Swift
import XCTest
|
||
@testable import TXCore
|
||
|
||
final class SSHWireTests: XCTestCase {
|
||
func testStringEncoding() {
|
||
XCTAssertEqual(SSHWire.string([0xAA, 0xBB]), [0, 0, 0, 2, 0xAA, 0xBB])
|
||
XCTAssertEqual(SSHWire.string(""), [0, 0, 0, 0])
|
||
XCTAssertEqual(SSHWire.string("nistp256"), [0, 0, 0, 8] + Array("nistp256".utf8))
|
||
}
|
||
|
||
func testMpint() {
|
||
// 普通正数(高位为 0):原样。
|
||
XCTAssertEqual(SSHWire.mpint([0x01, 0x02]), [0, 0, 0, 2, 0x01, 0x02])
|
||
// 高位为 1:前补 0x00(否则被当负数)。
|
||
XCTAssertEqual(SSHWire.mpint([0xFF]), [0, 0, 0, 2, 0x00, 0xFF])
|
||
// 前导零去除。
|
||
XCTAssertEqual(SSHWire.mpint([0x00, 0x00, 0x05]), [0, 0, 0, 1, 0x05])
|
||
// 值为 0 → 空串。
|
||
XCTAssertEqual(SSHWire.mpint([0x00, 0x00]), [0, 0, 0, 0])
|
||
}
|
||
|
||
func testDerToSSHSignatureHighBit() {
|
||
// 构造 DER SEQUENCE{ INTEGER r(高位1,含前导00), INTEGER s(高位0) }。
|
||
let r = [UInt8(0x00), 0xFF] + [UInt8](repeating: 0x11, count: 31) // 33B DER 内容
|
||
let s = [UInt8(0x01)] + [UInt8](repeating: 0x22, count: 31) // 32B DER 内容
|
||
var der: [UInt8] = [0x30, UInt8(2 + r.count + 2 + s.count)]
|
||
der += [0x02, UInt8(r.count)] + r
|
||
der += [0x02, UInt8(s.count)] + s
|
||
|
||
let sig = ECDSAConv.derToSSHSignature(der)
|
||
XCTAssertNotNil(sig)
|
||
// 应等于独立计算的 mpint(r)+mpint(s)。
|
||
let expected = SSHWire.mpint(r) + SSHWire.mpint(s)
|
||
XCTAssertEqual(sig, expected)
|
||
// r 高位为 1 → mpint 33 字节,长度前缀 0x21。
|
||
XCTAssertEqual(Array(sig!.prefix(4)), [0, 0, 0, 0x21])
|
||
}
|
||
|
||
func testP256Blob() {
|
||
let q = [UInt8(0x04)] + [UInt8](repeating: 0xAB, count: 64) // X9.63 65B
|
||
let blob = ECDSAConv.p256PublicKeyBlob(x963: q)
|
||
// 开头必须是 string("ecdsa-sha2-nistp256")。
|
||
XCTAssertEqual(Array(blob.prefix(4)), [0, 0, 0, 0x13])
|
||
XCTAssertEqual(Array(blob[4 ..< 4 + 19]), Array("ecdsa-sha2-nistp256".utf8))
|
||
}
|
||
|
||
/// 真实向量:192.168.9.199 的 ecdsa host key,指纹须与 `ssh-keygen -lf` 一致。
|
||
func testOpenSSHFingerprintRealVector() {
|
||
let b64 = "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCkvFPRkGE8RGQ+scBa3JZMWnZnGUn7BWfsz8rfEtKjIojjqxxJLdZvQE9rUlaNNUFi8eGb9lfr5rO4yFS0dnsU="
|
||
let blob = Data(base64Encoded: b64)!
|
||
XCTAssertEqual(HostKey.opensshFingerprint(blob), "SHA256:dcpjdomtDzOZJuv/NHr1vYtm0mP5EbsEML0kClhgqxg")
|
||
}
|
||
|
||
func testEvaluate() {
|
||
let a = Data([1, 2, 3]); let b = Data([1, 2, 3]); let c = Data([9])
|
||
XCTAssertEqual(HostKey.evaluate(stored: nil, presented: a), .firstUse)
|
||
XCTAssertEqual(HostKey.evaluate(stored: b, presented: a), .trusted)
|
||
XCTAssertEqual(HostKey.evaluate(stored: c, presented: a), .mismatch(stored: c))
|
||
}
|
||
}
|