def get_certs(self, host=None, port=None, **kwargs): """Returns XML response containing certificate data and their timestamps. Only leaf (EE) certificates are included, not the whole chain. @param host: hostname @param port: port where service runs """ if (host is None or port is None): raise cherrypy.HTTPError(400) try: host_port = str(host + ":" + port) service_id = notary_common.ObservedServer(host_port) except ValueError: raise cherrypy.HTTPError(400) logger.info("Certs request '%s'" % service_id) ee_certs = notary_common.get_ee_certs(service_id) if len(ee_certs) == 0: self.launch_on_demand_probe(service_id) raise cherrypy.HTTPError(404) to_be_signed = host_port #Data to be signed are simple concatenation of following: # - service_id as string in the form 'host:port' #Then, for each 'certificate' element, following attributes #(ints are network order): # - DER encoded cert body, uint32 start, uint32 end dom_impl = getDOMImplementation() new_doc = dom_impl.createDocument(None, "notary_reply", None) top_element = new_doc.documentElement top_element.setAttribute("version", "1") top_element.setAttribute("sig_type", "rsa-sha256") for ee_cert in ee_certs: cert_elem = new_doc.createElement("certificate") top_element.appendChild(cert_elem) cert_elem.setAttribute("body", base64.standard_b64encode(ee_cert.cert)) to_be_signed += ee_cert.cert cert_elem.setAttribute("start", str(ee_cert.start_ts)) cert_elem.setAttribute("end", str(ee_cert.end_ts)) to_be_signed += struct.pack("!2I", ee_cert.start_ts, ee_cert.end_ts) sig = self.sign_rsa_base64(to_be_signed, digest="sha256") top_element.setAttribute("sig", sig) cherrypy.response.headers['Content-Type'] = 'text/xml' return new_doc.toprettyxml()
def get_xml(self, service_id): """Return xml with certificates' fingerprints. @param service_id: requested service @type service_id: notary_common.ObservedServer """ logger.info("Request for '%s'" % service_id) ee_certs = notary_common.get_ee_certs(service_id) ee_certs_by_key = {} keys = [] for ee_cert in ee_certs: md5_fp = hashlib.md5(ee_cert.cert).digest() k = md5_fp if k not in ee_certs_by_key: ee_certs_by_key[k] = [] keys.append(k) ee_certs_by_key[k].append(ee_cert) #If we have no record of the service_id, launch a on-demand #scan, but only if the scan for the same service_id is not #already scheduled or being scanned. #Perspectives Firefox extensions likes to fire 3 requests #often faster than we get the scan results, so this way we won't #clog up the queue with unnecessary scans. if len(ee_certs) == 0: self.launch_on_demand_probe(service_id) # return 404, assume client will re-query raise cherrypy.HTTPError(404) dom_impl = getDOMImplementation() new_doc = dom_impl.createDocument(None, "notary_reply", None) top_element = new_doc.documentElement top_element.setAttribute("version", "1") top_element.setAttribute("sig_type", "rsa-md5") ## Packed data format: #service-id (variable length, terminated with null-byte) #num_timespans (2-bytes) #key_len_bytes (2-bytes, 16 for version 1 - md5 only) #key type (1-byte), always has a value of 3 for SSL #key (md5) data (length specified in key_len_bytes) #list of timespan start,end pairs (length is 2 * 4 * num_timespans) packed_data = "" for k in keys: md5_fp = k key_elem = new_doc.createElement("key") key_elem.setAttribute("type","ssl") fp_len = len(md5_fp) fp_bytes = md5_fp key_elem.setAttribute("fp", self._unpack_hex_with_colons(md5_fp)) top_element.appendChild(key_elem) num_timespans = len(ee_certs_by_key[k]) TYPE_SSL = 3 #from FF extension's comments head = struct.pack("!2HB", num_timespans, fp_len, TYPE_SSL) ts_bytes = "" for ee_cert in sorted(ee_certs_by_key[k], key=lambda ee_cert: ee_cert.start_ts): ts_start = ee_cert.start_ts ts_end = ee_cert.end_ts ts_elem = new_doc.createElement("timestamp") ts_elem.setAttribute("end",str(ts_end)) ts_elem.setAttribute("start", str(ts_start)) key_elem.appendChild(ts_elem) ts_bytes += struct.pack("!I", ts_start) ts_bytes += struct.pack("!I", ts_end) packed_data =(head + fp_bytes + ts_bytes) + packed_data packed_data = service_id.old_str() + struct.pack("B", 0) + packed_data sig = self.sign_rsa_base64(packed_data, digest="md5") top_element.setAttribute("sig",sig) return top_element.toprettyxml()