╭ changed ─────────────────╮ │src/serve_ssh_protocol.zig│ ╰──────────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │591 try sendChannelMessage(conn, self.channel, SSH_MSG_CHANNEL_CLOSE);│ │592 }, │ │593 SSH_MSG_GLOBAL_REQUEST => try handleGlobalRequest(conn, packet), │ │594 + // refused, so a multiplexing client doesn't wait on it │ │595 + SSH_MSG_CHANNEL_OPEN => _ = try handleChannelOpen(conn, packet, true),│ │596 // RFC 4252 §5.1: silently ignored once authenticated │ │597 SSH_MSG_USERAUTH_REQUEST => {}, │ │598 else => try sendUnimplemented(conn), │ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │1819 if (!std.mem.eql(u8, pk_algo, "ssh-ed25519")) return false; │ │1820 const raw_pubkey = try takeString(&pubkey_reader, 64); │ │1821 if (raw_pubkey.len != Ed25519.PublicKey.encoded_length) return false; │ │1822 + // the fingerprint hashes the whole blob, so it must hold nothing else│ │1823 + if (pubkey_reader.bufferedLen() != 0) return false; │ │1824 │ │1825 // parse signature_blob: string "ssh-ed25519" || string raw_signature │ │1826 var sig_reader = std.Io.Reader.fixed(signature_blob); │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1844 // parse pubkey_blob: string "ssh-rsa" || mpint e || mpint n │ │1845 var pubkey_reader = std.Io.Reader.fixed(pubkey_blob); │ │1846 if (!std.mem.eql(u8, try takeString(&pubkey_reader, 64), "ssh-rsa")) return false; │ │1843 - const e = std.mem.trimStart(u8, try takeString(&pubkey_reader, 1024), &.{0}); │ │1844 - const n = std.mem.trimStart(u8, try takeString(&pubkey_reader, 1024), &.{0}); │ │1847 + // the fingerprint hashes the whole blob, so one key must have one encoding │ │1848 + const e = canonicalMpintMagnitude(try takeString(&pubkey_reader, 1024)) orelse return false;│ │1849 + const n = canonicalMpintMagnitude(try takeString(&pubkey_reader, 1024)) orelse return false;│ │1850 + if (pubkey_reader.bufferedLen() != 0) return false; │ │1851 │ │1852 // parse signature_blob: string algo || string sig │ │1853 var sig_reader = std.Io.Reader.fixed(signature_blob); │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │1865 } │ │1866 } │ │1867 │ │1868 +// the unsigned bytes of a positive mpint in its shortest encoding (RFC 4251 │ │1869 +// §5), or null for zero, negative, or padded encodings │ │1870 +fn canonicalMpintMagnitude(bytes: []const u8) ?[]const u8 { │ │1871 + if (bytes.len == 0 or bytes[0] & 0x80 != 0) return null; │ │1872 + if (bytes[0] != 0) return bytes; │ │1873 + if (bytes.len < 2 or bytes[1] & 0x80 == 0) return null; │ │1874 + return bytes[1..]; │ │1875 +} │ │1876 + │ │1877 // ---------------------------------------------------------------------------│ │1878 // connection layer (RFC 4254): channels, requests, data, flow control │ │1879 // ---------------------------------------------------------------------------│ ╰────────────────────────────────────────────────────────────────────────────────────╯