Skip to content

Commit

Permalink
Merge pull request #214 from h2o/kazuho/precompressed-certificates-fix
Browse files Browse the repository at this point in the history
build two types of pre-compressed certificate chain (OCSP stapling)
  • Loading branch information
kazuho authored Feb 18, 2019
2 parents 4d9c016 + 03048cb commit e278d03
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 21 deletions.
17 changes: 13 additions & 4 deletions include/picotls/certificate_compression.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,23 @@ extern "C" {
typedef struct st_ptls_emit_compressed_certificate_t {
ptls_emit_certificate_t super;
uint16_t algo;
uint32_t uncompressed_length;
ptls_iovec_t buf;
struct st_ptls_compressed_certificate_entry_t {
uint32_t uncompressed_length;
ptls_iovec_t bytes;
} with_ocsp_status, without_ocsp_status;
} ptls_emit_compressed_certificate_t;

extern ptls_decompress_certificate_t ptls_decompress_certificate;

int ptls_init_compressed_certificate(ptls_emit_compressed_certificate_t *ecc, uint16_t algo, ptls_iovec_t *certificates,
size_t num_certificates, ptls_iovec_t ocsp_status);
/**
* initializes a certificate emitter that precompresses a certificate chain (and ocsp status)
*/
int ptls_init_compressed_certificate(ptls_emit_compressed_certificate_t *ecc, ptls_iovec_t *certificates, size_t num_certificates,
ptls_iovec_t ocsp_status);
/**
*
*/
void ptls_dispose_compressed_certificate(ptls_emit_compressed_certificate_t *ecc);

#ifdef __cplusplus
}
Expand Down
62 changes: 48 additions & 14 deletions lib/certificate_compression.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,22 @@ static const uint16_t algorithms[] = {PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_BRO
ptls_decompress_certificate_t ptls_decompress_certificate = {algorithms, decompress_certificate};

static int emit_compressed_certificate(ptls_emit_certificate_t *_self, ptls_t *tls, ptls_message_emitter_t *emitter,
ptls_key_schedule_t *key_sched, ptls_iovec_t context)
ptls_key_schedule_t *key_sched, ptls_iovec_t context, int push_status_request)
{
ptls_emit_compressed_certificate_t *self = (void *)_self;
struct st_ptls_compressed_certificate_entry_t *entry;
int ret;

assert(context.len == 0 || !"precompressed mode can only be used for server certificates");

entry = &self->without_ocsp_status;
if (push_status_request && self->with_ocsp_status.uncompressed_length != 0)
entry = &self->with_ocsp_status;

ptls_push_message(emitter, key_sched, PTLS_HANDSHAKE_TYPE_COMPRESSED_CERTIFICATE, {
ptls_buffer_push16(emitter->buf, self->algo);
ptls_buffer_push24(emitter->buf, self->uncompressed_length);
ptls_buffer_push_block(emitter->buf, 3, { ptls_buffer_pushv(emitter->buf, self->buf.base, self->buf.len); });
ptls_buffer_push16(emitter->buf, PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_BROTLI);
ptls_buffer_push24(emitter->buf, entry->uncompressed_length);
ptls_buffer_push_block(emitter->buf, 3, { ptls_buffer_pushv(emitter->buf, entry->bytes.base, entry->bytes.len); });
});

ret = 0;
Expand All @@ -67,39 +72,68 @@ static int emit_compressed_certificate(ptls_emit_certificate_t *_self, ptls_t *t
return ret;
}

int ptls_init_compressed_certificate(ptls_emit_compressed_certificate_t *self, uint16_t algo, ptls_iovec_t *certificates,
size_t num_certificates, ptls_iovec_t ocsp_status)
static int build_compressed(struct st_ptls_compressed_certificate_entry_t *entry, ptls_iovec_t *certificates,
size_t num_certificates, ptls_iovec_t ocsp_status)
{
ptls_buffer_t uncompressed;
int ret;

*self = (ptls_emit_compressed_certificate_t){{emit_compressed_certificate}, algo};

ptls_buffer_init(&uncompressed, "", 0);

/* build uncompressed */
if ((ret = ptls_build_certificate_message(&uncompressed, ptls_iovec_init(NULL, 0), certificates, num_certificates,
ocsp_status)) != 0)
goto Exit;
self->uncompressed_length = (uint32_t)uncompressed.off;
entry->uncompressed_length = (uint32_t)uncompressed.off;

/* compress */
self->buf.len = uncompressed.off - 1;
if ((self->buf.base = malloc(self->buf.len)) == NULL) {
entry->bytes.len = uncompressed.off - 1;
if ((entry->bytes.base = malloc(entry->bytes.len)) == NULL) {
ret = PTLS_ERROR_NO_MEMORY;
goto Exit;
}
if (BrotliEncoderCompress(BROTLI_MAX_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_MODE_GENERIC, uncompressed.off, uncompressed.base,
&self->buf.len, self->buf.base) != BROTLI_TRUE) {
&entry->bytes.len, entry->bytes.base) != BROTLI_TRUE) {
ret = PTLS_ERROR_COMPRESSION_FAILURE;
goto Exit;
}

ret = 0;

Exit:
if (ret != 0)
free(self->buf.base);
if (ret != 0) {
free(entry->bytes.base);
*entry = (struct st_ptls_compressed_certificate_entry_t){0};
}
ptls_buffer_dispose(&uncompressed);
return ret;
}

int ptls_init_compressed_certificate(ptls_emit_compressed_certificate_t *self, ptls_iovec_t *certificates, size_t num_certificates,
ptls_iovec_t ocsp_status)
{
int ret;

*self = (ptls_emit_compressed_certificate_t){{emit_compressed_certificate}, PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_BROTLI};

/* build entries */
if ((ret = build_compressed(&self->without_ocsp_status, certificates, num_certificates, ptls_iovec_init(NULL, 0))) != 0)
goto Exit;
if (ocsp_status.len != 0) {
if ((ret = build_compressed(&self->with_ocsp_status, certificates, num_certificates, ocsp_status)) != 0)
goto Exit;
}

ret = 0;

Exit:
if (ret != 0)
ptls_dispose_compressed_certificate(self);
return ret;
}

void ptls_dispose_compressed_certificate(ptls_emit_compressed_certificate_t *self)
{
free(self->with_ocsp_status.bytes.base);
free(self->without_ocsp_status.bytes.base);
}
8 changes: 5 additions & 3 deletions t/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -459,8 +459,10 @@ int main(int argc, char **argv)
case 'u':
request_key_update = 1;
break;
default:
case 'h':
usage(argv[0]);
exit(0);
default:
exit(1);
}
}
Expand All @@ -478,8 +480,8 @@ int main(int argc, char **argv)
#if PICOTLS_USE_BROTLI
if (ctx.decompress_certificate != NULL) {
static ptls_emit_compressed_certificate_t ecc;
if (ptls_init_compressed_certificate(&ecc, PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_BROTLI, ctx.certificates.list,
ctx.certificates.count, ptls_iovec_init(NULL, 0)) != 0) {
if (ptls_init_compressed_certificate(&ecc, ctx.certificates.list, ctx.certificates.count, ptls_iovec_init(NULL, 0)) !=
0) {
fprintf(stderr, "failed to create a brotli-compressed version of the certificate chain.\n");
exit(1);
}
Expand Down
10 changes: 10 additions & 0 deletions t/e2e.t
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ subtest "early-data" => sub {
};
};

subtest "certificate-compression" => sub {
plan skip_all => "feature disabled"
unless system("$cli -b -h > /dev/null 2>&1") == 0;
my $guard = spawn_server(qw(-i t/assets/hello.txt -b));
my $resp = `$cli 127.0.0.1 $port 2> /dev/null`;
isnt $resp, "hello";
$resp = `$cli -b 127.0.0.1 $port 2> /dev/null`;
is $resp, "hello";
};

done_testing;

sub spawn_server {
Expand Down

0 comments on commit e278d03

Please sign in to comment.