mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
Move password-packet padding into the BPP module.
Now when we construct a packet containing sensitive data, we just set a field saying '... and make it take up at least this much space, to disguise its true size', and nothing in the rest of the system worries about that flag until ssh2bpp.c acts on it. Also, I've changed the strategy for doing the padding. Previously, we were following the real packet with an SSH_MSG_IGNORE to make up the size. But that was only a partial defence: it works OK against passive traffic analysis, but an attacker proxying the TCP stream and dribbling it out one byte at a time could still have found out the size of the real packet by noting when the dribbled data provoked a response. Now I put the SSH_MSG_IGNORE _first_, which should defeat that attack. But that in turn doesn't work when we're doing compression, because we can't predict the compressed sizes accurately enough to make that strategy sensible. Fortunately, compression provides an alternative strategy anyway: if we've got zlib turned on when we send one of these sensitive packets, then we can pad out the compressed zlib data as much as we like by adding empty RFC1951 blocks (effectively chaining ZLIB_PARTIAL_FLUSHes). So both strategies should now be dribble-proof.
This commit is contained in:
parent
bcb94f966e
commit
20a9bd5642
87
ssh.c
87
ssh.c
@ -342,7 +342,8 @@ static void ssh_comp_none_cleanup(void *handle)
|
||||
{
|
||||
}
|
||||
static void ssh_comp_none_block(void *handle, unsigned char *block, int len,
|
||||
unsigned char **outblock, int *outlen)
|
||||
unsigned char **outblock, int *outlen,
|
||||
int minlen)
|
||||
{
|
||||
}
|
||||
static int ssh_decomp_none_block(void *handle, unsigned char *block, int len,
|
||||
@ -350,15 +351,11 @@ static int ssh_decomp_none_block(void *handle, unsigned char *block, int len,
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static int ssh_comp_none_disable(void *handle)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
const static struct ssh_compress ssh_comp_none = {
|
||||
"none", NULL,
|
||||
ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block,
|
||||
ssh_comp_none_init, ssh_comp_none_cleanup, ssh_decomp_none_block,
|
||||
ssh_comp_none_disable, NULL
|
||||
NULL
|
||||
};
|
||||
extern const struct ssh_compress ssh_zlib;
|
||||
const static struct ssh_compress *const compressions[] = {
|
||||
@ -1327,72 +1324,6 @@ static void ssh_send_outgoing_data(void *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a packet whose length needs to be disguised (typically
|
||||
* passwords or keyboard-interactive responses).
|
||||
*/
|
||||
static void ssh2_pkt_send_with_padding(Ssh ssh, PktOut *pkt, int padsize)
|
||||
{
|
||||
#if 0
|
||||
if (0) {
|
||||
/*
|
||||
* The simplest way to do this is to adjust the
|
||||
* variable-length padding field in the outgoing packet.
|
||||
*
|
||||
* Currently compiled out, because some Cisco SSH servers
|
||||
* don't like excessively padded packets (bah, why's it
|
||||
* always Cisco?)
|
||||
*/
|
||||
pkt->forcepad = padsize;
|
||||
ssh2_pkt_send(ssh, pkt);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/*
|
||||
* If we can't do that, however, an alternative approach is to
|
||||
* bundle the packet tightly together with an SSH_MSG_IGNORE
|
||||
* such that their combined length is a constant. So first we
|
||||
* construct the final form of this packet and append it to
|
||||
* the outgoing_data bufchain...
|
||||
*/
|
||||
ssh_pkt_write(ssh, pkt);
|
||||
|
||||
/*
|
||||
* ... but before we return from this function (triggering a
|
||||
* call to the outgoing_data_sender), we also construct an
|
||||
* SSH_MSG_IGNORE which includes a string that's an exact
|
||||
* multiple of the cipher block size. (If the cipher is NULL
|
||||
* so that the block size is unavailable, we don't do this
|
||||
* trick at all, because we gain nothing by it.)
|
||||
*/
|
||||
if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
|
||||
int stringlen, i;
|
||||
|
||||
stringlen = (256 - bufchain_size(&ssh->outgoing_data));
|
||||
stringlen += ssh->v2_out_cipherblksize - 1;
|
||||
stringlen -= (stringlen % ssh->v2_out_cipherblksize);
|
||||
|
||||
/*
|
||||
* Temporarily disable actual compression, so we can
|
||||
* guarantee to get this string exactly the length we want
|
||||
* it. The compression-disabling routine should return an
|
||||
* integer indicating how many bytes we should adjust our
|
||||
* string length by.
|
||||
*/
|
||||
stringlen -= ssh2_bpp_temporarily_disable_compression(ssh->bpp);
|
||||
|
||||
pkt = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_IGNORE);
|
||||
{
|
||||
strbuf *substr = strbuf_new();
|
||||
for (i = 0; i < stringlen; i++)
|
||||
put_byte(substr, random_byte());
|
||||
put_stringsb(pkt, substr);
|
||||
}
|
||||
ssh_pkt_write(ssh, pkt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Send all queued SSH-2 packets.
|
||||
*/
|
||||
@ -9563,7 +9494,8 @@ static void do_ssh2_userauth(void *vctx)
|
||||
}
|
||||
|
||||
/*
|
||||
* Send the response(s) to the server.
|
||||
* Send the response(s) to the server, padding
|
||||
* them to disguise their true length.
|
||||
*/
|
||||
s->pktout = ssh_bpp_new_pktout(
|
||||
ssh->bpp, SSH2_MSG_USERAUTH_INFO_RESPONSE);
|
||||
@ -9572,7 +9504,8 @@ static void do_ssh2_userauth(void *vctx)
|
||||
put_stringz(s->pktout,
|
||||
s->cur_prompt->prompts[i]->result);
|
||||
}
|
||||
ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
|
||||
s->pktout->minlen = 256;
|
||||
ssh2_pkt_send(ssh, s->pktout);
|
||||
|
||||
/*
|
||||
* Free the prompts structure from this iteration.
|
||||
@ -9661,7 +9594,8 @@ static void do_ssh2_userauth(void *vctx)
|
||||
put_stringz(s->pktout, "password");
|
||||
put_bool(s->pktout, FALSE);
|
||||
put_stringz(s->pktout, s->password);
|
||||
ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
|
||||
s->pktout->minlen = 256;
|
||||
ssh2_pkt_send(ssh, s->pktout);
|
||||
logevent("Sent password");
|
||||
s->type = AUTH_TYPE_PASSWORD;
|
||||
|
||||
@ -9797,7 +9731,8 @@ static void do_ssh2_userauth(void *vctx)
|
||||
put_stringz(s->pktout,
|
||||
s->cur_prompt->prompts[1]->result);
|
||||
free_prompts(s->cur_prompt);
|
||||
ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
|
||||
s->pktout->minlen = 256;
|
||||
ssh2_pkt_send(ssh, s->pktout);
|
||||
logevent("Sent new password");
|
||||
|
||||
/*
|
||||
|
8
ssh.h
8
ssh.h
@ -81,7 +81,7 @@ typedef struct PktOut {
|
||||
long prefix; /* bytes up to and including type field */
|
||||
long length; /* total bytes, including prefix */
|
||||
int type;
|
||||
long forcepad; /* SSH-2: force padding to at least this length */
|
||||
long minlen; /* SSH-2: ensure wire length is at least this */
|
||||
unsigned char *data; /* allocated storage */
|
||||
long maxlen; /* amount of storage allocated for `data' */
|
||||
long encrypted_len; /* for SSH-2 total-size counting */
|
||||
@ -561,12 +561,12 @@ struct ssh_compress {
|
||||
void *(*compress_init) (void);
|
||||
void (*compress_cleanup) (void *);
|
||||
void (*compress) (void *, unsigned char *block, int len,
|
||||
unsigned char **outblock, int *outlen);
|
||||
unsigned char **outblock, int *outlen,
|
||||
int minlen);
|
||||
void *(*decompress_init) (void);
|
||||
void (*decompress_cleanup) (void *);
|
||||
int (*decompress) (void *, unsigned char *block, int len,
|
||||
unsigned char **outblock, int *outlen);
|
||||
int (*disable_compression) (void *);
|
||||
const char *text_name;
|
||||
};
|
||||
|
||||
@ -982,7 +982,7 @@ void zlib_compress_cleanup(void *);
|
||||
void *zlib_decompress_init(void);
|
||||
void zlib_decompress_cleanup(void *);
|
||||
void zlib_compress_block(void *, unsigned char *block, int len,
|
||||
unsigned char **outblock, int *outlen);
|
||||
unsigned char **outblock, int *outlen, int minlen);
|
||||
int zlib_decompress_block(void *, unsigned char *block, int len,
|
||||
unsigned char **outblock, int *outlen);
|
||||
|
||||
|
@ -251,7 +251,7 @@ static void ssh1_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
|
||||
unsigned char *compblk;
|
||||
int complen;
|
||||
zlib_compress_block(s->compctx, pkt->data + 12, pkt->length - 12,
|
||||
&compblk, &complen);
|
||||
&compblk, &complen, 0);
|
||||
/* Replace the uncompressed packet data with the compressed
|
||||
* version. */
|
||||
pkt->length = 12;
|
||||
|
108
ssh2bpp.c
108
ssh2bpp.c
@ -496,32 +496,19 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
|
||||
crFinishV;
|
||||
}
|
||||
|
||||
int ssh2_bpp_temporarily_disable_compression(BinaryPacketProtocol *bpp)
|
||||
{
|
||||
struct ssh2_bpp_state *s;
|
||||
assert(bpp->vt == &ssh2_bpp_vtable);
|
||||
s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
|
||||
|
||||
if (!s->out.comp || !s->out.comp_ctx)
|
||||
return 0;
|
||||
|
||||
return s->out.comp->disable_compression(s->out.comp_ctx);
|
||||
}
|
||||
|
||||
static PktOut *ssh2_bpp_new_pktout(int pkt_type)
|
||||
{
|
||||
PktOut *pkt = ssh_new_packet();
|
||||
pkt->length = 5; /* space for packet length + padding length */
|
||||
pkt->forcepad = 0;
|
||||
pkt->minlen = 0;
|
||||
pkt->type = pkt_type;
|
||||
put_byte(pkt, pkt_type);
|
||||
pkt->prefix = pkt->length;
|
||||
return pkt;
|
||||
}
|
||||
|
||||
static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
|
||||
static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
|
||||
{
|
||||
struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
|
||||
int origlen, cipherblk, maclen, padding, unencrypted_prefix, i;
|
||||
|
||||
if (s->bpp.logctx) {
|
||||
@ -537,14 +524,29 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
|
||||
pkt->downstream_id, pkt->additional_log_text);
|
||||
}
|
||||
|
||||
cipherblk = s->out.cipher ? s->out.cipher->blksize : 8;
|
||||
cipherblk = cipherblk < 8 ? 8 : cipherblk; /* or 8 if blksize < 8 */
|
||||
|
||||
if (s->out.comp && s->out.comp_ctx) {
|
||||
unsigned char *newpayload;
|
||||
int newlen;
|
||||
int minlen, newlen;
|
||||
|
||||
/*
|
||||
* Compress packet payload.
|
||||
*/
|
||||
minlen = pkt->minlen;
|
||||
if (minlen) {
|
||||
/*
|
||||
* Work out how much compressed data we need (at least) to
|
||||
* make the overall packet length come to pkt->minlen.
|
||||
*/
|
||||
if (s->out.mac)
|
||||
minlen -= s->out.mac->len;
|
||||
minlen -= 8; /* length field + min padding */
|
||||
}
|
||||
|
||||
s->out.comp->compress(s->out.comp_ctx, pkt->data + 5, pkt->length - 5,
|
||||
&newpayload, &newlen);
|
||||
&newpayload, &newlen, minlen);
|
||||
pkt->length = 5;
|
||||
put_data(pkt, newpayload, newlen);
|
||||
sfree(newpayload);
|
||||
@ -556,12 +558,8 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
|
||||
* If pkt->forcepad is set, make sure the packet is at least that size
|
||||
* after padding.
|
||||
*/
|
||||
cipherblk = s->out.cipher ? s->out.cipher->blksize : 8;
|
||||
cipherblk = cipherblk < 8 ? 8 : cipherblk; /* or 8 if blksize < 8 */
|
||||
padding = 4;
|
||||
unencrypted_prefix = (s->out.mac && s->out.etm_mode) ? 4 : 0;
|
||||
if (pkt->length + padding < pkt->forcepad)
|
||||
padding = pkt->forcepad - pkt->length;
|
||||
padding +=
|
||||
(cipherblk - (pkt->length - unencrypted_prefix + padding) % cipherblk)
|
||||
% cipherblk;
|
||||
@ -607,6 +605,74 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
|
||||
s->out.sequence++; /* whether or not we MACed */
|
||||
pkt->encrypted_len = origlen + padding;
|
||||
|
||||
}
|
||||
|
||||
static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt)
|
||||
{
|
||||
struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp);
|
||||
|
||||
if (pkt->minlen > 0 && !(s->out.comp && s->out.comp_ctx)) {
|
||||
/*
|
||||
* If we've been told to pad the packet out to a given minimum
|
||||
* length, but we're not compressing (and hence can't get the
|
||||
* compression to do the padding by pointlessly opening and
|
||||
* closing zlib blocks), then our other strategy is to precede
|
||||
* this message with an SSH_MSG_IGNORE that makes it up to the
|
||||
* right length.
|
||||
*
|
||||
* A third option in principle, and the most obviously
|
||||
* sensible, would be to set the explicit padding field in the
|
||||
* packet to more than its minimum value. Sadly, that turns
|
||||
* out to break some servers (our institutional memory thinks
|
||||
* Cisco in particular) and so we abandoned that idea shortly
|
||||
* after trying it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Calculate the length we expect the real packet to have.
|
||||
*/
|
||||
int block, length;
|
||||
PktOut *ignore_pkt;
|
||||
|
||||
block = s->out.cipher ? s->out.cipher->blksize : 0;
|
||||
if (block < 8)
|
||||
block = 8;
|
||||
length = pkt->length;
|
||||
length += 4; /* minimum 4 byte padding */
|
||||
length += block-1;
|
||||
length -= (length % block);
|
||||
if (s->out.mac)
|
||||
length += s->out.mac->len;
|
||||
|
||||
if (length < pkt->minlen) {
|
||||
/*
|
||||
* We need an ignore message. Calculate its length.
|
||||
*/
|
||||
length = pkt->minlen - length;
|
||||
|
||||
/*
|
||||
* And work backwards from that to the length of the
|
||||
* contained string.
|
||||
*/
|
||||
if (s->out.mac)
|
||||
length -= s->out.mac->len;
|
||||
length -= 8; /* length field + min padding */
|
||||
length -= 5; /* type code + string length prefix */
|
||||
|
||||
if (length < 0)
|
||||
length = 0;
|
||||
|
||||
ignore_pkt = ssh2_bpp_new_pktout(SSH2_MSG_IGNORE);
|
||||
put_uint32(ignore_pkt, length);
|
||||
while (length-- > 0)
|
||||
put_byte(ignore_pkt, random_byte());
|
||||
ssh2_bpp_format_packet_inner(s, ignore_pkt);
|
||||
bufchain_add(s->bpp.out_raw, ignore_pkt->data, ignore_pkt->length);
|
||||
ssh_free_pktout(ignore_pkt);
|
||||
}
|
||||
}
|
||||
|
||||
ssh2_bpp_format_packet_inner(s, pkt);
|
||||
bufchain_add(s->bpp.out_raw, pkt->data, pkt->length);
|
||||
|
||||
ssh_free_pktout(pkt);
|
||||
|
1
sshbpp.h
1
sshbpp.h
@ -47,7 +47,6 @@ void ssh2_bpp_new_incoming_crypto(
|
||||
const struct ssh2_cipher *cipher, const void *ckey, const void *iv,
|
||||
const struct ssh_mac *mac, int etm_mode, const void *mac_key,
|
||||
const struct ssh_compress *compression);
|
||||
int ssh2_bpp_temporarily_disable_compression(BinaryPacketProtocol *bpp);
|
||||
|
||||
BinaryPacketProtocol *ssh2_bare_bpp_new(void);
|
||||
|
||||
|
196
sshzlib.c
196
sshzlib.c
@ -374,7 +374,6 @@ struct Outbuf {
|
||||
unsigned long outbits;
|
||||
int noutbits;
|
||||
int firstblock;
|
||||
int comp_disabled;
|
||||
};
|
||||
|
||||
static void outbits(struct Outbuf *out, unsigned long bits, int nbits)
|
||||
@ -502,14 +501,6 @@ static void zlib_literal(struct LZ77Context *ectx, unsigned char c)
|
||||
{
|
||||
struct Outbuf *out = (struct Outbuf *) ectx->userdata;
|
||||
|
||||
if (out->comp_disabled) {
|
||||
/*
|
||||
* We're in an uncompressed block, so just output the byte.
|
||||
*/
|
||||
outbits(out, c, 8);
|
||||
return;
|
||||
}
|
||||
|
||||
if (c <= 143) {
|
||||
/* 0 through 143 are 8 bits long starting at 00110000. */
|
||||
outbits(out, mirrorbytes[0x30 + c], 8);
|
||||
@ -525,8 +516,6 @@ static void zlib_match(struct LZ77Context *ectx, int distance, int len)
|
||||
int i, j, k;
|
||||
struct Outbuf *out = (struct Outbuf *) ectx->userdata;
|
||||
|
||||
assert(!out->comp_disabled);
|
||||
|
||||
while (len > 0) {
|
||||
int thislen;
|
||||
|
||||
@ -623,7 +612,6 @@ void *zlib_compress_init(void)
|
||||
out = snew(struct Outbuf);
|
||||
out->outbits = out->noutbits = 0;
|
||||
out->firstblock = 1;
|
||||
out->comp_disabled = FALSE;
|
||||
ectx->userdata = out;
|
||||
|
||||
return ectx;
|
||||
@ -637,50 +625,9 @@ void zlib_compress_cleanup(void *handle)
|
||||
sfree(ectx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn off actual LZ77 analysis for one block, to facilitate
|
||||
* construction of a precise-length IGNORE packet. Returns the
|
||||
* length adjustment (which is only valid for packets < 65536
|
||||
* bytes, but that seems reasonable enough).
|
||||
*/
|
||||
static int zlib_disable_compression(void *handle)
|
||||
{
|
||||
struct LZ77Context *ectx = (struct LZ77Context *)handle;
|
||||
struct Outbuf *out = (struct Outbuf *) ectx->userdata;
|
||||
int n;
|
||||
|
||||
out->comp_disabled = TRUE;
|
||||
|
||||
n = 0;
|
||||
/*
|
||||
* If this is the first block, we will start by outputting two
|
||||
* header bytes, and then three bits to begin an uncompressed
|
||||
* block. This will cost three bytes (because we will start on
|
||||
* a byte boundary, this is certain).
|
||||
*/
|
||||
if (out->firstblock) {
|
||||
n = 3;
|
||||
} else {
|
||||
/*
|
||||
* Otherwise, we will output seven bits to close the
|
||||
* previous static block, and _then_ three bits to begin an
|
||||
* uncompressed block, and then flush the current byte.
|
||||
* This may cost two bytes or three, depending on noutbits.
|
||||
*/
|
||||
n += (out->noutbits + 10) / 8;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we output four bytes for the length / ~length pair in
|
||||
* the uncompressed block.
|
||||
*/
|
||||
n += 4;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void zlib_compress_block(void *handle, unsigned char *block, int len,
|
||||
unsigned char **outblock, int *outlen)
|
||||
unsigned char **outblock, int *outlen,
|
||||
int minlen)
|
||||
{
|
||||
struct LZ77Context *ectx = (struct LZ77Context *)handle;
|
||||
struct Outbuf *out = (struct Outbuf *) ectx->userdata;
|
||||
@ -702,97 +649,57 @@ void zlib_compress_block(void *handle, unsigned char *block, int len,
|
||||
} else
|
||||
in_block = TRUE;
|
||||
|
||||
if (out->comp_disabled) {
|
||||
if (in_block)
|
||||
outbits(out, 0, 7); /* close static block */
|
||||
|
||||
while (len > 0) {
|
||||
int blen = (len < 65535 ? len : 65535);
|
||||
|
||||
/*
|
||||
* Start a Deflate (RFC1951) uncompressed block. We
|
||||
* transmit a zero bit (BFINAL=0), followed by two more
|
||||
* zero bits (BTYPE=00). Of course these are in the
|
||||
* wrong order (00 0), not that it matters.
|
||||
*/
|
||||
outbits(out, 0, 3);
|
||||
|
||||
/*
|
||||
* Output zero bits to align to a byte boundary.
|
||||
*/
|
||||
if (out->noutbits)
|
||||
outbits(out, 0, 8 - out->noutbits);
|
||||
|
||||
/*
|
||||
* Output the block length, and then its one's
|
||||
* complement. They're little-endian, so all we need to
|
||||
* do is pass them straight to outbits() with bit count
|
||||
* 16.
|
||||
*/
|
||||
outbits(out, blen, 16);
|
||||
outbits(out, blen ^ 0xFFFF, 16);
|
||||
|
||||
/*
|
||||
* Do the `compression': we need to pass the data to
|
||||
* lz77_compress so that it will be taken into account
|
||||
* for subsequent (distance,length) pairs. But
|
||||
* lz77_compress is passed FALSE, which means it won't
|
||||
* actually find (or even look for) any matches; so
|
||||
* every character will be passed straight to
|
||||
* zlib_literal which will spot out->comp_disabled and
|
||||
* emit in the uncompressed format.
|
||||
*/
|
||||
lz77_compress(ectx, block, blen, FALSE);
|
||||
|
||||
len -= blen;
|
||||
block += blen;
|
||||
}
|
||||
outbits(out, 2, 3); /* open new block */
|
||||
} else {
|
||||
if (!in_block) {
|
||||
/*
|
||||
* Start a Deflate (RFC1951) fixed-trees block. We
|
||||
* transmit a zero bit (BFINAL=0), followed by a zero
|
||||
* bit and a one bit (BTYPE=01). Of course these are in
|
||||
* the wrong order (01 0).
|
||||
*/
|
||||
outbits(out, 2, 3);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the compression.
|
||||
*/
|
||||
lz77_compress(ectx, block, len, TRUE);
|
||||
|
||||
/*
|
||||
* End the block (by transmitting code 256, which is
|
||||
* 0000000 in fixed-tree mode), and transmit some empty
|
||||
* blocks to ensure we have emitted the byte containing the
|
||||
* last piece of genuine data. There are three ways we can
|
||||
* do this:
|
||||
*
|
||||
* - Minimal flush. Output end-of-block and then open a
|
||||
* new static block. This takes 9 bits, which is
|
||||
* guaranteed to flush out the last genuine code in the
|
||||
* closed block; but allegedly zlib can't handle it.
|
||||
*
|
||||
* - Zlib partial flush. Output EOB, open and close an
|
||||
* empty static block, and _then_ open the new block.
|
||||
* This is the best zlib can handle.
|
||||
*
|
||||
* - Zlib sync flush. Output EOB, then an empty
|
||||
* _uncompressed_ block (000, then sync to byte
|
||||
* boundary, then send bytes 00 00 FF FF). Then open the
|
||||
* new block.
|
||||
*
|
||||
* For the moment, we will use Zlib partial flush.
|
||||
*/
|
||||
outbits(out, 0, 7); /* close block */
|
||||
outbits(out, 2, 3 + 7); /* empty static block */
|
||||
outbits(out, 2, 3); /* open new block */
|
||||
if (!in_block) {
|
||||
/*
|
||||
* Start a Deflate (RFC1951) fixed-trees block. We
|
||||
* transmit a zero bit (BFINAL=0), followed by a zero
|
||||
* bit and a one bit (BTYPE=01). Of course these are in
|
||||
* the wrong order (01 0).
|
||||
*/
|
||||
outbits(out, 2, 3);
|
||||
}
|
||||
|
||||
out->comp_disabled = FALSE;
|
||||
/*
|
||||
* Do the compression.
|
||||
*/
|
||||
lz77_compress(ectx, block, len, TRUE);
|
||||
|
||||
/*
|
||||
* End the block (by transmitting code 256, which is
|
||||
* 0000000 in fixed-tree mode), and transmit some empty
|
||||
* blocks to ensure we have emitted the byte containing the
|
||||
* last piece of genuine data. There are three ways we can
|
||||
* do this:
|
||||
*
|
||||
* - Minimal flush. Output end-of-block and then open a
|
||||
* new static block. This takes 9 bits, which is
|
||||
* guaranteed to flush out the last genuine code in the
|
||||
* closed block; but allegedly zlib can't handle it.
|
||||
*
|
||||
* - Zlib partial flush. Output EOB, open and close an
|
||||
* empty static block, and _then_ open the new block.
|
||||
* This is the best zlib can handle.
|
||||
*
|
||||
* - Zlib sync flush. Output EOB, then an empty
|
||||
* _uncompressed_ block (000, then sync to byte
|
||||
* boundary, then send bytes 00 00 FF FF). Then open the
|
||||
* new block.
|
||||
*
|
||||
* For the moment, we will use Zlib partial flush.
|
||||
*/
|
||||
outbits(out, 0, 7); /* close block */
|
||||
outbits(out, 2, 3 + 7); /* empty static block */
|
||||
outbits(out, 2, 3); /* open new block */
|
||||
|
||||
/*
|
||||
* If we've been asked to pad out the compressed data until it's
|
||||
* at least a given length, do so by emitting further empty static
|
||||
* blocks.
|
||||
*/
|
||||
while (out->outlen < minlen) {
|
||||
outbits(out, 0, 7); /* close block */
|
||||
outbits(out, 2, 3); /* open new static block */
|
||||
}
|
||||
|
||||
*outblock = out->outbuf;
|
||||
*outlen = out->outlen;
|
||||
@ -1391,7 +1298,6 @@ const struct ssh_compress ssh_zlib = {
|
||||
zlib_decompress_init,
|
||||
zlib_decompress_cleanup,
|
||||
zlib_decompress_block,
|
||||
zlib_disable_compression,
|
||||
"zlib (RFC1950)"
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user