--- /dev/null
+#include <stdint.h>
+#include <stdio.h>
+#include <string>
+#include <cstring>
+#include <stdlib.h>
+#include <iostream>
+#include "base32.hh"
+using namespace std;
+
+/* based on freebsd:src/contrib/opie/libopie/btoe.c extract: get bit ranges from a char* */
+uint32_t extract_bits(const char *s, int start, int length)
+{
+ uint32_t x;
+ unsigned char cl, cc, cr;
+
+ cl = s[start / 8];
+ cc = s[start / 8 + 1];
+ cr = s[start / 8 + 2];
+ x = ((uint32_t) (cl << 8 | cc) << 8 | cr);
+ x = x >> (24 - (length + (start % 8)));
+ x = (x & (0xffff >> (16 - length)));
+ return (x);
+}
+
+/* same, set bit ranges in a char* */
+static void set_bits(char* s, int x, int start, int length)
+{
+ unsigned char cl, cc, cr;
+ uint32_t y;
+ int shift;
+
+ shift = ((8 - ((start + length) % 8)) % 8);
+ y = (uint32_t) x << shift;
+ cl = (y >> 16) & 0xff;
+ cc = (y >> 8) & 0xff;
+ cr = y & 0xff;
+ if (shift + length > 16) {
+ s[start / 8] |= cl;
+ s[start / 8 + 1] |= cc;
+ s[start / 8 + 2] |= cr;
+ }
+ else {
+ if (shift + length > 8) {
+ s[start / 8] |= cc;
+ s[start / 8 + 1] |= cr;
+ } else {
+ s[start / 8] |= cr;
+ }
+ }
+}
+
+/* convert a base32 hex character to its decoded equivalent */
+static int unbase32hex(char c)
+{
+ if(c >= '0' && c<='9')
+ return c-'0';
+ if(c >= 'a' && c<='z')
+ return 10 + (c-'a');
+ if(c >= 'A' && c<='Z')
+ return 10 + (c-'A');
+ if(c=='=')
+ return '=';
+ return -1;
+}
+
+/* convert a binary string to base32hex */
+string toBase32Hex(const std::string& input)
+{
+ static const char base32hex[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV=";
+ string ret;
+ ret.reserve(4+ 8*input.length()/5); // optimization
+ // process input in groups of 5 8-bit chunks, emit 8 5-bit chunks
+ for(string::size_type offset = 0 ; offset < input.length(); offset+=5) {
+ int todo = input.length() - offset;
+ int stuffing; // how much '=' to add at the end
+
+ switch(todo) {
+ case 1:
+ stuffing = 6; break;
+ case 2:
+ stuffing = 4; break;
+ case 3:
+ stuffing = 3; break;
+ case 4:
+ stuffing = 1; break;
+ default: // -> 0 or more than 5, no stuffing
+ stuffing = 0; break;
+ }
+
+ for(int n=0; n < 8 - stuffing; ++n)
+ ret.append(1, base32hex[extract_bits(input.c_str()+offset, n*5, 5)]);
+ ret.append(stuffing, '=');
+ }
+
+ return ret;
+}
+
+// convert base32hex encoded string to normal string
+string fromBase32Hex(const std::string& input)
+{
+ string ret;
+ char block[5]={0,0,0,0,0}; // we process 5 8-bit chunks at a time
+ string::size_type n, toWrite=0;
+ for(n = 0; n < input.length(); ++n) {
+ int c=unbase32hex(input[n]);
+ if(c == '=' || c < 0) // stop at stuffing or error
+ break;
+ set_bits(block, c , (n % 8) * 5, 5);
+ if(++toWrite == 8) {
+ ret.append(block, sizeof(block));
+ memset(block, 0, sizeof(block));
+ toWrite = 0;
+ }
+ }
+ ret.append(block, (7+toWrite*5)/8); // round up
+
+ return ret;
+}
+
+#if 0
+int main(int argc, char **argv)
+{
+ if(argc!=3 || (argc==3 && strcmp(argv[1],"from") && strcmp(argv[1],"to"))) {
+ printf("syntax: base32 from|to string\n");
+ exit(0);
+ }
+ if(!strcmp(argv[1],"to")) {
+ printf("input: '%s'\noutput: '%s'\n",
+ argv[2],
+ toBase32Hex(argv[2]).c_str());
+ }
+ else {
+ cout<<"input: '"<<argv[2]<<"'\noutput: '"<<fromBase32Hex(argv[2])<<"'\n";
+ }
+}
+#endif
--- /dev/null
+#ifndef PDNS_BASE32_HH
+#define PDNS_BASE32_HH
+#include <string>
+
+std::string toBase32Hex(const std::string& input);
+std::string fromBase32Hex(const std::string& input);
+
+#endif
--- /dev/null
+#include "dnsparser.hh"
+#include "sstuff.hh"
+#include "misc.hh"
+#include "dnswriter.hh"
+#include "dnsrecords.hh"
+#include "statbag.hh"
+#include "iputils.hh"
+#include <netinet/sctp.h>
+#include <boost/foreach.hpp>
+#include <boost/algorithm/string.hpp>
+#include <polarssl/rsa.h>
+#include <polarssl/base64.h>
+#include <polarssl/sha1.h>
+#include <polarssl/sha2.h>
+#include "dnssecinfra.hh"
+#include "dnsseckeeper.hh"
+
+using namespace boost;
+using namespace std;
+
+DNSKEYRecordContent getRSAKeyFromISC(rsa_context* rsa, const char* fname)
+{
+ char line[1024];
+
+ string sline;
+ string key,value;
+ map<string, mpi*> places;
+
+ FILE *fp=fopen(fname, "r");
+ if(!fp)
+ unixDie("opening file '"+string(fname)+"'");
+
+ rsa_init(rsa, RSA_PKCS_V15, 0, NULL, NULL );
+
+ places["Modulus"]=&rsa->N;
+ places["PublicExponent"]=&rsa->E;
+ places["PrivateExponent"]=&rsa->D;
+ places["Prime1"]=&rsa->P;
+ places["Prime2"]=&rsa->Q;
+ places["Exponent1"]=&rsa->DP;
+ places["Exponent2"]=&rsa->DQ;
+ places["Coefficient"]=&rsa->QP;
+
+ unsigned char decoded[1024];
+ DNSKEYRecordContent drc;
+ string modulus, exponent;
+ while(fgets(line, sizeof(line),fp)) {
+ sline.assign(line);
+ tie(key,value)=splitField(line, ':');
+ trim(value);
+
+ if(places.count(key)) {
+ if(places[key]) {
+
+ int len=sizeof(decoded);
+ if(base64_decode(decoded, &len, (unsigned char*)value.c_str(), value.length()) < 0) {
+ cerr<<"Error base64 decoding '"<<value<<"'\n";
+ exit(1);
+ }
+ // B64Decode(value, decoded);
+ // cerr<<key<<" decoded.length(): "<<8*len<<endl;
+ mpi_read_binary(places[key], decoded, len);
+ if(key=="Modulus")
+ modulus.assign((const char*)decoded,len);
+ if(key=="PublicExponent")
+ exponent.assign((const char*)decoded,len);
+ }
+ }
+ else {
+ if(key != "Private-key-format" && key != "Algorithm")
+ cerr<<"Unknown field '"<<key<<"'\n";
+ }
+ }
+ rsa->len = ( mpi_msb( &rsa->N ) + 7 ) >> 3; // no clue what this does
+
+ if(exponent.length() < 255)
+ drc.d_key.assign(1, (char) (unsigned int) exponent.length());
+ else {
+ drc.d_key.assign(1, 0);
+ uint16_t len=htons(exponent.length());
+ drc.d_key.append((char*)&len, 2);
+ }
+ drc.d_key.append(exponent);
+ drc.d_key.append(modulus);
+ drc.d_protocol=3;
+ drc.d_algorithm = 5;
+ fclose(fp);
+ return drc;
+}
+
+
+void makeRSAPublicKeyFromDNS(rsa_context* rc, const DNSKEYRecordContent& dkrc)
+{
+ rsa_init(rc, RSA_PKCS_V15, 0, NULL, NULL );
+
+ mpi_read_binary(&rc->E, (unsigned char*)dkrc.getExponent().c_str(), dkrc.getExponent().length()); // exponent
+ mpi_read_binary(&rc->N, (unsigned char*)dkrc.getModulus().c_str(), dkrc.getModulus().length()); // modulus
+ rc->len = ( mpi_msb( &rc->N ) + 7 ) >> 3; // no clue what this does
+}
+
+bool sharedDNSSECCompare(const shared_ptr<DNSRecordContent>& a, const shared_ptr<DNSRecordContent>& b)
+{
+ return a->serialize("", true) < b->serialize("", true);
+}
+
+string getSHA1HashForRRSET(const std::string& qname, const RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& signRecords)
+{
+ sort(signRecords.begin(), signRecords.end(), sharedDNSSECCompare);
+
+ string toHash;
+ toHash.append(const_cast<RRSIGRecordContent&>(rrc).serialize("", true));
+ toHash.resize(toHash.size() - rrc.d_signature.length()); // chop off the end;
+ // cerr<<"toHash start size: "<<toHash.size()<<", signature length: "<<rrc.d_signature.length()<<endl;
+
+
+ BOOST_FOREACH(shared_ptr<DNSRecordContent>& add, signRecords) {
+ // cerr<<"\t IN "<<rrc.d_originalttl<<"\t"<<add->getZoneRepresentation()<<"\n";
+ toHash.append(toLower(simpleCompress(qname, "")));
+ uint16_t tmp=htons(rrc.d_type);
+ toHash.append((char*)&tmp, 2);
+ tmp=htons(1); // class
+ toHash.append((char*)&tmp, 2);
+ uint32_t ttl=htonl(rrc.d_originalttl);
+ toHash.append((char*)&ttl, 4);
+ string rdata=add->serialize("", true); // case issues hiding here..
+ tmp=htons(rdata.length());
+ toHash.append((char*)&tmp, 2);
+ toHash.append(rdata);
+ }
+ // cerr<<"toHash: "<<makeHexDump(toHash)<<endl;
+ unsigned char hash[20];
+ sha1((unsigned char*)toHash.c_str(), toHash.length(), hash);
+ return string((char*)hash, 20);
+}
+
+DSRecordContent makeDSFromDNSKey(const std::string& qname, const DNSKEYRecordContent& drc, int digest)
+{
+ string toHash;
+ toHash.assign(toLower(simpleCompress(qname)));
+ toHash.append(const_cast<DNSKEYRecordContent&>(drc).serialize("", true));
+
+ unsigned char hash[32];
+ if(digest==1)
+ sha1((unsigned char*)toHash.c_str(), toHash.length(), hash);
+ else
+ sha2((unsigned char*)toHash.c_str(), toHash.length(), hash, 0);
+
+ DSRecordContent dsrc;
+ dsrc.d_algorithm=5;
+ dsrc.d_digesttype=digest;
+ dsrc.d_tag=const_cast<DNSKEYRecordContent&>(drc).getTag();
+ dsrc.d_digest.assign((const char*)hash, digest == 1 ? 20 : 32);
+ return dsrc;
+}
+
+DNSKEYRecordContent makeDNSKEYFromRSAKey(rsa_context* rc)
+{
+ DNSKEYRecordContent drc;
+ char tmp[256];
+
+ // cerr<<"in makeDNSKEY rsa_check_pubkey: "<<rsa_check_pubkey(rc)<<", bits="<<mpi_size(&rc->N)*8<<endl;
+
+ mpi_write_binary(&rc->E, (unsigned char*)tmp, mpi_size(&rc->E) );
+ string exponent((char*)tmp, mpi_size(&rc->E));
+
+ mpi_write_binary(&rc->N, (unsigned char*)tmp, mpi_size(&rc->N) );
+ string modulus((char*)tmp, mpi_size(&rc->N));
+
+ if(exponent.length() < 255)
+ drc.d_key.assign(1, (char) (unsigned int) exponent.length());
+ else {
+ drc.d_key.assign(1, 0);
+ uint16_t len=htons(exponent.length());
+ drc.d_key.append((char*)&len, 2);
+ }
+ drc.d_key.append(exponent);
+ drc.d_key.append(modulus);
+
+ drc.d_protocol=3;
+ drc.d_algorithm = 5;
+
+ drc.d_flags=256 + (modulus.length()>128); // oops, I just made this up..
+
+ return drc;
+}
+
+bool getSignerFor(const std::string& keyRepositoryDir, const std::string& qname, std::string &signer)
+{
+ DNSSECKeeper dk(keyRepositoryDir);
+
+ signer=qname;
+ do {
+ if(dk.haveKSKFor(signer))
+ return true;
+ } while(chopOff(signer));
+ return false;
+}
+
+int countLabels(const std::string& signQName)
+{
+ int count =1;
+ for(string::const_iterator pos = signQName.begin(); pos != signQName.end() ; ++pos)
+ if(*pos == '.' && pos+1 != signQName.end())
+ count++;
+
+ if(starts_with(signQName, "*."))
+ count--;
+ return count;
+}
+
+DNSKEYRecordContent getDNSKEYFor(const std::string& keyRepositoryDir, const std::string& qname, bool withKSK, RSAContext* rc)
+{
+ DNSSECKeeper dk(keyRepositoryDir);
+ cerr<<"Asked for a DNSKEY for '"<<qname<<"', withKSK="<<withKSK<<"\n";
+ DNSSECPrivateKey dpk;
+
+ if(!withKSK) {
+ DNSSECKeeper::zskset_t zskset=dk.getZSKsFor(qname);
+ BOOST_FOREACH(DNSSECKeeper::zskset_t::value_type value, zskset) {
+ if(value.second) {
+ cerr<<"Found a ZSK for '"<<qname<<"', key tag = "<<value.first.getDNSKEY().getTag()<<endl;
+ *rc=value.first.d_key;
+ return value.first.getDNSKEY();
+ }
+ else
+ cerr<<"Found an expired ZSK for '"<<qname<<"', key tag = "<<value.first.getDNSKEY().getTag()<<endl;
+ }
+ cerr<<"withKSK was not true, but found nothing!"<<endl;
+ exit(1);
+ }
+ else if(dk.haveKSKFor(qname, &dpk)) {
+ cerr<<"Found something"<<endl;
+ *rc=dpk.d_key;
+ return dpk.getDNSKEY();
+ } else {
+ cerr<<"DID NOT FIND A ZSK!"<<endl;
+ exit(1);
+ }
+}
+
+
+map<pair<string, uint16_t>, RRSIGRecordContent> g_rrsigs;
+
+void fillOutRRSIG(const std::string& keyrepodir, const std::string& signQName, RRSIGRecordContent& rrc, const std::string& hash, vector<shared_ptr<DNSRecordContent> >& toSign, bool withKSK)
+{
+ RSAContext rc;
+
+ DNSKEYRecordContent drc =getDNSKEYFor(keyrepodir, rrc.d_signer, withKSK, &rc);
+ rrc.d_tag = drc.getTag();
+
+ if(g_rrsigs.count(make_pair(hash, rrc.d_tag))) {
+ cerr<<"RRSIG cache hit !"<<endl;
+ rrc = g_rrsigs[make_pair(hash, rrc.d_tag)];
+ return;
+ }
+
+ string realhash=getSHA1HashForRRSET(signQName, rrc, toSign);
+
+ unsigned char signature[mpi_size(&rc.getContext().N)];
+
+ int ret=rsa_pkcs1_sign(&rc.getContext(), RSA_PRIVATE, SIG_RSA_SHA1, 20, (unsigned char*) realhash.c_str(), signature);
+
+ if(ret!=0) {
+ cerr<<"signing returned: "<<ret<<endl;
+ exit(1);
+ }
+
+ rrc.d_signature.assign((char*)signature, sizeof(signature));
+
+ g_rrsigs[make_pair(hash, rrc.d_tag)] = rrc;
+
+}
+
+uint32_t getCurrentInception()
+{
+ uint32_t now = time(0);
+ now -= (now % (7*86400));
+ return now;
+}
+
+
+int getRRSIGForRRSET(const std::string& keyrepodir, const std::string signQName, uint16_t signQType, uint32_t signTTL,
+ vector<shared_ptr<DNSRecordContent> >& toSign, RRSIGRecordContent& rrc, bool ksk)
+{
+ if(toSign.empty())
+ return -1;
+
+ rrc.d_type=signQType;
+ rrc.d_algorithm=5; // rsasha1
+ rrc.d_labels=countLabels(signQName);
+ rrc.d_originalttl=signTTL;
+ rrc.d_siginception=getCurrentInception();;
+ rrc.d_sigexpire = rrc.d_siginception + 14*86400;
+
+ rrc.d_tag=0;
+ if(!getSignerFor(keyrepodir, signQName, rrc.d_signer)) {
+ cerr<<"No signer known for '"<<signQName<<"'\n";
+ return -1;
+ }
+
+ string hash= getSHA1HashForRRSET(signQName, rrc, toSign);
+ fillOutRRSIG(keyrepodir, signQName, rrc, hash, toSign, ksk);
+ return 0;
+}
+
+void addSignature(const std::string& keyrepodir, const std::string signQName, const std::string& wildcardname, uint16_t signQType, uint32_t signTTL, DNSPacketWriter::Place signPlace, vector<shared_ptr<DNSRecordContent> >& toSign, DNSPacketWriter& pw)
+{
+ cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n";
+
+ RRSIGRecordContent rrc;
+ if(toSign.empty())
+ return;
+
+ for(int ksk = 0; ksk < 2; ++ksk) {
+ if(getRRSIGForRRSET(keyrepodir, wildcardname.empty() ? signQName : wildcardname, signQType, signTTL, toSign, rrc, ksk) < 0) {
+ cerr<<"Error signing a record!"<<endl;
+ return;
+ }
+
+ pw.startRecord(signQName, QType::RRSIG, 3600, 1,
+ signQType==QType::DNSKEY ? DNSPacketWriter:: ANSWER : signPlace);
+ rrc.toPacket(pw);
+
+ pw.commit();
+ if(signQType != QType::DNSKEY)
+ break;
+ }
+
+ toSign.clear();
+}
+
+
+std::string hashQNameWithSalt(unsigned int times, const std::string& salt, const std::string& qname)
+{
+ string toHash;
+ toHash.assign(simpleCompress(toLower(qname)));
+ toHash.append(salt);
+
+ cerr<<makeHexDump(toHash)<<endl;
+ unsigned char hash[20];
+ for(;;) {
+ sha1((unsigned char*)toHash.c_str(), toHash.length(), hash);
+ if(!times--)
+ break;
+ toHash.assign((char*)hash, 20);
+ toHash.append(salt);
+ }
+ return string((char*)hash, 20);
+}
--- /dev/null
+#ifndef PDNS_DNSSECINFRA_HH
+#define PDNS_DNSSECINFRA_HH
+#include "dnsrecords.hh"
+#include <polarssl/rsa.h>
+#include <boost/shared_ptr.hpp>
+#include <string>
+#include <vector>
+#include "misc.hh"
+
+struct CanonicalCompare: public binary_function<string, string, bool>
+{
+ bool operator()(const std::string& a, const std::string& b) {
+ std::vector<std::string> avect, bvect;
+
+ stringtok(avect, a, ".");
+ stringtok(bvect, b, ".");
+
+ reverse(avect.begin(), avect.end());
+ reverse(bvect.begin(), bvect.end());
+
+ return avect < bvect;
+ }
+};
+
+
+DNSKEYRecordContent getRSAKeyFromISC(rsa_context* rsa, const char* fname);
+
+void makeRSAPublicKeyFromDNS(rsa_context* rc, const DNSKEYRecordContent& dkrc);
+bool sharedDNSSECCompare(const boost::shared_ptr<DNSRecordContent>& a, const shared_ptr<DNSRecordContent>& b);
+string getSHA1HashForRRSET(const std::string& qname, const RRSIGRecordContent& rrc, std::vector<boost::shared_ptr<DNSRecordContent> >& signRecords);
+DNSKEYRecordContent makeDNSKEYFromRSAKey(rsa_context* rc);
+DSRecordContent makeDSFromDNSKey(const std::string& qname, const DNSKEYRecordContent& drc, int digest=1);
+
+bool getSignerFor(const std::string& keyrepodir, const std::string& qname, std::string &signer);
+int countLabels(const std::string& signQName);
+
+class RSAContext;
+
+DNSKEYRecordContent getDNSKEYFor(const std::string& keyrepodir, const std::string& qname, bool withKSK, RSAContext* rc);
+void fillOutRRSIG(const std::string& keyrepodir, const std::string& signQName, RRSIGRecordContent& rrc, const std::string& hash, vector<shared_ptr<DNSRecordContent> >& toSign, bool withKSK=false);
+uint32_t getCurrentInception();
+void addSignature(const std::string& keyrepodir, const std::string signQName, const std::string& wildcardname, uint16_t signQType, uint32_t signTTL, DNSPacketWriter::Place signPlace, vector<shared_ptr<DNSRecordContent> >& toSign, DNSPacketWriter& pw);
+int getRRSIGForRRSET(const std::string& keyrepodir, const std::string signQName, uint16_t signQType, uint32_t signTTL,
+ vector<shared_ptr<DNSRecordContent> >& toSign, RRSIGRecordContent &rrc, bool ksk);
+
+std::string hashQNameWithSalt(unsigned int times, const std::string& salt, const std::string& qname);
+
+#endif
--- /dev/null
+#include "dnsseckeeper.hh"
+#include "dnssecinfra.hh"
+#include "statbag.hh"
+#include <iostream>
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/path.hpp>
+#include <polarssl/havege.h>
+#include <polarssl/base64.h>
+#include <boost/foreach.hpp>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fstream>
+#include <boost/algorithm/string.hpp>
+#include <boost/format.hpp>
+
+namespace fs = boost::filesystem;
+
+using namespace std;
+using namespace boost;
+
+void RSAContext::create(unsigned int bits)
+{
+ havege_state hs;
+ havege_init( &hs );
+
+ rsa_init(&d_context, RSA_PKCS_V15, 0, havege_rand, &hs ); // FIXME this leaks memory
+ int ret=rsa_gen_key(&d_context, bits, 65537);
+ if(ret < 0)
+ throw runtime_error("Key generation failed");
+}
+
+std::string RSAContext::convertToISC()
+{
+ string ret;
+ typedef vector<pair<string, mpi*> > outputs_t;
+ outputs_t outputs;
+
+ outputs.push_back(make_pair("Modulus", &d_context.N));
+ outputs.push_back(make_pair("PublicExponent",&d_context.E));
+ outputs.push_back(make_pair("PrivateExponent",&d_context.D));
+ outputs.push_back(make_pair("Prime1",&d_context.P));
+ outputs.push_back(make_pair("Prime2",&d_context.Q));
+ outputs.push_back(make_pair("Exponent1",&d_context.DP));
+ outputs.push_back(make_pair("Exponent2",&d_context.DQ));
+ outputs.push_back(make_pair("Coefficient",&d_context.QP));
+
+ ret = "Private-key-format: v1.2\nAlgorithm: 5 (RSASHA1)\n";
+
+ BOOST_FOREACH(outputs_t::value_type value, outputs) {
+ ret += value.first;
+ ret += ": ";
+ unsigned char tmp[mpi_size(value.second)];
+ mpi_write_binary(value.second, tmp, sizeof(tmp));
+ unsigned char base64tmp[sizeof(tmp)*2];
+ int dlen=sizeof(base64tmp);
+ base64_encode(base64tmp, &dlen, tmp, sizeof(tmp));
+ ret.append((const char*)base64tmp, dlen);
+ ret.append(1, '\n');
+ }
+ return ret;
+}
+
+bool DNSSECKeeper::haveKSKFor(const std::string& zone, DNSSECPrivateKey* dpk)
+{
+ fs::path full_path = fs::system_complete( fs::path(d_dirname + "/" + zone + "/ksks/" ) );
+
+ if ( !fs::exists( full_path ) )
+ return false;
+
+ fs::directory_iterator end_iter;
+ for ( fs::directory_iterator dir_itr( full_path );
+ dir_itr != end_iter;
+ ++dir_itr )
+ {
+ // cerr<<"Entry: '"<< dir_itr->leaf() <<"'"<<endl;
+ if(ends_with(dir_itr->leaf(),".isc")) {
+ // cerr<<"Hit!"<<endl;
+
+ if(dpk) {
+ getRSAKeyFromISC(&dpk->d_key.getContext(), dir_itr->path().file_string().c_str());
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void DNSSECKeeper::addZSKFor(const std::string& name, bool next)
+{
+ DNSSECPrivateKey dpk;
+ dpk.d_key.create(1024); // for testing, 1024
+
+ string isc = dpk.d_key.convertToISC();
+ DNSKEYRecordContent drc = dpk.getDNSKEY();
+ drc.d_flags = 256; // KSK
+
+ string iscName=d_dirname+"/"+name+"/zsks/";
+ time_t inception=getCurrentInception();
+ time_t end=inception+14*86400;
+
+ if(next) {
+ inception += 14*86400;
+ end += 14*86400;
+ }
+
+ struct tm ts;
+ gmtime_r(&inception, &ts);
+
+ iscName += (boost::format("%04d%02d%02d%02d%02d")
+ % (1900+ts.tm_year) % (ts.tm_mon + 1)
+ % ts.tm_mday % ts.tm_hour % ts.tm_min).str();
+
+ iscName += "-";
+
+ gmtime_r(&end, &ts);
+ iscName += (boost::format("%04d%02d%02d%02d%02d.%u")
+ % (1900+ts.tm_year) % (ts.tm_mon + 1)
+ % ts.tm_mday % ts.tm_hour % ts.tm_min % drc.getTag()).str();
+
+ {
+ ofstream iscFile((iscName+".isc").c_str());
+ iscFile << isc;
+ }
+
+ {
+ ofstream dnskeyFile((iscName+".dnskey").c_str());
+ dnskeyFile << name << " IN DNSKEY " << drc.getZoneRepresentation()<<endl;
+ }
+
+}
+
+bool zskSortByDates(const DNSSECKeeper::zskset_t::value_type& a, const DNSSECKeeper::zskset_t::value_type& b)
+{
+ return
+ tie(a.first.beginValidity, a.first.endValidity) <
+ tie(b.first.beginValidity, b.first.endValidity);
+}
+
+DNSSECKeeper::zskset_t DNSSECKeeper::getZSKsFor(const std::string& zone, bool all)
+{
+ zskset_t zskset;
+
+ fs::path full_path = fs::system_complete( fs::path(d_dirname + "/" + zone + "/zsks/" ) );
+
+ if ( !fs::exists( full_path ) )
+ return zskset;
+
+ fs::directory_iterator end_iter;
+ for ( fs::directory_iterator dir_itr( full_path );
+ dir_itr != end_iter;
+ ++dir_itr )
+ {
+ // cerr<<"Entry: '"<< dir_itr->leaf() <<"'"<<endl;
+ if(ends_with(dir_itr->leaf(),".isc")) {
+ //cerr<<"Hit!"<<endl;
+ DNSSECPrivateKey dpk;
+ getRSAKeyFromISC(&dpk.d_key.getContext(), dir_itr->path().file_string().c_str());
+
+ struct tm ts1, ts2;
+
+ memset(&ts1, 0, sizeof(ts1));
+ memset(&ts2, 0, sizeof(ts2));
+
+ sscanf(dir_itr->leaf().c_str(), "%04d%02d%02d%02d%02d-%04d%02d%02d%02d%02d",
+ &ts1.tm_year,
+ &ts1.tm_mon, &ts1.tm_mday, &ts1.tm_hour, &ts1.tm_min,
+ &ts2.tm_year,
+ &ts2.tm_mon, &ts2.tm_mday, &ts2.tm_hour, &ts2.tm_min);
+
+ ts1.tm_year -= 1900;
+ ts2.tm_year -= 1900;
+
+ ts1.tm_mon--;
+ ts2.tm_mon--;
+
+ dpk.beginValidity=timegm(&ts1);
+ dpk.endValidity=timegm(&ts2);
+ time_t now=time(0);
+
+ zskset.push_back(make_pair(dpk, now > dpk.beginValidity && now < dpk.endValidity));
+ }
+ sort(zskset.begin(), zskset.end(), zskSortByDates);
+ }
+
+ return zskset;
+}
+
+DNSKEYRecordContent DNSSECPrivateKey::getDNSKEY()
+{
+ return makeDNSKEYFromRSAKey(&d_key.getContext());
+}
+
+
+void DNSSECKeeper::addZone(const std::string& name)
+{
+ mkdir((d_dirname+"/"+name).c_str(), 0700);
+ mkdir((d_dirname+"/"+name+"/ksks").c_str(), 0700);
+ mkdir((d_dirname+"/"+name+"/zsks").c_str(), 0700);
+
+ DNSSECPrivateKey dpk;
+ dpk.d_key.create(2048); // for testing, 1024
+
+ string isc = dpk.d_key.convertToISC();
+ DNSKEYRecordContent drc = dpk.getDNSKEY();
+ drc.d_flags = 257; // ZSK
+
+ string iscName=d_dirname+"/"+name+"/ksks/";
+
+ time_t now=time(0);
+ struct tm ts;
+ gmtime_r(&now, &ts);
+ iscName += (boost::format("%04d%02d%02d%02d%02d.%u")
+ % (1900+ts.tm_year) % (ts.tm_mon + 1)
+ % ts.tm_mday % ts.tm_hour % ts.tm_min % drc.getTag()).str();
+
+
+ {
+ ofstream iscFile((iscName+".isc").c_str());
+ iscFile << isc;
+ }
+
+ {
+ ofstream dnskeyFile((iscName+".dnskey").c_str());
+ dnskeyFile << name << " IN DNSKEY " << drc.getZoneRepresentation()<<endl;
+ }
+
+}
+
+
--- /dev/null
+#ifndef PDNSDNSSECKEEPER_HH
+#define PDNSDNSSECKEEPER_HH
+#include <string>
+#include <polarssl/rsa.h>
+#include <string.h>
+#include <vector>
+#include "dnsrecords.hh"
+
+#define PDNSSEC_MI(x) mpi_init(&d_context.x, 0)
+#define PDNSSEC_MC(x) PDNSSEC_MI(x); mpi_copy(&d_context.x, const_cast<mpi*>(&orig.d_context.x))
+#define PDNSSEC_MF(x) mpi_free(&d_context.x, 0)
+
+class RSAContext
+{
+public:
+ RSAContext()
+ {
+ memset(&d_context, 0, sizeof(d_context));
+ PDNSSEC_MI(N);
+ PDNSSEC_MI(E); PDNSSEC_MI(D); PDNSSEC_MI(P); PDNSSEC_MI(Q); PDNSSEC_MI(DP); PDNSSEC_MI(DQ); PDNSSEC_MI(QP); PDNSSEC_MI(RN); PDNSSEC_MI(RP); PDNSSEC_MI(RQ);
+ }
+
+ ~RSAContext()
+ {
+ PDNSSEC_MF(N);
+ PDNSSEC_MF(E); PDNSSEC_MF(D); PDNSSEC_MF(P); PDNSSEC_MF(Q); PDNSSEC_MF(DP); PDNSSEC_MF(DQ); PDNSSEC_MF(QP); PDNSSEC_MF(RN); PDNSSEC_MF(RP); PDNSSEC_MF(RQ);
+ }
+
+ RSAContext(const RSAContext& orig)
+ {
+ d_context.ver = orig.d_context.ver;
+ d_context.len = orig.d_context.len;
+
+ d_context.padding = orig.d_context.padding;
+ d_context.hash_id = orig.d_context.hash_id;
+ d_context.f_rng = orig.d_context.f_rng;
+ d_context.p_rng = orig.d_context.p_rng;
+ PDNSSEC_MC(N);
+ PDNSSEC_MC(E); PDNSSEC_MC(D); PDNSSEC_MC(P); PDNSSEC_MC(Q); PDNSSEC_MC(DP); PDNSSEC_MC(DQ); PDNSSEC_MC(QP); PDNSSEC_MC(RN); PDNSSEC_MC(RP); PDNSSEC_MC(RQ);
+ }
+
+ RSAContext& operator=(const RSAContext& orig)
+ {
+ d_context.ver = orig.d_context.ver;
+ d_context.len = orig.d_context.len;
+
+ d_context.padding = orig.d_context.padding;
+ d_context.hash_id = orig.d_context.hash_id;
+ d_context.f_rng = orig.d_context.f_rng;
+ d_context.p_rng = orig.d_context.p_rng;
+
+ PDNSSEC_MC(N);
+ PDNSSEC_MC(E); PDNSSEC_MC(D); PDNSSEC_MC(P); PDNSSEC_MC(Q); PDNSSEC_MC(DP); PDNSSEC_MC(DQ); PDNSSEC_MC(QP); PDNSSEC_MC(RN); PDNSSEC_MC(RP); PDNSSEC_MC(RQ);
+ return *this;
+ }
+
+ rsa_context& getContext()
+ {
+ return d_context;
+ }
+
+ void create(unsigned int bits);
+ std::string convertToISC();
+
+private:
+ rsa_context d_context;
+};
+
+#undef PDNSSEC_MC
+#undef PDNSSEC_MI
+#undef PDNSSEC_MF
+
+struct DNSSECPrivateKey
+{
+ uint16_t getTag();
+
+ RSAContext d_key;
+ DNSKEYRecordContent getDNSKEY();
+ time_t beginValidity, endValidity; // wart
+};
+
+class DNSSECKeeper
+{
+public:
+ explicit DNSSECKeeper(const std::string& dirname) : d_dirname(dirname){}
+ bool haveKSKFor(const std::string& zone, DNSSECPrivateKey* ksk=0);
+
+ typedef std::vector<std::pair<DNSSECPrivateKey, bool> > zskset_t;
+ zskset_t getZSKsFor(const std::string& zone, bool all=false);
+ void addZSKFor(const std::string& fname, bool next=false);
+
+ void addZone(const std::string& fname);
+
+private:
+ std::string d_dirname;
+};
+
+#endif