diff --git a/base/pcap-snoop.c b/base/pcap-snoop.c index e0c9b62..27b47c8 100644 --- a/base/pcap-snoop.c +++ b/base/pcap-snoop.c @@ -104,7 +104,7 @@ int err_exit(str,num) int usage() { - fprintf(stderr,"Usage: ssldump [-r dumpfile] [-i interface] \n"); + fprintf(stderr,"Usage: ssldump [-r dumpfile] [-i interface] [-l sslkeylogfile] \n"); fprintf(stderr," [-k keyfile] [-p password] [-vtaTnsAxVNde]\n"); fprintf(stderr," [filter]\n"); exit(0); @@ -293,7 +293,7 @@ int main(argc,argv) signal(SIGINT,sig_handler); - while((c=getopt(argc,argv,"vr:F:f:S:yTt:ai:k:p:nsAxXhHVNdqem:P"))!=EOF){ + while((c=getopt(argc,argv,"vr:F:f:S:yTt:ai:k:l:p:nsAxXhHVNdqem:P"))!=EOF){ switch(c){ case 'v': print_version(); @@ -326,6 +326,9 @@ int main(argc,argv) case 'k': SSL_keyfile=strdup(optarg); break; + case 'l': + SSL_keylogfile=strdup(optarg); + break; case 'p': SSL_password=strdup(optarg); break; @@ -465,6 +468,8 @@ int main(argc,argv) free(interface_name); if(SSL_keyfile) free(SSL_keyfile); + if(SSL_keylogfile) + free(SSL_keylogfile); if(SSL_password) free(SSL_password); diff --git a/common/include/r_macros.h b/common/include/r_macros.h index fa1b318..0a9e565 100644 --- a/common/include/r_macros.h +++ b/common/include/r_macros.h @@ -108,5 +108,18 @@ int debug(int class,char *format,...); #define UNIMPLEMENTED do { fprintf(stderr,"Function %s unimplemented\n",__FUNCTION__); abort(); } while(0) +#ifdef STDC_HEADERS +#include +#endif + +#ifndef STRNICMP + +#ifdef _WIN32 +#define STRNICMP(a,b,n) strnicmp(a,b,n) +#else +#define STRNICMP(a,b,n) strncasecmp(a,b,n) +#endif + +#endif #endif diff --git a/ssl/ciphersuites.c b/ssl/ciphersuites.c index 1954881..eb07c73 100644 --- a/ssl/ciphersuites.c +++ b/ssl/ciphersuites.c @@ -130,18 +130,18 @@ static SSL_CipherSuite CipherSuites[]={ {153,KEX_DH,SIG_DSS,ENC_SEED,16,128,128,DIG_SHA,20,0}, {154,KEX_DH,SIG_RSA,ENC_SEED,16,128,128,DIG_SHA,20,0}, {155,KEX_DH,SIG_NONE,ENC_SEED,16,128,128,DIG_SHA,20,0}, - {156,KEX_RSA,SIG_RSA,ENC_AES128,4,128,128,DIG_SHA256,32,0}, - {157,KEX_RSA,SIG_RSA,ENC_AES256,4,256,256,DIG_SHA384,48,0}, - {158,KEX_DH,SIG_RSA,ENC_AES128,4,128,128,DIG_SHA256,32,0}, - {159,KEX_DH,SIG_RSA,ENC_AES256,4,256,256,DIG_SHA384,48,0}, - {160,KEX_DH,SIG_RSA,ENC_AES128,4,128,128,DIG_SHA256,32,0}, - {161,KEX_DH,SIG_RSA,ENC_AES256,4,256,256,DIG_SHA384,48,0}, - {162,KEX_DH,SIG_DSS,ENC_AES128,4,128,128,DIG_SHA256,32,0}, - {163,KEX_DH,SIG_DSS,ENC_AES256,4,256,256,DIG_SHA384,48,0}, - {164,KEX_DH,SIG_DSS,ENC_AES128,4,128,128,DIG_SHA256,32,0}, - {165,KEX_DH,SIG_DSS,ENC_AES256,4,256,256,DIG_SHA384,48,0}, - {166,KEX_DH,SIG_NONE,ENC_AES128,4,128,128,DIG_SHA256,32,0}, - {167,KEX_DH,SIG_NONE,ENC_AES256,4,256,256,DIG_SHA384,48,0}, + {156,KEX_RSA,SIG_RSA,ENC_AES128_GCM,4,128,128,DIG_SHA256,32,0}, + {157,KEX_RSA,SIG_RSA,ENC_AES256_GCM,4,256,256,DIG_SHA384,48,0}, + {158,KEX_DH,SIG_RSA,ENC_AES128_GCM,4,128,128,DIG_SHA256,32,0}, + {159,KEX_DH,SIG_RSA,ENC_AES256_GCM,4,256,256,DIG_SHA384,48,0}, + {160,KEX_DH,SIG_RSA,ENC_AES128_GCM,4,128,128,DIG_SHA256,32,0}, + {161,KEX_DH,SIG_RSA,ENC_AES256_GCM,4,256,256,DIG_SHA384,48,0}, + {162,KEX_DH,SIG_DSS,ENC_AES128_GCM,4,128,128,DIG_SHA256,32,0}, + {163,KEX_DH,SIG_DSS,ENC_AES256_GCM,4,256,256,DIG_SHA384,48,0}, + {164,KEX_DH,SIG_DSS,ENC_AES128_GCM,4,128,128,DIG_SHA256,32,0}, + {165,KEX_DH,SIG_DSS,ENC_AES256_GCM,4,256,256,DIG_SHA384,48,0}, + {166,KEX_DH,SIG_NONE,ENC_AES128_GCM,4,128,128,DIG_SHA256,32,0}, + {167,KEX_DH,SIG_NONE,ENC_AES256_GCM,4,256,256,DIG_SHA384,48,0}, // Missing: 168-185 {186,KEX_RSA,SIG_RSA,ENC_CAMELLIA128,16,128,128,DIG_SHA256,32,0}, {187,KEX_DH,SIG_DSS,ENC_CAMELLIA128,16,128,128,DIG_SHA256,32,0}, @@ -188,14 +188,14 @@ static SSL_CipherSuite CipherSuites[]={ {49192,KEX_DH,SIG_RSA,ENC_AES256,16,256,256,DIG_SHA384,48,0}, {49193,KEX_DH,SIG_RSA,ENC_AES128,16,128,128,DIG_SHA256,32,0}, {49194,KEX_DH,SIG_RSA,ENC_AES256,16,256,256,DIG_SHA384,48,0}, - {49195,KEX_DH,SIG_DSS,ENC_AES128,4,128,128,DIG_SHA256,32,0}, - {49196,KEX_DH,SIG_DSS,ENC_AES256,4,256,256,DIG_SHA384,48,0}, - {49197,KEX_DH,SIG_DSS,ENC_AES128,4,128,128,DIG_SHA256,32,0}, - {49198,KEX_DH,SIG_DSS,ENC_AES256,4,256,256,DIG_SHA384,48,0}, - {49199,KEX_DH,SIG_RSA,ENC_AES128,4,128,128,DIG_SHA256,32,0}, - {49200,KEX_DH,SIG_RSA,ENC_AES256,4,256,256,DIG_SHA384,48,0}, - {49201,KEX_DH,SIG_RSA,ENC_AES128,4,128,128,DIG_SHA256,32,0}, - {49202,KEX_DH,SIG_RSA,ENC_AES256,4,256,256,DIG_SHA384,48,0}, + {49195,KEX_DH,SIG_DSS,ENC_AES128_GCM,4,128,128,DIG_SHA256,32,0}, + {49196,KEX_DH,SIG_DSS,ENC_AES256_GCM,4,256,256,DIG_SHA384,48,0}, + {49197,KEX_DH,SIG_DSS,ENC_AES128_GCM,4,128,128,DIG_SHA256,32,0}, + {49198,KEX_DH,SIG_DSS,ENC_AES256_GCM,4,256,256,DIG_SHA384,48,0}, + {49199,KEX_DH,SIG_RSA,ENC_AES128_GCM,4,128,128,DIG_SHA256,32,0}, + {49200,KEX_DH,SIG_RSA,ENC_AES256_GCM,4,256,256,DIG_SHA384,48,0}, + {49201,KEX_DH,SIG_RSA,ENC_AES128_GCM,4,128,128,DIG_SHA256,32,0}, + {49202,KEX_DH,SIG_RSA,ENC_AES256_GCM,4,256,256,DIG_SHA384,48,0}, // Missing: 49203-49211 {49266,KEX_DH,SIG_DSS,ENC_CAMELLIA128,16,128,128,DIG_SHA256,32,0}, {49267,KEX_DH,SIG_DSS,ENC_CAMELLIA256,16,256,256,DIG_SHA256,48,0}, diff --git a/ssl/ssl.enums.c b/ssl/ssl.enums.c index 55278b1..b5f8014 100644 --- a/ssl/ssl.enums.c +++ b/ssl/ssl.enums.c @@ -466,8 +466,10 @@ static int decode_HandshakeType_ClientKeyExchange(ssl,dir,seg,data) break; case KEX_DH: - SSL_DECODE_OPAQUE_ARRAY(ssl,"DiffieHellmanClientPublicValue", - -(1<<15-1),P_HL,data,0); + SSL_DECODE_OPAQUE_ARRAY(ssl,"DiffieHellmanClientPublicValue", + -((1<<7)-1),P_HL,data,0); + ssl_process_client_key_exchange(ssl, + ssl->decoder,NULL,0); } } return(0); diff --git a/ssl/ssl_analyze.c b/ssl/ssl_analyze.c index 67b1f73..5a6b7ea 100644 --- a/ssl/ssl_analyze.c +++ b/ssl/ssl_analyze.c @@ -77,6 +77,7 @@ static int print_ssl_record PROTO_LIST((ssl_obj *obj,int direction, segment *q,UCHAR *data,int len)); char *SSL_keyfile=0; char *SSL_password=0; +char *SSL_keylogfile=0; #define NEGATE 0x800000 @@ -218,7 +219,7 @@ static int create_ssl_ctx(handle,ctxp) ssl_decode_ctx *ctx=0; int r,_status; - if(r=ssl_decode_ctx_create(&ctx,SSL_keyfile,SSL_password)) + if(r=ssl_decode_ctx_create(&ctx,SSL_keyfile,SSL_password,SSL_keylogfile)) ABORT(r); *ctxp=(proto_ctx *)ctx; diff --git a/ssl/ssl_analyze.h b/ssl/ssl_analyze.h index d985218..20d2e64 100644 --- a/ssl/ssl_analyze.h +++ b/ssl/ssl_analyze.h @@ -82,6 +82,7 @@ extern proto_mod ssl_mod; extern UINT4 SSL_print_flags; extern char *SSL_keyfile; extern char *SSL_password; +extern char *SSL_keylogfile; #endif diff --git a/ssl/ssl_rec.c b/ssl/ssl_rec.c index cdf946e..098bb2d 100644 --- a/ssl/ssl_rec.c +++ b/ssl/ssl_rec.c @@ -61,6 +61,8 @@ static char *RCSSTRING="$Id: ssl_rec.c,v 1.3 2000/11/03 06:38:06 ekr Exp $"; struct ssl_rec_decoder_ { SSL_CipherSuite *cs; Data *mac_key; + Data *implicit_iv; /* for AEAD ciphers */ + Data *write_key; /* for AEAD ciphers */ #ifdef OPENSSL EVP_CIPHER_CTX *evp; #endif @@ -89,7 +91,9 @@ char *ciphers[]={ "CAMELLIA128", "CAMELLIA256", "SEED", - NULL + NULL, + "aes-128-gcm", + "aes-256-gcm" }; @@ -123,8 +127,28 @@ int ssl_create_rec_decoder(dp,cs,mk,sk,iv) ABORT(R_NO_MEMORY); dec->cs=cs; - if(r=r_data_create(&dec->mac_key,mk,cs->dig_len)) + + if(r=r_data_alloc(&dec->mac_key,cs->dig_len)) ABORT(r); + + if(r=r_data_alloc(&dec->implicit_iv,cs->block)) + ABORT(r); + memcpy(dec->implicit_iv->data,iv,cs->block); + + if(r=r_data_create(&dec->write_key,sk,cs->eff_bits/8)) + ABORT(r); + + /* + This is necessary for AEAD ciphers, because we must wait to fully initialize the cipher + in order to include the implicit IV + */ + if(IS_AEAD_CIPHER(cs)){ + sk=NULL; + iv=NULL; + } + else + memcpy(dec->mac_key->data,mk,cs->dig_len); + if(!(dec->evp=(EVP_CIPHER_CTX *)malloc(sizeof(EVP_CIPHER_CTX)))) ABORT(R_NO_MEMORY); EVP_CIPHER_CTX_init(dec->evp); @@ -150,6 +174,8 @@ int ssl_destroy_rec_decoder(dp) d=*dp; r_data_destroy(&d->mac_key); + r_data_destroy(&d->implicit_iv); + r_data_destroy(&d->write_key); #ifdef OPENSSL if(d->evp){ EVP_CIPHER_CTX_cleanup(d->evp); @@ -163,6 +189,9 @@ int ssl_destroy_rec_decoder(dp) } +#define MSB(a) ((a>>8)&0xff) +#define LSB(a) (a&0xff) + int ssl_decode_rec_data(ssl,d,ct,version,in,inl,out,outl) ssl_obj *ssl; ssl_rec_decoder *d; @@ -175,12 +204,48 @@ int ssl_decode_rec_data(ssl,d,ct,version,in,inl,out,outl) { #ifdef OPENSSL int pad; - int r,encpadl; - UCHAR *mac,*iv; + int r,encpadl,x; + UCHAR *mac,*iv,aead_tag[13],aead_nonce[12]; CRDUMP("Ciphertext",in,inl); + if(IS_AEAD_CIPHER(d->cs)){ + memcpy(aead_nonce,d->implicit_iv->data,d->implicit_iv->len); + memcpy(aead_nonce+d->implicit_iv->len,in,12-d->implicit_iv->len); + in+=12-d->implicit_iv->len; + inl-=12-d->implicit_iv->len; - if(ssl->extensions->encrypt_then_mac==2){ + EVP_DecryptInit(d->evp, + NULL, + d->write_key->data, + aead_nonce); + + /* + Then tag is always 16 bytes, as per: + https://tools.ietf.org/html/rfc5116#section-5.2 + */ + EVP_CIPHER_CTX_ctrl(d->evp,EVP_CTRL_GCM_SET_TAG,16,in+(inl-16)); + inl-=16; + + fmt_seq(d->seq,aead_tag); + d->seq++; + aead_tag[8]=ct; + aead_tag[9]=MSB(version); + aead_tag[10]=LSB(version); + aead_tag[11]=MSB(inl); + aead_tag[12]=LSB(inl); + + EVP_DecryptUpdate(d->evp,NULL,outl,aead_tag,13); + EVP_DecryptUpdate(d->evp,out,outl,in,inl); + + if (!(x=EVP_DecryptFinal(d->evp,NULL,&x))) + ERETURN(SSL_BAD_MAC); + } + + /* + Encrypt-then-MAC is not used with AEAD ciphers, as per: + https://tools.ietf.org/html/rfc7366#section-3 + */ + else if(ssl->extensions->encrypt_then_mac==2){ *outl=inl; /* First strip off the MAC */ @@ -265,8 +330,6 @@ int ssl_decode_rec_data(ssl,d,ct,version,in,inl,out,outl) } -#define MSB(a) ((a>>8)&0xff) -#define LSB(a) (a&0xff) #ifdef OPENSSL /* This should go to 2^128, but we're never really going to see diff --git a/ssl/ssl_rec.h b/ssl/ssl_rec.h index 203f55f..fa91b00 100644 --- a/ssl/ssl_rec.h +++ b/ssl/ssl_rec.h @@ -55,5 +55,6 @@ int ssl_create_rec_decoder PROTO_LIST((ssl_rec_decoder **dp, int ssl_decode_rec_data PROTO_LIST((ssl_obj *ssl,ssl_rec_decoder *d, int ct,int version,UCHAR *in,int inl,UCHAR *out,int *outl)); +#define IS_AEAD_CIPHER(cs) (cs->enc==0x3b||cs->enc==0x3c) #endif diff --git a/ssl/sslciphers.h b/ssl/sslciphers.h index 95e2939..a41aaf4 100644 --- a/ssl/sslciphers.h +++ b/ssl/sslciphers.h @@ -77,6 +77,8 @@ typedef struct SSL_CipherSuite_ { #define ENC_CAMELLIA256 0x38 #define ENC_SEED 0x39 #define ENC_NULL 0x3a +#define ENC_AES128_GCM 0x3b +#define ENC_AES256_GCM 0x3c #define DIG_MD5 0x40 #define DIG_SHA 0x41 diff --git a/ssl/ssldecode.c b/ssl/ssldecode.c index 7c3ee79..32946e9 100644 --- a/ssl/ssldecode.c +++ b/ssl/ssldecode.c @@ -76,6 +76,7 @@ struct ssl_decode_ctx_ { SSL_CTX *ssl_ctx; SSL *ssl; r_assoc *session_cache; + FILE *ssl_key_log_file; #else char dummy; /* Some compilers (Win32) don't like empty structs */ @@ -115,6 +116,7 @@ static int ssl_generate_keying_material PROTO_LIST((ssl_obj *ssl, ssl_decoder *d)); static int ssl_generate_session_hash PROTO_LIST((ssl_obj *ssl, ssl_decoder *d)); +static int ssl_read_key_log_file PROTO_LIST((ssl_decoder *d)); #endif static int ssl_create_session_lookup_key PROTO_LIST((ssl_obj *ssl, @@ -132,10 +134,11 @@ static int password_cb(char *buf,int num,int rwflag,void *userdata) return(strlen(ssl_password)); } -int ssl_decode_ctx_create(dp,keyfile,pass) +int ssl_decode_ctx_create(dp,keyfile,pass,keylogfile) ssl_decode_ctx **dp; char *keyfile; char *pass; + char *keylogfile; { #ifdef OPENSSL ssl_decode_ctx *d=0; @@ -169,6 +172,11 @@ int ssl_decode_ctx_create(dp,keyfile,pass) if(r_assoc_create(&d->session_cache)) ABORT(R_NO_MEMORY); + if(keylogfile && !(d->ssl_key_log_file=fopen(keylogfile, "r"))){ + fprintf(stderr,"Failed to open ssl key log file"); + ABORT(R_INTERNAL); + } + X509V3_add_standard_extensions(); *dp=d; @@ -539,39 +547,42 @@ int ssl_process_client_key_exchange(ssl,d,msg,len) #ifdef OPENSSL int r,_status; int i; - EVP_PKEY *pk; - - if(ssl->cs->kex!=KEX_RSA) - return(-1); - - if(d->ephemeral_rsa) - return(-1); - - pk=SSL_get_privatekey(d->ctx->ssl); - if(!pk) - return(-1); - - if(pk->type!=EVP_PKEY_RSA) - return(-1); - - if(r=r_data_alloc(&d->PMS,BN_num_bytes(pk->pkey.rsa->n))) - ABORT(r); - - i=RSA_private_decrypt(len,msg,d->PMS->data, - pk->pkey.rsa,RSA_PKCS1_PADDING); - - if(i!=48) - ABORT(SSL_BAD_PMS); - - d->PMS->len=48; - - CRDUMPD("PMS",d->PMS); /* Remove the master secret if it was there to force keying material regeneration in case we're renegotiating */ r_data_destroy(&d->MS); + + if(!d->ctx->ssl_key_log_file || + ssl_read_key_log_file(d) || + !d->MS){ + if(ssl->cs->kex!=KEX_RSA) + return(-1); + + if(d->ephemeral_rsa) + return(-1); + + pk=SSL_get_privatekey(d->ctx->ssl); + if(!pk) + return(-1); + + if(pk->type!=EVP_PKEY_RSA) + return(-1); + + if(r=r_data_alloc(&d->PMS,BN_num_bytes(pk->pkey.rsa->n))) + ABORT(r); + + i=RSA_private_decrypt(len,msg,d->PMS->data, + pk->pkey.rsa,RSA_PKCS1_PADDING); + + if(i!=48) + ABORT(SSL_BAD_PMS); + + d->PMS->len=48; + + CRDUMPD("PMS",d->PMS); + } switch(ssl->version){ case SSLV3_VERSION: @@ -731,7 +742,8 @@ static int tls12_prf(ssl,secret,usage,rnd1,rnd2,out) memcpy(ptr,rnd2->data,rnd2->len); ptr+=rnd2->len; /* Earlier versions of openssl didn't have SHA256 of course... */ - dgi = MAX(DIG_SHA256, ssl->cs->dig)-0x40; + dgi = MAX(DIG_SHA256, ssl->cs->dig); + dgi-=0x40; if ((md=EVP_get_digestbyname(digests[dgi])) == NULL) { DBG((0,"Cannot get EVP for digest %s, openssl library current?", digests[dgi])); @@ -876,7 +888,8 @@ static int ssl_generate_keying_material(ssl,d) /* Compute the key block. First figure out how much data we need*/ - needed=ssl->cs->dig_len*2; + /* Ideally find a cleaner way to check for AEAD cipher */ + needed=!IS_AEAD_CIPHER(ssl->cs)?ssl->cs->dig_len*2:0; needed+=ssl->cs->bits / 4; if(ssl->cs->block>1) needed+=ssl->cs->block*2; @@ -888,8 +901,11 @@ static int ssl_generate_keying_material(ssl,d) ABORT(r); ptr=key_block->data; - c_mk=ptr; ptr+=ssl->cs->dig_len; - s_mk=ptr; ptr+=ssl->cs->dig_len; + /* Ideally find a cleaner way to check for AEAD cipher */ + if(!IS_AEAD_CIPHER(ssl->cs)){ + c_mk=ptr; ptr+=ssl->cs->dig_len; + s_mk=ptr; ptr+=ssl->cs->dig_len; + } c_wk=ptr; ptr+=ssl->cs->eff_bits/8; s_wk=ptr; ptr+=ssl->cs->eff_bits/8; @@ -1051,4 +1067,46 @@ static int ssl_generate_session_hash(ssl,d) abort: return(_status); } + +static int ssl_read_key_log_file(d) + ssl_decoder *d; + { + int r,_status,dgi,n; + unsigned int t; + size_t l=0; + char *line,*label_data; + + while ((n=getline(&line,&l,d->ctx->ssl_key_log_file))!=-1) { + if(n==(d->client_random->len*2)+112 && + !strncmp(line,"CLIENT_RANDOM",13)) { + + if(!(label_data=malloc((d->client_random->len*2)+1))) + ABORT(r); + + for(int i=0;iclient_random->len;i++) + if(snprintf(label_data+(i*2),3,"%02x",d->client_random->data[i])!=2) + ABORT(r); + + if(STRNICMP(line+14,label_data,64)) + continue; + + if(r=r_data_alloc(&d->MS,48)) + ABORT(r); + + for(int i=0; i < d->MS->len; i++) { + if(sscanf(line+14+65+(i*2),"%2x",&t)!=1) + ABORT(r); + *(d->MS->data+i)=(char)t; + } + } + /* + Eventually add support for other labels defined here: + https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format + */ + } + _status=0; + abort: + fseek(d->ctx->ssl_key_log_file, SEEK_SET, 0); + return(_status); + } #endif diff --git a/ssl/ssldecode.h b/ssl/ssldecode.h index 3ef9226..48acafe 100644 --- a/ssl/ssldecode.h +++ b/ssl/ssldecode.h @@ -51,7 +51,7 @@ #define CRDUMPD(a,b) P_(P_CR) {exdump(ssl,a,b);printf("\n");} int ssl_decode_ctx_create PROTO_LIST((ssl_decode_ctx **ctx, - char *keyfile,char *password)); + char *keyfile,char *password,char *keylogfile)); int ssl_decoder_destroy PROTO_LIST((ssl_decoder **dp)); int ssl_decoder_create PROTO_LIST((ssl_decoder **dp,ssl_decode_ctx *ctx)); int ssl_set_client_random PROTO_LIST((ssl_decoder *dp, diff --git a/ssldump.1 b/ssldump.1 index ca5681c..a4a8e97 100644 --- a/ssldump.1 +++ b/ssldump.1 @@ -74,6 +74,10 @@ ssldump \- dump SSL traffic on a network .I keyfile ] [ +.B \-l +.I sslkeylogfile +] +[ .B \-p .I password ] @@ -210,6 +214,10 @@ Use \fIkeyfile\fP as the location of the SSL keyfile (OpenSSL format) Previous versions of ssldump automatically looked in ./server.pem. Now you must specify your keyfile every time. .TP +.BI \-l " sslkeylogfile" +Use \fIsslkeylogfile\fP as the location of the SSLKEYLOGFILE +(https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format) +.TP .BI \-p " password" Use \fIpassword\fP as the SSL keyfile password. .TP