From eba4a2ebb4dfe2b71ee8a9e7910d67229912184e Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Mon, 30 Jan 2017 21:58:08 +0100 Subject: [PATCH 1/3] import from icann-keytools-20160419 --- ksrsigner/ksrcommon.c | 176 ++++++++++++++++++++++++++++++++++++------ ksrsigner/ksrcommon.h | 3 +- ksrsigner/ksrpolicy.h | 5 +- ksrsigner/ksrsigner.c | 85 ++++++++++++++------ ksrsigner/wksr.c | 36 +++++---- 5 files changed, 241 insertions(+), 64 deletions(-) diff --git a/ksrsigner/ksrcommon.c b/ksrsigner/ksrcommon.c index b062bdc..8dfc3aa 100644 --- a/ksrsigner/ksrcommon.c +++ b/ksrsigner/ksrcommon.c @@ -39,6 +39,7 @@ static void free_responsepolicy(responsepolicy *r); static char *gattr(char *tp,char *str); static time_t ztime2sec(char *s); static int fillinpinfo(krecord *kr); +static uint16_t updatekeytag(krecord *kr); // Called earlier to check RequestBundle keyTags with Key material #define DBUFSIZE 2048 @@ -236,16 +237,24 @@ int xmlparse(char *tp,xmlstate *xs) rppolicy = (responsepolicy *)ksr_calloc(1,sizeof(responsepolicy)); #define SIGALG_STR "SignatureAlgorithm " } else if(strncasecmp(p,SIGALG_STR,sizeof(SIGALG_STR)-1) == 0) { - if(rppolicy) { + if(rppolicy) { char *q2; - if(rppolicy->sigalg) free(rppolicy->sigalg); - rppolicy->sigalg = (sigalg *)ksr_calloc(1,sizeof(sigalg)); - if((q2=gattr(p,"algorithm=")) == NULL) { + sigalg *xpsigalg; + // Add multiple SignatureAlgorithm support + xpsigalg = (sigalg *)ksr_calloc(1,sizeof(sigalg)); + if(rppolicy->sigalg) { + sigalg *xp; + for(xp=rppolicy->sigalg;xp->next;xp=xp->next) ; + xp->next = xpsigalg; + } else { + rppolicy->sigalg = xpsigalg; + } + if((q2=gattr(p,"algorithm=")) == NULL) { logger_error("XML tag <%s> is missing attribute value for %s",p,"algorithm="); ksrinvalid++; break; } - rppolicy->sigalg->algorithm = strtol(q2,&q2p,10); + xpsigalg->algorithm = strtol(q2,&q2p,10); // Add multiple SignatureAlgorithm support if(*q2p) { logger_error("algorithm non numeric \"%s\"",q2); ksrinvalid++; @@ -253,18 +262,22 @@ int xmlparse(char *tp,xmlstate *xs) break; } free(q2); - } + } #define RSACMP_STR "RSA " } else if(strncasecmp(p,RSACMP_STR,sizeof(RSACMP_STR)-1) == 0) { if(rppolicy && rppolicy->sigalg) { char *q2; - if((q2=gattr(p,"size=")) == NULL) { + sigalg *xp; + // Add multiple SignatureAlgorithm support + for(xp=rppolicy->sigalg;xp->next;xp=xp->next) ; + + if((q2=gattr(p,"size=")) == NULL) { logger_error("XML tag <%s> is missing attribute value for %s", p,"size="); ksrinvalid++; break; } - rppolicy->sigalg->rsa_size = strtol(q2,&q2p,10); + xp->rsa_size = strtol(q2,&q2p,10); // Add multiple SignatureAlgorithm support if(*q2p) { logger_error("rsa size non numeric \"%s\"",q2); ksrinvalid++; @@ -278,7 +291,7 @@ int xmlparse(char *tp,xmlstate *xs) ksrinvalid++; break; } - rppolicy->sigalg->rsa_exp = strtol(q2,&q2p,10); + xp->rsa_exp = strtol(q2,&q2p,10); // Add multiple SignatureAlgorithm support if(*q2p) { logger_error("Exponent non numeric \"%s\"",q2); ksrinvalid++; @@ -286,7 +299,7 @@ int xmlparse(char *tp,xmlstate *xs) break; } free(q2); - } + } } if(xmlparse(&lbuf[1],xs) < 0) return -1; @@ -645,9 +658,128 @@ static void free_responsepolicy(responsepolicy *r) if(r->MinSignatureValidity) free(r->MinSignatureValidity); if(r->MaxValidityOverlap) free(r->MaxValidityOverlap); if(r->MinValidityOverlap) free(r->MinValidityOverlap); - if(r->sigalg) free(r->sigalg); + if(r->sigalg) { + sigalg *xp,*xpn; + for(xp=r->sigalg;xp;) { // Add multiple SignatureAlgorithm processing + xpn = xp->next; + free(xp); + xp = xpn; + } + } free(r); } + +/*! Return (XML-format-days) - (days). Support for testing RequestPolicy + + \param xst pointer to XML formated days. e.g. PxD x days + \param days + \return (XML-format-days) - (days) +*/ +static int xmltimecmp(char *xst,int days) +{ + int dout; + if(xst == NULL) return 0; // accept if not specified since this does not effect DNSKEY RRset + dout = 0; + sscanf(xst,"P%dD",&dout); + if(debug) logger_info("%s: %d - %d",__func__,dout,days); + return dout - days; +} +/*! Check RequestPolicy and Other sections of KSR. Note:Has no effect on resultant DNSKEY RRSet + + \param rq pointer to parsed request + \return 0 but global variable ksrinvalid is incremented for each error +*/ +static int check_requestpolicy(reqresp *rq) +{ + static reqresp *last_rq=NULL; + + if(last_rq) { // Test RRSIG overlap of RequestBundles (Other) + // On subsequent passes check T_MIN/MAX_VALIDITY_OVERLAP - MUST BE SEQUENTIAL RequestBundles + time_t li; + if(rq->Inception > last_rq->Expiration) { + logger_error("Signatures do not overlap"); + ksrinvalid++; + } + li = (1 + last_rq->Expiration - rq->Inception)/T_ONEDAY; + if(debug) logger_info("Overlap = %d",li); + if(li < T_MIN_VALIDITY_OVERLAP || li > T_MAX_VALIDITY_OVERLAP) { + logger_error("Overlap period out of policy %d days",li); + ksrinvalid++; + } + } + + last_rq = rq; // first pass so set last_rq for subsequent sequential overlap comparison + + // Check stated VRSN ZSK RequestPolicy to see if they match specs. + // Not used in results (DNSKEY RRsets) except to check VRSN's own work. + // We enforce actual param limits on keys+sigs in later section. + if(xmltimecmp(rppolicy->PublishSafety,T_PUBLISH_SAFETY) < 0) { + logger_error("ZSK POLICY: Bad PublishSafety (%s)",rppolicy->PublishSafety); + ksrinvalid++; + } + if(xmltimecmp(rppolicy->RetireSafety,T_RETIRE_SAFETY) > 0) { + logger_error("ZSK POLICY: Bad RetireSafety (%s)",rppolicy->RetireSafety); + ksrinvalid++; + } + // ICANN controls this since we are the signer - see original code later + // if(li > (T_MAX_SIG_VAL*T_ONEDAY) || (li+1) < (T_MIN_SIG_VAL*T_ONEDAY)) 15-20 days + if(xmltimecmp(rppolicy->MaxSignatureValidity,T_MAX_SIG_VAL)) { // match + logger_error("ZSK POLICY: Bad MaxSignatureValidity (%s)",rppolicy->MaxSignatureValidity); + ksrinvalid++; + } + if(xmltimecmp(rppolicy->MinSignatureValidity,T_MIN_SIG_VAL)) { // match + logger_error("ZSK POLICY: Bad MinSignatureValidity (%s)",rppolicy->MinSignatureValidity); + ksrinvalid++; + } + if(xmltimecmp(rppolicy->MaxValidityOverlap,T_MAX_VALIDITY_OVERLAP) > 0) { + logger_error("ZSK POLICY: Bad MaxValidityOverlap (%s)",rppolicy->MaxValidityOverlap); + ksrinvalid++; + } + if(xmltimecmp(rppolicy->MinValidityOverlap,T_MIN_VALIDITY_OVERLAP) < 0) { + logger_error("ZSK POLICY: Bad MinValidityOverlap (%s)",rppolicy->MinValidityOverlap); + ksrinvalid++; + } + + // Test ZSK Key parameters (Other) against VRSN stated policies (RequestPolicy) + { + sigalg *xp; + uint32_t rsa_exp; + int n; + uint8_t *p; + krecord *y; + + for(y=rq->x_key;y;y=y->next) { + p = y->pubexp->p0; + n = (int)(y->pubexp->pc - p); + rsa_exp = 0; + if(n > 4) { + logger_error("Unsupported exponent length (%d)",n); + ksrinvalid++; + } else { + for(;n>0;n--) rsa_exp = (rsa_exp<<8)|*p++; + } + if(debug) logger_info("alg:%d size:%d exp:%d",y->Algorithm,y->bits,rsa_exp); + for(xp=rppolicy->sigalg;xp;xp=xp->next) { // walk RequestPolicy SignatureAlgorithm and compare + if(debug) logger_info(" alg=%d size=%d exp=%d",xp->algorithm,xp->rsa_size,xp->rsa_exp); + if(y->Algorithm == xp->algorithm && y->bits == xp->rsa_size + && rsa_exp == (uint32_t)xp->rsa_exp) break; + } + if(xp == NULL) { // no matches + logger_error("ZSK does not match any xml ZSK SignatureAlgorithm"); + ksrinvalid++; + } + // no effect on DNSKEY RRset result but check VRSN xml "keytag" match while at it + if(y->keyTag != updatekeytag(y)) { + logger_error("ZSK xml keytag and actual keytag do not match (%d != %d)", + y->keyTag,updatekeytag(y)); + ksrinvalid++; + } + } + } + + return 0; +} + /*! Check if request is valid (e.g., algorithms, protocols, validity period, proof of private key ownership, etc..) @@ -723,12 +855,8 @@ int check_requestbundle(reqresp *rq,char *ksrdomain) } if(rq->Expiration > maxexpiration) { - /* - logger_warning("Requests signature expiration exceeds %d days. Limiting!", - (T_VLIMIT+1)); - rq->Expiration = maxexpiration; - ksrinvalid++; - */ + /* Changed from error to warning in DVD r600 to provide flexibility to personnel scheduling. + Previously limited rq->Expiration to maxexpiration and incremented ksrinvalid. */ logger_warning("*** Requests signature expiration exceeds limit of %d days! ***",(T_VLIMIT+1)); } @@ -755,6 +883,8 @@ int check_requestbundle(reqresp *rq,char *ksrdomain) } #endif + check_requestpolicy(rq); // Add check of RequestPolicy. Note: This has no effect on resultant DNSKEY RRset + if(ksrinvalid) goto enderror; /* @@ -788,7 +918,7 @@ int check_requestbundle(reqresp *rq,char *ksrdomain) ksrinvalid++; goto enderror; } - + /* * Check other parameters and policy and signature lifetimes * ===== most done prior to here ==== @@ -873,10 +1003,12 @@ int signem(FILE *ftmp,xmlstate *xs) if(rppolicy->MinValidityOverlap) fprintf(ftmp,"%s\n",rppolicy->MinValidityOverlap); if(rppolicy->sigalg) { sigalg *sa; - sa = rppolicy->sigalg; - fprintf(ftmp,"\n",sa->algorithm); - fprintf(ftmp,"\n",sa->rsa_size,sa->rsa_exp); - fprintf(ftmp,"\n"); + // Add support for multiple SignatureAlgorithms in ResponsePolicy. Note: This has no effect on resultant DNSKEY RRset + for(sa=rppolicy->sigalg;sa;sa=sa->next) { + fprintf(ftmp,"\n",sa->algorithm); + fprintf(ftmp,"\n",sa->rsa_size,sa->rsa_exp); + fprintf(ftmp,"\n"); + } } fprintf(ftmp,"\n"); } diff --git a/ksrsigner/ksrcommon.h b/ksrsigner/ksrcommon.h index e54ff6b..b2fed84 100644 --- a/ksrsigner/ksrcommon.h +++ b/ksrsigner/ksrcommon.h @@ -81,7 +81,8 @@ typedef struct _signature { char *SignatureData; } signature; -typedef struct { +typedef struct _sigalg { + struct _sigalg *next; // Add multiple SignatureAlgorithm support int algorithm; int rsa_size; int rsa_exp; diff --git a/ksrsigner/ksrpolicy.h b/ksrsigner/ksrpolicy.h index e382887..c214604 100644 --- a/ksrsigner/ksrpolicy.h +++ b/ksrsigner/ksrpolicy.h @@ -54,7 +54,10 @@ #define KSK_RRSIG_ALG RRSIG_RSASHA256 /*!< KSK sig algorithm */ #define KSK_RRSIG_RSA_KEYSIZE 2048 /*!< KSK sig keysize */ -#define KSK_RRSIG_RSA_EXPONENT 3 /*!< KSK sig exponent */ +// Correct exponent used in RequestPolicy/ResponsePolicy. Does not effect DNSKEY RRSet result +#define KSK_RRSIG_RSA_EXPONENT 65537 /*!< KSK sig exponent */ +#define KSK_RRSIG_RSA_EXPONENT_BN "\x01\x00\x01" /*!< KSK sig exponent BN */ +#define KSK_RRSIG_RSA_EXPONENT_BNLEN 3 /*!< KSK sig exponent BN len */ /*! Minimum number of empty slots required for KSK roll */ #define MIN_SLOTS_FOR_KSK_ROLL 9 diff --git a/ksrsigner/ksrsigner.c b/ksrsigner/ksrsigner.c index 850b7af..06a8dfa 100644 --- a/ksrsigner/ksrsigner.c +++ b/ksrsigner/ksrsigner.c @@ -91,6 +91,8 @@ int main(int argc,char *argv[]) printf("%s %s version %s\n", PACKAGE_TARNAME, progname, PACKAGE_VERSION); exit(0); } + + if(strcmp(argv[i],"-d") == 0) { debug = 1; continue; } // Added a debug flag for testing if(strcmp(argv[i],"-h") == 0) { printf("Usage: %s [-Override] [-Revoke] [-h] [current-KSK] [next-KSK] [KSR-to-sign.xml]\n", argv[0]); @@ -280,26 +282,28 @@ int main(int argc,char *argv[]) } for(x=rq->x_key,j=0;x && j<2;x=x->next) ksrk[j++] = x; if(i != 2) { - logger_error("Wrong number (%d) of ZSKs in SKR",i); - k++; - goto nmatch; + // Change for 1024->2048 ZSK fallback path KSRs. Error is now warning + logger_warning("Wrong number (%d) of ZSKs in SKR",i); } if(j != 2) { - logger_error("Wrong number (%d) of ZSKs in KSR",j); - k++; - goto nmatch; - } - if( - ((strcmp(skrk[0]->PublicKey,ksrk[0]->PublicKey) - || strcmp(skrk[1]->PublicKey,ksrk[1]->PublicKey)) - && - (strcmp(skrk[0]->PublicKey,ksrk[1]->PublicKey) - || strcmp(skrk[1]->PublicKey,ksrk[0]->PublicKey))) - ) { - logger_error("Last SKR and current KSR keys do not match"); - k++; + // Changed for 1024->2048 ZSK fallback path KSRs. Error is now warning + logger_warning("Wrong number (%d) of ZSKs in KSR",j); } - nmatch: + /* Added for 1024->2048 ZSK fallback path KSRs so that a single ZSK at the begining or end is acceptable */ + if(i != j) k++; + else if(i == 1) { + if(strcmp(skrk[0]->PublicKey,ksrk[0]->PublicKey)) k++; + } else if(i == 2) { + if( + ((strcmp(skrk[0]->PublicKey,ksrk[0]->PublicKey) + || strcmp(skrk[1]->PublicKey,ksrk[1]->PublicKey)) + && + (strcmp(skrk[0]->PublicKey,ksrk[1]->PublicKey) + || strcmp(skrk[1]->PublicKey,ksrk[0]->PublicKey))) + ) k++; + } else k++; + if(k) logger_error("Last SKR and current KSR keys do not match"); + // nmatch: label no longer needed after 1024->2048 ZSK fallback changes above if(k) { logger_error("Problem with ZSK trust daisy chain."); ksrinvalid++; @@ -431,6 +435,18 @@ static krecord *fillinkinfo(void *pk) free_keyrecord(kr); return NULL; } + // Narrow range of acceptable KSK match policy exponent and size + if(elen != KSK_RRSIG_RSA_EXPONENT_BNLEN + || memcmp(kr->pubexp->p0,KSK_RRSIG_RSA_EXPONENT_BN,KSK_RRSIG_RSA_EXPONENT_BNLEN)) { + logger_error("Unsupported public exponent"); + free_keyrecord(kr); + return NULL; + } + if(kr->bits != KSK_RRSIG_RSA_KEYSIZE) { + logger_error("Unsupported key size %d",kr->bits); + free_keyrecord(kr); + return NULL; + } mlen = (int)(kr->modulus->pc - kr->modulus->p0); q = q0 = (uint8_t *)malloc((elen+mlen+1)); @@ -480,14 +496,37 @@ static krecord *fillinkinfo(void *pk) static int getKSKs(krecord *ksks[]) { void *pk[MAX_KSKS]; - int i,n; - + int i,j,n,m,m1,m2; + // Added support for being able to load specific keys - not all which can cause confusion + // Was not a problem with one key in the HSM + m1 = m2 = 0; + if(ksklabel_1) m1 = strlen(ksklabel_1); + if(ksklabel_2) m2 = strlen(ksklabel_2); n = pkcs11_getpub(NULL,NULL,NULL,NULL,pk,MAX_KSKS); - for(i=0;ipc - bp->p0); + if(m2) { + if((m == m1 && memcmp(bp->p0,ksklabel_1,m) == 0) + || (m == m2 && memcmp(bp->p0,ksklabel_2,m) == 0) ) goto accept; + } else if(m == m1 && memcmp(bp->p0,ksklabel_1,m) == 0) goto accept; + mbuf_free(bp); + continue; + accept: + mbuf_free(bp); + } + if((ksks[j] = fillinkinfo(pk[i])) == NULL) { // load keys. any failure is serious enough to terminate + for(i=0;ksks[j] && ix_key,j=0;x && j<2;x=x->next) ksrk[j++] = x; if(i != 2) { - logger_error("Wrong number (%d) of ZSKs in SKR",i); - k++; - goto nmatch; + // Change for 1024->2048 ZSK fallback path KSRs. Error is now warning + logger_warning("Wrong number (%d) of ZSKs in SKR",i); } if(j != 2) { - logger_error("Wrong number (%d) of ZSKs in KSR",j); - k++; - goto nmatch; + // Changed for 1024->2048 ZSK fallback path KSRs. Error is now warning + logger_warning("Wrong number (%d) of ZSKs in KSR",j); } - if( - ((strcmp(skrk[0]->PublicKey,ksrk[0]->PublicKey) - || strcmp(skrk[1]->PublicKey,ksrk[1]->PublicKey)) - && - (strcmp(skrk[0]->PublicKey,ksrk[1]->PublicKey) - || strcmp(skrk[1]->PublicKey,ksrk[0]->PublicKey))) - ) { - logger_error("Last SKR and current KSR keys do not match"); - k++; - } - nmatch: + /* Added for 1024->2048 ZSK fallback path KSRs so that a single ZSK at the begining or end is acceptable */ + if(i != j) k++; + else if(i == 1) { + if(strcmp(skrk[0]->PublicKey,ksrk[0]->PublicKey)) k++; + } else if(i == 2) { + if( + ((strcmp(skrk[0]->PublicKey,ksrk[0]->PublicKey) + || strcmp(skrk[1]->PublicKey,ksrk[1]->PublicKey)) + && + (strcmp(skrk[0]->PublicKey,ksrk[1]->PublicKey) + || strcmp(skrk[1]->PublicKey,ksrk[0]->PublicKey))) + ) k++; + } else k++; + if(k) logger_error("Last SKR and current KSR keys do not match"); + // nmatch: label no longer needed after 1024->2048 ZSK fallback changes above if(k) { logger_error("Problem with ZSK trust daisey chain."); logger_error("....FAILED. Skipped SKR-KSR trust chain check"); From d68f4f43c1c30b265b197dbdceea84e50800c7f2 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Mon, 30 Jan 2017 21:58:39 +0100 Subject: [PATCH 2/3] import from icann-keytools-20161012 --- README.md | 6 +++--- common/logger.c | 4 ++-- common/pkcs11_dnssec.c | 2 +- kskgen/kskparams.h | 4 ++-- ksrsigner/ksrpolicy.h | 12 ++++++------ 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index c923fcf..4000648 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # ICANN DNSSEC Key Tools -This repository contains source code for the software used by IANA to manage +This repository contains source code for the software used by PTI to manage the DNSSEC Key Signing Key (KSK) for the Root Zone. More information about Root Zone Management is available at -http://www.iana.org/domains/root. +https://www.iana.org/domains/root. ## License - Copyright (c) 2010-2013 Internet Corporation for Assigned Names and + Copyright (c) 2010-2016 Internet Corporation for Assigned Names and Numbers ("ICANN") Permission to use, copy, modify, and/or distribute this software for any diff --git a/common/logger.c b/common/logger.c index da119b4..627aa10 100644 --- a/common/logger.c +++ b/common/logger.c @@ -43,7 +43,7 @@ static const char *logfile_timeformat = "%Y%m%d-%H%M%S"; static const char *logentry_timeformat = "%Y-%m-%dT%H:%M:%SZ"; static char logfile_fname[MAXPATHLEN]; -/*! emit fata error message with decoded error number and exit +/*! emit fatal error message with decoded error number and exit \param message optional message string to incorporate into display */ @@ -74,7 +74,7 @@ static const char *pri2str(int pri) if (pri == LOG_ALERT) return "alert"; if (pri == LOG_CRIT) return "critical"; if (pri == LOG_ERR) return "error"; - if (pri == LOG_WARNING) return "waring"; + if (pri == LOG_WARNING) return "warning"; if (pri == LOG_NOTICE) return "notice"; if (pri == LOG_INFO) return "info"; if (pri == LOG_DEBUG) return "debug"; diff --git a/common/pkcs11_dnssec.c b/common/pkcs11_dnssec.c index 7cf3d24..9f85cad 100644 --- a/common/pkcs11_dnssec.c +++ b/common/pkcs11_dnssec.c @@ -850,7 +850,7 @@ int pkcs11_getpub(char *label,char *id,mbuf *mod,mbuf *exp,void *vdc[],int kmax) if((rv=pfl->C_FindObjectsFinal(sh)) != CKR_OK) goto nopriv; if(i <= 0) { nopriv: - logger_warning("No matching private key for %s/%s in HSM %s slot %d",kr->label->p0,kr->id->p0,pk->lib,pk->slot); + logger_warning("No matching private key for %s in HSM %s slot %d",kr->label->p0,pk->lib,pk->slot); } else { kr->hkp = (void *)hPrivKeys[0]; } diff --git a/kskgen/kskparams.h b/kskgen/kskparams.h index c9ca6d6..6303d1b 100644 --- a/kskgen/kskparams.h +++ b/kskgen/kskparams.h @@ -35,8 +35,8 @@ /* * Certificate Request Subject */ -#define DN_O "ICANN" /*!< Organization */ -#define DN_OU "IANA" /*!< Organization Unit */ +#define DN_O "Public Technical Identifiers" // was "ICANN" /*!< Organization */ +#define DN_OU "Cryptographic Business Operations" // was "IANA" /*!< Organization Unit */ #define DN_EMAIL "dnssec@iana.org" /*!< Email Address */ #define OID_DNS "1.3.6.1.4.1.1000.53" /*!< Enterprise specific DNS OID */ diff --git a/ksrsigner/ksrpolicy.h b/ksrsigner/ksrpolicy.h index c214604..7746570 100644 --- a/ksrsigner/ksrpolicy.h +++ b/ksrsigner/ksrpolicy.h @@ -35,7 +35,7 @@ #define T_STEP 10 /*! Signature validity period (in days) */ -#define T_VALIDITY 15 +#define T_VALIDITY 21 // was 15days. RSSAC003 upped to 21 /*! ZSK Rollover Interval (in days) */ #define T_ZSKROLL 90 @@ -44,11 +44,11 @@ #define T_VLIMIT ((2*T_ZSKROLL) - 1) #define T_PUBLISH_SAFETY 0 /*!< KSK Publish Safety (in days) */ -#define T_RETIRE_SAFETY 28 /*!< KSK Retire Safety (in days) */ -#define T_MAX_SIG_VAL 20 /*!< Max sig validity (in days) */ -#define T_MIN_SIG_VAL 15 /*!< Min sig validity (in days) */ -#define T_MAX_VALIDITY_OVERLAP 10 /*!< Max sig validity overlap (in days) */ -#define T_MIN_VALIDITY_OVERLAP 5 /*!< Min sig validity overlap (in days) */ +#define T_RETIRE_SAFETY 28 // Not mentioned in RSSAC003. /*!< KSK Retire Safety (in days) */ +#define T_MAX_SIG_VAL 21 // was 20 RSSAC003 changed below so shift up. /*!< Max sig validity (in days) */ +#define T_MIN_SIG_VAL 21 // was 15 RSSAC003 upped to 21 /*!< Min sig validity (in days) */ +#define T_MAX_VALIDITY_OVERLAP 16 // was 10 RSSAC003 added 6. /*!< Max sig validity overlap (in days) */ +#define T_MIN_VALIDITY_OVERLAP 9 // was 5 RSSAC003 added 6. 11-2 for short months. /*!< Min sig validity overlap (in days) */ #define T_DEFAULT_TTL 172800 /*!< Default TTL (in seconds) */ From 840b09d5fed8348ee0f8cf70c8279b99ee6e9725 Mon Sep 17 00:00:00 2001 From: Jakob Schlyter Date: Tue, 18 Apr 2017 07:36:02 +0200 Subject: [PATCH 3/3] import from icann-keytools-20170403 --- ChangeLog | 120 +++++++++++++++ README.md | 2 +- ksrsigner/ksrcommon.c | 336 +++++++++++++++++++++++++++++++++++++++--- ksrsigner/ksrcommon.h | 20 +++ ksrsigner/ksrsigner.c | 5 +- ksrsigner/wksr.c | 1 + utils/hsmfd-hash | 75 ++++++++++ utils/printlog | 13 +- 8 files changed, 546 insertions(+), 26 deletions(-) create mode 100644 ChangeLog create mode 100644 utils/hsmfd-hash diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..135c3d6 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,120 @@ +2017-04-03 KSK Rollover + + Version 20170403 + + * Changes to the KSR Signer “ksrsigner” component: + + 1. ksrsigner/ksrcommon.c: + a. Added support to process the configuration file “kskschedule.json”. + b. Added support to process the configuration options to sign, publish + or revoke the KSK. + + 2. ksrsigner/ksrcommon.h: + a. Added definition of json key schedule parsing support. + + 3. ksrsigner/ksrsigner.c: + a. Added support to locate the configuration file kskschedule.json in the same + place as the KSR.xml file. + + 4. ksrsigner/wksr.c: + a. Added support to locate the configuration file kskschedule.json in the same + place as the KSR.xml file. + + * Other changes: + + 5. utils/printlog: + a. Removed top margin. + b. Added header with name and page. + c. Reduced font size to fit better on the page. + + 6. utils/hsmfd-hash: + a. New bash script to calculate, print and compare hashes for HSMFDs. + + 7. ChangeLog: + a. Added a change log file. + + 8. README.md: + a. Update copyright year to 2017 + + +2016-10-12 RSSAC003 Recommendation + + Version 20161012 + + * Changes to the KSR Signer “ksrsigner” component: + + 1. ksrsigner/ksrpolicy.h: + a. Changed the signature validity period to 21 days. + b. Changed the maximum signature validity to 21 days. + c. Changed the minimum signature validity to 21 days. + d. Changed the maximum signature validity overlap to 16 days. + e. Changed the minimum signature validity overlap to 9 days. + + * Changes to the KSK Generator “kskgen” component: + + 2. kskgen/kskparams.h: + a. Changed the certificate signing request Organization (O) from ICANN to + Public Technical Identifiers (PTI) to reflect the organization PTI as the + new Root Zone KSK Operator. + b. Changed the certificate signing request Organization Unit (OU) to + Cryptographic Business Operations. + + * Other changes: + + 3. common/logger.c + a. Fixed the misspelled word “fatal”. + b. Fixed the misspelled word “warning”. + + 4. common/pkcs11_dnssec.c + a. Changed a warning message in case there is not private key match. + + +2016-04-19 ZSK Length Change + + Version 20160419 + + * Changes to the KSR Signer “ksrsigner” component: + + 1. ksrsigner/ksrcommon.c + 2. ksrsigner/ksrcommon.h + 3. ksrsigner/ksrpolicy.h + 4. ksrsigner/ksrsigner.c + 5. ksrsigner/wksr.c + + a. Added support for multiple SignatureAlgorithm fields and check + the field on incoming KSR against actual key material and match fields + outgoing SKR. Specifically it was discovered that neither ICANN nor + Verisign were inspecting RequestPolicy or ResponsePolicy sections of + the KSR-SKR XML exchange. This was evidenced by the incorrect key + exponent “3” being passed back and forth since the first key ceremony + when it should have been “65537”. This had no effect on operations. + However, it does not meet the specifications originally laid out. + b. Added tests for other fields in the ksr.xml fields including: + Exponent length, SignatureAlgorithm and keytag. + c. Removed tests that limited acceptable KSR formats to allow for + extended single ZSK use across KSRs. + + +2011-09-21 180 Days Warning + + Version 20110921 + + Allowed signings prior to 180 days from signature expiration. The idea + is to keep the 180-day validation but to replace the error (signing + interception) with a warning so that the signing could still be + completed. + + +2010-10-29 x509 Schema + + Version 20101029 + + Changed to x509 schema. + + +2010-06-12 Original + + Version 20100612 + + First version. + diff --git a/README.md b/README.md index 4000648..7b96e0d 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ https://www.iana.org/domains/root. ## License - Copyright (c) 2010-2016 Internet Corporation for Assigned Names and + Copyright (c) 2010-2017 Internet Corporation for Assigned Names and Numbers ("ICANN") Permission to use, copy, modify, and/or distribute this software for any diff --git a/ksrsigner/ksrcommon.c b/ksrsigner/ksrcommon.c index 8dfc3aa..4491c7d 100644 --- a/ksrsigner/ksrcommon.c +++ b/ksrsigner/ksrcommon.c @@ -540,8 +540,13 @@ void display_reqresp(reqresp *rqrs[],int cnt) if((y->Flags & DNSKEY_SEP_FLAG) == 0) continue; jj = strlen(lbuf); if(jj) lbuf[jj++] = ','; - cX -= snprintf(&lbuf[jj],cX,"%05u%s",y->keyTag, - y->Flags & DNSKEY_REVOKE_FLAG ? "/R" : ""); + char kuse[5]; + signature *s; + kuse[0] = '\0'; + if( (y->Flags & DNSKEY_REVOKE_FLAG) ) strcat(kuse,"R"); + for(s=rq->x_sig;s;s=s->next) if(strcmp(s->keyIdentifier,y->keyIdentifier) == 0) break; + if(s) strcat(kuse,"S"); else strcat(kuse,"P"); // signed or pub only + cX -= snprintf(&lbuf[jj],cX,"%05u(%s)/%s",y->keyTag,y->keyIdentifier,kuse); // note: Revoke flag changes tag value } #endif myx_syslog(LOG_INFO," %-12s",lbuf); @@ -901,15 +906,7 @@ int check_requestbundle(reqresp *rq,char *ksrdomain) ksrinvalid++; goto enderror; /* if any error - exit */ } - if(debug) - logger_info("Verified private key ownership for %05u",s->KeyTag); -#ifdef VALIDATE_VALIDITY - /* find max VALIDATED expiration and inception time */ - if(s->SignatureExpiration > rq->expmax) - rq->expmax = s->SignatureExpiration; - if(s->SignatureInception < rq->incmin) - rq->incmin = s->SignatureInception; -#endif + if(debug) logger_info("Verified private key ownership for %05u",s->KeyTag); keycnt++; } } @@ -1023,6 +1020,22 @@ int signem(FILE *ftmp,xmlstate *xs) } } + kskslot *ks; + ks = NULL; + loadkeyschedule(); /* Try to load generalized key schedule json configuration */ + if(ksksch0) { + i = 0; + for(ks=ksksch0->s;ks;ks=ks->next) i++; + if(i != reqscnt) { + logger_error("Number of slots in KSR and JSON (file:%s) config do not match",JSONKSCHEDULEFILE); + ksrinvalid++; + goto enderror; + } + ks = ksksch0->s; // Note: multiple KSK schedules are loaded but only first one is used. + myx_syslog(LOG_INFO,"Reading KSK schedule \"%s\" from \"%s\"\n",ksksch0->name,JSONKSCHEDULEFILE); + myx_syslog(LOG_INFO,"# KSK Tag(CKA_LABEL)\n"); + } + /* reqs[] is now a sorted list */ for(ir=0;irsigner = 0; + if(ksks[i]->user) { + free(ksks[i]->user); + ksks[i]->user = NULL; + } + } + for(j=0;jn_pub;j++) { + for(i=0;ilabel->p0,ks->cka_label_pub[j]) == 0) { + keys[keycnt++] = ksks[i]; + ksks[i]->Flags &= ~DNSKEY_REVOKE_FLAG; /* clear prior use */ + ksks[i]->keyTag = updatekeytag(ksks[i]); + break; + } + } + if(i == nksk) { + logger_error("Key not in HSM %s",ks->cka_label_pub[j]); + ksrinvalid++; + goto enderror; + } + } + for(j=0;jn_revoke;j++) { + for(i=0;ilabel->p0,ks->cka_label_revoke[j]) == 0) { + keys[keycnt++] = ksks[i]; + ksks[i]->Flags |= DNSKEY_REVOKE_FLAG; + ksks[i]->keyTag = updatekeytag(ksks[i]); + break; + } + } + if(i == nksk) { + logger_error("Key not in HSM %s",ks->cka_label_revoke[j]); + ksrinvalid++; + goto enderror; + } + } + for(j=0;jn_sign;j++) { + for(i=0;ilabel->p0,ks->cka_label_sign[j]) == 0) { // Assumes no 2 KSK CKA_LABELS the same + if(pkcs11_have_private_key(keys[i]->pkcb) == 0) { + logger_error("Do not have private key for %s",ks->cka_label_sign[j]); + ksrinvalid++; + goto enderror; + } + keys[i]->signer = 1; + break; + } + } + if(i == keycnt) { + logger_error("Key not in HSM %s",ks->cka_label_sign[j]); + ksrinvalid++; + goto enderror; + } + } + + char lbuf[MAXPATHLEN]; + krecord *y; + int cX,jj; + char kuse[5]; + cX = sizeof(lbuf); + jj = 0; + for(i=0;iFlags & DNSKEY_REVOKE_FLAG) ) strcat(kuse,"R"); + if(y->signer) strcat(kuse,"S"); else strcat(kuse,"P"); // signed or pub only + if(i) { lbuf[jj] = ','; jj++; cX--; } + j = snprintf(&lbuf[jj],cX,"%05u(%s)/%s",y->keyTag,y->keyIdentifier,kuse); // note: Revoke flag changes tag value + cX -= j; + jj += j; + } + myx_syslog(LOG_INFO,"%d %s\n",ir+1,lbuf); + + ks = ks->next; // next parralel ksk instructions + } else if(revoke_all) { for(i=0;iKeyTag); -#ifdef VALIDATE_VALIDITY - if(s->SignatureExpiration > rq->expmax) - rq->expmax = s->SignatureExpiration; - if(s->SignatureInception < rq->incmin) - rq->incmin = s->SignatureInception; -#endif any++; } } @@ -1437,3 +1522,218 @@ int algtohash(int alg) } } +/*! Read in JSON config files + Added 13 January 2017 to support Jakob Schlyter config files + for flexible key scheduling for first root KSK rollover. RHLamb +*/ + +/* +Note: CKA_LABEL for Root KSK Operations is time() encoded in BASE32, i.e., +static const char cb32[] = "abcdefghijklmnopqrstuvwxyz234567"; +static const char cb32_ucase[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; +*/ + +static kskschedule *ksksch=NULL; + +static void kskschedule_add(char *label) +{ + kskschedule *ks; + + if(label == NULL) return; + ks = (kskschedule *)malloc(sizeof(kskschedule)); + memset(ks,0,sizeof(kskschedule)); + if(ksksch0 == NULL) { + ksksch0 = ks; + } else { + if(ksksch->next) { + printf("%s: Error!!\n",__func__); + exit(1); + } + ksksch->next = ks; + } + ksksch = ks; + ksksch->name = strdup(label); +} +static void kskschedule_add_seq(char *label) +{ + kskslot *ks; + + if(label == NULL) return; + if(ksksch == NULL) { + printf("%s: Error!!\n",__func__); + exit(1); + } + ks = (kskslot *)malloc(sizeof(kskslot)); + memset(ks,0,sizeof(kskslot)); + ks->seq = atoi(label); + if(ksksch->s == NULL) { + ksksch->s = ks; + } else { + kskslot *s; + for(s=ksksch->s;s;s=s->next) { + if(s->next == NULL) break; + } + s->next = ks; + } +} +static void kskschedule_add_key(char *label,char *key) +{ + kskslot *ks; + kskslot *s; + char *t_key,*args[JSON_NKEYS]; + int i; + + if(label == NULL || key == NULL) return; + if(ksksch == NULL) { + printf("%s: Error A!!\n",__func__); + exit(1); + } + if(ksksch->s == NULL) { + printf("%s: Error B!!\n",__func__); + exit(1); + } + for(s=ksksch->s;s;s=s->next) { + if(s->next == NULL) break; + } + if(strcmp(label,"publish") == 0) { + if(s->cka_label_pub[0]) { printf("%s: Error C publish!!\n",__func__); exit(1); } + t_key = strdup(key); + s->n_pub = lparse(t_key,args,JSON_NKEYS,','); + for(i=0;in_pub;i++) s->cka_label_pub[i] = strdup(args[i]); + free(t_key); + } else if(strcmp(label,"sign") == 0) { + if(s->cka_label_sign[0]) { printf("%s: Error C sign!!\n",__func__); exit(1); } + t_key = strdup(key); + s->n_sign = lparse(t_key,args,JSON_NKEYS,','); + for(i=0;in_sign;i++) s->cka_label_sign[i] = strdup(args[i]); + free(t_key); + } else if(strcmp(label,"revoke") == 0) { + if(s->cka_label_revoke[0]) { printf("%s: Error C revoke!!\n",__func__); exit(1); } + t_key = strdup(key); + s->n_revoke = lparse(t_key,args,JSON_NKEYS,','); + for(i=0;in_revoke;i++) s->cka_label_revoke[i] = strdup(args[i]); + free(t_key); + } else { + printf("%s: Error C |%s|!!\n",__func__,label); + exit(1); + } +} +static void kskschedule_print() +{ + kskschedule *ks; + kskslot *s; + int j; + char kpub[80],krev[80],ksign[80]; + for(ks=ksksch0;ks;ks=ks->next) { + printf("%s: %s\n %15s %15s %15s\n",__func__,ks->name,"Pub","Revoke","Sign"); + for(s=ks->s;s;s=s->next) { + kpub[0] = krev[0] = ksign[0] = '\0'; + for(j=0;jn_pub;j++) { if(j) strcat(kpub,","); strcat(kpub,s->cka_label_pub[j]); } + for(j=0;jn_revoke;j++) { if(j) strcat(krev,","); strcat(krev,s->cka_label_revoke[j]); } + for(j=0;jn_sign;j++) { if(j) strcat(ksign,","); strcat(ksign,s->cka_label_sign[j]); } + printf(" %d: %15s %15s %15s\n",s->seq,kpub,krev,ksign); + } + } +} + +#define LABELBUFLEN 2560 +static FILE *jsonfp=NULL; +static int jsondepth=0; + +static int jparse(void) +{ + int c,quote,bracket,n; + char *p,buf[LABELBUFLEN],label[LABELBUFLEN]; + + jsondepth++; + p = label; + n = 0; + quote = 0; + bracket = 0; + while((c=fgetc(jsonfp)) != EOF) { + + if(c == '\n' || c == '\r' || c == ' ' || c == '\t') continue; + + if(quote == 1) { + if(c == '"') { + quote = 0; + } else { + if(c != '\\') { *p++ = c; n++; } + } + continue; + } + if(c == '"') { quote = 1; continue; } + + else if(bracket == 1) { + if(c == ']') { + bracket = 0; + } else { + if(c != '\\') { *p++ = c; n++; } + } + continue; + } + if(c == '[') { bracket = 1; continue; } + + + else if(c == '{') { +#ifdef JSONTEST + printf("\n"); +#endif + *p++ = '\0'; + p = label; + n = 0; + jparse(); // go till next '}' +#ifdef JSONTEST + printf("*eol*%d*\n",jsondepth); +#endif + } else if(c == ':') { // label + *p++ = '\0'; +#ifdef JSONTEST + printf("%s:",label); +#endif + if(jsondepth == 3) kskschedule_add(label); + if(jsondepth == 4) kskschedule_add_seq(label); + + p = buf; + n = 0; + + //*p = '\0'; + + } else if(c == ',' || c == '}') { // contents + + *p++ = '\0'; +#ifdef JSONTEST + printf("%s",buf); +#endif + if(jsondepth == 5) kskschedule_add_key(label,buf); + + p = label; + n = 0; + + if(c == '}') { + jsondepth--; + return 0; + } + } else if(c == '\\') { + continue; + } else { + *p++ = c; + n++; + } + } + return 0; +} +int loadkeyschedule(void) +{ + int ret; + char lbuf[MAXPATHLEN]; + extern char *ksrpath; + + if(ksrpath) snprintf(lbuf,sizeof(lbuf),"%s/%s",ksrpath,JSONKSCHEDULEFILE); + else snprintf(lbuf,sizeof(lbuf),"%s",JSONKSCHEDULEFILE); + if((jsonfp=fopen(lbuf,"r")) == NULL) return -1; + ret = jparse(); + fclose(jsonfp); + if(debug) kskschedule_print(); + return ret; +} diff --git a/ksrsigner/ksrcommon.h b/ksrsigner/ksrcommon.h index b2fed84..077a921 100644 --- a/ksrsigner/ksrcommon.h +++ b/ksrsigner/ksrcommon.h @@ -159,4 +159,24 @@ extern int revoke_all; /* this was not */ extern int debug; +/* json key schedule parsing support */ + +#define JSONKSCHEDULEFILE "kskschedule.json" +typedef struct _kskslot { + struct _kskslot *next; + int seq; + int n_pub,n_revoke,n_sign; +#define JSON_NKEYS 5 + char *cka_label_pub[JSON_NKEYS]; + char *cka_label_sign[JSON_NKEYS]; + char *cka_label_revoke[JSON_NKEYS]; +} kskslot; +typedef struct _kskschedule { + struct _kskschedule *next; + char *name; + kskslot *s; +} kskschedule; +static kskschedule *ksksch0=NULL; +int loadkeyschedule(void); + #endif /* _KSRCOMMON_H_ */ diff --git a/ksrsigner/ksrsigner.c b/ksrsigner/ksrsigner.c index 06a8dfa..e4975f0 100644 --- a/ksrsigner/ksrsigner.c +++ b/ksrsigner/ksrsigner.c @@ -37,6 +37,7 @@ static const char *progname = "ksrsigner"; */ char *ksklabel_1,*ksklabel_2; time_t t_step,validityperiod,maxexpiration; +char *ksrpath=NULL; krecord *ksks[MAX_KSKS]; int nksk; @@ -113,7 +114,7 @@ int main(int argc,char *argv[]) if(ksrfile == NULL && (p=strrchr(argv[i],'.')) && strcmp(p,".xml") == 0) { - char *ksrpath; + ksrfile = strdup(argv[i]); /* CVTY - maybe quiet complaints */ ksrpath = strdup(ksrfile); @@ -133,7 +134,7 @@ int main(int argc,char *argv[]) skrfile = strdup(lbuf); snprintf(lbuf,sizeof(lbuf),"%s/%s",ksrpath,DEFAULT_SKR_FILENAME); skrold = strdup(lbuf); - free(ksrpath); + continue; } /* From key ceremony rehearsals can specify -OceRidE or just -O */ diff --git a/ksrsigner/wksr.c b/ksrsigner/wksr.c index 4f32adf..03a246f 100644 --- a/ksrsigner/wksr.c +++ b/ksrsigner/wksr.c @@ -40,6 +40,7 @@ static const char *progname = "wksr"; */ char *ksklabel_1,*ksklabel_2; time_t t_step,validityperiod,maxexpiration; +char *ksrpath=NULL; krecord *ksks[MAX_KSKS]; int nksk; diff --git a/utils/hsmfd-hash b/utils/hsmfd-hash new file mode 100644 index 0000000..0bc61f0 --- /dev/null +++ b/utils/hsmfd-hash @@ -0,0 +1,75 @@ +#!/bin/sh +# +# Easy way to calculate, print and compare hashes for HSMFDs + +# Mount point for HSMFD +hsmfd="/media/HSMFD" +hsmfd_="/media/HSMFD_" + +# How to use the script +usage () + { + echo "Usage:$0 option" + echo "Option:" + echo " -h Show this message" + echo " -c Calculate the HSMFD SHA-256 hash and PGP Word List" + echo " -p Print the calculated HSMFD SHA-256 hash and PGP Word List using the default printer" + echo " -m Compare the calculated SHA-256 hashes between HSMFDs" + } + +# Checking if the FD is mounted +check_mount() + { + if [ ! -d $1 ]; then + echo "$1 is not mounted" + exit 1 + fi + } + +# Select an option +case $1 in + "-h") + usage + exit 1 + ;; + "-c") + check_mount $hsmfd + # Calculating the SHA-256 hash + sha256hsmfd=$(find -P $hsmfd -type f -print0 | sort -z | xargs -0 cat | sha2wordlist) + echo "$hsmfd" + echo "$sha256hsmfd" + exit + ;; + "-p") + check_mount $hsmfd + # Calculating the SHA-256 hash + sha256hsmfd=$(find -P $hsmfd -type f -print0 | sort -z | xargs -0 cat | sha2wordlist) + echo "$hsmfd" + echo "$sha256hsmfd" + echo -e "\n # find -P $hsmfd -type f -print0 | sort -z | xargs -0 cat | sha2wordlist \n\n $sha256hsmfd" | enscript --font=Courier10 --header='HSMFD SHA-256 HASH||%D{%Y/%m/%d}' + exit + ;; + "-m") + check_mount $hsmfd + check_mount $hsmfd_ + # Calculating the SHA-256 hash + sha256hsmfd=$(find -P $hsmfd -type f -print0 | sort -z | xargs -0 cat | sha256sum) + sha256hsmfd_=$(find -P $hsmfd_ -type f -print0 | sort -z | xargs -0 cat | sha256sum) + echo "SHA-256 $hsmfd : $sha256hsmfd" + echo "SHA-256 $hsmfd_ : $sha256hsmfd_" + # Comparing the hashes + if [ "$sha256hsmfd" != "$sha256hsmfd_" ]; + then + echo "ERROR: SHA-256 hashes do not match, try to format the $hsmfd_ and copy again" + exit 1 + else + echo "SHA-256 hashes match" + fi + exit + ;; + *) + usage + exit 1 + ;; +esac + diff --git a/utils/printlog b/utils/printlog index c896dba..86d9c16 100644 --- a/utils/printlog +++ b/utils/printlog @@ -12,14 +12,17 @@ fi # approx top margin in postscript points (1 point = 0.353 mm) # (might need to be adjusted to match the letterhead) -top_margin=200 +## --margins=left:right:top:bottom +## top_margin=200 #for ICANN header +##--margins=::${top_margin}: \ +##--no-header \ # strip keymgmt tools timestams and loglevel, then print using enscript sed 's/....-..-..T..:..:..Z: \[.*\] //' < $LOGFILE |\ enscript \ --copies=${COPIES} \ - --no-header \ - --margins=::${top_margin}: \ - --font=Courier10 \ - --setpagedevice=Collate:true + --font=Courier7.5 \ + --setpagedevice=Collate:true \ + --header=$LOGFILE'||Page $% of $=' +