def bootstrap(): global trust_anchor, signer import_safebag("sec/server.safebag", "1234") import_cert("sec/server.ndncert") with open("sec/server.safebag", "r") as safebag: wire = safebag.read() wire = base64.b64decode(wire) wire = parse_and_check_tl(wire, SecurityV2TypeNumber.SAFE_BAG) bag = SafeBag.parse(wire) testbed_signed = CertificateV2Value.parse(bag.certificate_v2) server_key_name = Name.to_str(testbed_signed.name[:-2]) privateKey = serialization.load_der_private_key( bytes(bag.encrypted_key_bag), password=b'1234', backend=default_backend()) server_prv_key = privateKey.private_bytes(Encoding.DER, PrivateFormat.PKCS8, NoEncryption()) signer = Sha256WithEcdsaSigner(server_key_name, server_prv_key) with open("sec/testbed.anchor", "r") as ndncert: wire = ndncert.read() wire = base64.b64decode(wire) trust_anchor = parse_certificate(wire)
def list_key_tree(self): """ Return the id-key-cert tree in a JSON like dict object. """ def get_key_type(key): key = bytes(key) try: RSA.import_key(key) return 'RSA' except ValueError: pass try: ECC.import_key(key) return 'ECC' except ValueError: pass return 'Unknown' sig_type_dic = { SignatureType.NOT_SIGNED: 'NotSigned', SignatureType.DIGEST_SHA256: 'DigestSha256', SignatureType.SHA256_WITH_RSA: 'SignatureSha256WithRsa', SignatureType.SHA256_WITH_ECDSA: 'SignatureSha256WithEcdsa', SignatureType.HMAC_WITH_SHA256: 'SignatureHmacWithSha256' } pib = self.app.keychain ret = {} for id_name, id_obj in pib.items(): cur_id = {'default': '*' if id_obj.is_default else ' ', 'keys': {}} for key_name, key_obj in id_obj.items(): cur_key = { 'default': '*' if key_obj.is_default else ' ', 'key_type': get_key_type(key_obj.key_bits), 'certs': {} } for cert_name, cert_obj in key_obj.items(): cert_v2 = parse_certificate(cert_obj.data) cur_cert = { 'default': '*' if cert_obj.is_default else ' ', 'not_before': bytes(cert_v2.signature_info.validity_period.not_before ).decode(), 'not_after': bytes(cert_v2.signature_info.validity_period.not_after ).decode(), 'issuer_id': Component.to_str(cert_v2.name[-2]), 'key_locator': Name.to_str(cert_v2.signature_info.key_locator.name), 'signature_type': sig_type_dic.get(cert_v2.signature_info.signature_type, 'Unknown') } cur_key['certs'][Name.to_str(cert_name)] = cur_cert cur_id['keys'][Name.to_str(key_name)] = cur_key ret[Name.to_str(id_name)] = cur_id return ret
async def verify_ecdsa_signature(name: FormalName, sig: SignaturePtrs) -> bool: global trust_anchor logging.debug("Validating certificate %s", Name.to_str(name)) sig_info = sig.signature_info covered_part = sig.signature_covered_part sig_value = sig.signature_value_buf if not sig_info or sig_info.signature_type != SignatureType.SHA256_WITH_ECDSA: return False if not covered_part or not sig_value: return False key_name = sig_info.key_locator.name[0:] logging.debug('Extracting key_name: ', Name.to_str(key_name)) # check it's testbed root key_bits = None if Name.to_str(key_name) == Name.to_str(trust_anchor.name[:-2]): logging.debug('Reaching the trust anchor, begin to return') key_bits = bytes(trust_anchor.content) else: try: cert_name, meta_info, content, raw_packet = await app.express_interest( key_name, must_be_fresh=True, can_be_prefix=True, lifetime=6000, need_raw_packet=True, validator=verify_ecdsa_signature) # certificate itself is a Data packet cert = parse_certificate(raw_packet) # load public key from the data content key_bits = None try: key_bits = bytes(content) except (KeyError, AttributeError): logging.debug('Cannot load pub key from received certificate') return False except InterestNack as e: logging.debug(f'Nacked with reason={e.reason}') except InterestTimeout: logging.debug(f'Timeout') except InterestCanceled: logging.debug(f'Canceled') except ValidationFailure: logging.debug(f'Data failed to validate') pk = ECC.import_key(key_bits) verifier = DSS.new(pk, 'fips-186-3', 'der') sha256_hash = SHA256.new() for blk in covered_part: sha256_hash.update(blk) try: verifier.verify(sha256_hash, bytes(sig_value)) except ValueError: logging.debug('Certificate validation failed! %s', Name.to_str(name)) return False logging.debug('Certificate validated! %s', Name.to_str(name)) return True
def process_cert_request(self, name, app_param): logging.info("[CERT REQ]: interest received") logging.info(name) if not app_param: logging.error("[CERT REQ]: interest has no parameter") return request = CertRequest.parse(app_param) if not request.identifier or not request.ecdh_n2 or not request.anchor_digest or not request.ecdh_n1: raise KeyError("[CERT REQ]: lacking parameters in application parameters") logging.info(bytes(request.identifier)) logging.info(bytes(request.ecdh_n2)) logging.info(bytes(request.anchor_digest)) logging.info(bytes(request.ecdh_n1)) if bytes(request.identifier) != self.boot_state['DeviceIdentifier'] or \ bytes(request.ecdh_n2) != self.boot_state['N2PublicKey'] or \ bytes(request.anchor_digest) != self.boot_state['TrustAnchorDigest'] or \ bytes(request.ecdh_n1) != self.boot_state['N1PublicKey']: logging.error("[CERT REQ]: unauthenticated request") return # anchor signed certificate # create identity and key for the device device_name = self.system_prefix + '/' + bytes(request.identifier).decode() device_key = self.app.keychain.touch_identity(device_name).default_key() private_key = get_prv_key_from_safe_bag(device_name) default_cert = device_key.default_cert().data # resign certificate using anchor's key cert = parse_certificate(default_cert) new_cert_name = cert.name[:-2] new_cert_name.append('home') new_cert_name.append('001') cert = self.app.prepare_data(new_cert_name, cert.content, identity=self.system_prefix) # AES iv = urandom(16) cipher = AES.new(self.boot_state['SharedKey'], AES.MODE_CBC, iv) ct_bytes = cipher.encrypt(pad(private_key, AES.block_size)) logging.info('Symmetic Key') logging.info(self.boot_state['SharedKey']) # AES IV logging.info("IV:") logging.info(iv) # encrpted device private key with temporary symmetric key ct = b64encode(ct_bytes) logging.info("Cipher:") logging.info(ct) response = CertResponse() response.cipher = ct response.iv = iv cert_bytes = parse_and_check_tl(cert, TypeNumber.DATA) response.id_cert = cert_bytes signer = HmacSha256Signer('pre-shared', self.boot_state['SharedSymmetricKey']) self.app.put_data(name, response.encode(), freshness_period=3000, signer=signer)
async def main(): global local_anchor import_safebag("sec/client.safebag", "1234") # parse again to read prv key into memory request = CertRequest() with open("sec/client.safebag", "r") as safebag: wire = safebag.read() wire = base64.b64decode(wire) wire = parse_and_check_tl(wire, SecurityV2TypeNumber.SAFE_BAG) bag = SafeBag.parse(wire) # attach the testbed-signed certificate request.testbed_signed = bag.certificate_v2 # parse the key bag to obtain private key testbed_signed = CertificateV2Value.parse(bag.certificate_v2) key_bag = bytes(bag.encrypted_key_bag) privateKey = serialization.load_der_private_key(key_bag, password=b'1234', backend=default_backend()) client_prv_key = privateKey.private_bytes(Encoding.DER, PrivateFormat.PKCS8, NoEncryption()) # parse trust anchor and self-assigns a name, then create self-signed certificate with open("sec/client.anchor", "r") as anchor: wire = anchor.read() wire = base64.b64decode(wire) local_anchor = parse_certificate(wire) # self-assign a name and create corresponding key pair client_name = local_anchor.name[:-4] + [testbed_signed.name[-5]] client_identity = app.keychain.touch_identity(client_name) # attach newly generated self-assigned certificate cert = client_identity.default_key().default_cert().data cert = parse_certificate(cert) request.self_signed = cert.encode() try: # express the first interest to fetch a token/secret code timestamp = ndn.utils.timestamp() name = Name.from_str('/edge/_ca/new-cert') + [Component.from_timestamp(timestamp)] logging.info(f'Sending Interest {Name.to_str(name)}, {InterestParam(must_be_fresh=True, lifetime=6000)}') data_name, meta_info, content = await app.express_interest( name, app_param=request.encode(), must_be_fresh=True, can_be_prefix=False, lifetime=6000, identity=client_identity, validator=verify_ecdsa_signature) # sign it use the private key, to prove the certificate possesion h = SHA256.new() h.update(bytes(content)) pk = ECC.import_key(client_prv_key) signature = DSS.new(pk, 'fips-186-3', 'der').sign(h) logging.info(f'Getting Data {Name.to_str(name)}, begin signing the token {bytes(content)}') # express the second interest to fetch the issued certificate name = Name.from_str('/edge/_ca/challenge') + [Component.from_timestamp(timestamp)] logging.info(f'Sending Interest {Name.to_str(name)}, {InterestParam(must_be_fresh=True, lifetime=6000)}') data_name, meta_info, content = await app.express_interest( name, app_param=signature, must_be_fresh=True, can_be_prefix=False, lifetime=6000, identity=client_identity, validator=verify_ecdsa_signature) # parse the issued certificate and install issued_cert = parse_certificate(content) logging.info("Issued certificate: %s", Name.to_str(issued_cert.name)) app.keychain.import_cert(Name.to_bytes(issued_cert.name[:-2]), Name.to_bytes(issued_cert.name), bytes(content)) except InterestNack as e: print(f'Nacked with reason={e.reason}') except InterestTimeout: print(f'Timeout') except InterestCanceled: print(f'Canceled') except ValidationFailure: print(f'Data failed to validate') app.shutdown()
def process_cert_request(self, name, app_param): logging.info("[CERT REQ]: interest received") logging.info(name) if not app_param: logging.error("[CERT REQ]: interest has no parameter") return request = CertRequest.parse(app_param) if not request.identifier or not request.ecdh_n2 or not request.anchor_digest or not request.ecdh_n1: raise KeyError( "[CERT REQ]: lacking parameters in application parameters") logging.info(bytes(request.identifier)) logging.info(bytes(request.ecdh_n2)) logging.info(bytes(request.anchor_digest)) logging.info(bytes(request.ecdh_n1)) if bytes(request.identifier) != self.boot_state['DeviceIdentifier'] or \ bytes(request.ecdh_n2) != self.boot_state['N2PublicKey'] or \ bytes(request.anchor_digest) != self.boot_state['TrustAnchorDigest'] or \ bytes(request.ecdh_n1) != self.boot_state['N1PublicKey']: logging.error("[CERT REQ]: unauthenticated request") return # anchor signed certificate # create identity and key for the device # TODO Remove hardcoded livingroom and ask user for which room the device belongs to m_measure_tp1 = time.time() device_name = '/' + self.system_prefix + '/livingroom' + '/' + bytes( request.identifier).decode() device_key = self.app.keychain.touch_identity( device_name).default_key() private_key = get_prv_key_from_safe_bag(device_name) default_cert = device_key.default_cert().data m_measure_tp2 = time.time() logging.debug( F'BOOTSTRAPPING-DATA2-ECDSA-KENGEN: {m_measure_tp2 - m_measure_tp1}' ) # re-sign certificate using anchor's key cert = parse_certificate(default_cert) new_cert_name = cert.name[:-2] logging.debug(new_cert_name) new_cert_name.append('home') new_cert_name.append( Name.Component.from_version(SystemRandom().randint( 10000000, 99999999))) logging.debug(new_cert_name) m_measure_tp1 = time.time() cert = self.app.prepare_data(new_cert_name, cert.content, identity=self.system_prefix) m_measure_tp2 = time.time() logging.debug( F'BOOTSTRAPPING-DATA2-ECDSA-RESIGN: {m_measure_tp2 - m_measure_tp1}' ) m_measure_tp1 = time.time() # AES iv = urandom(16) cipher = AES.new(self.boot_state['SharedAESKey'], AES.MODE_CBC, iv) ct_bytes = cipher.encrypt(private_key) m_measure_tp2 = time.time() logging.debug( F'BOOTSTRAPPING-DATA2-AES-ENC: {m_measure_tp2 - m_measure_tp1}') logging.info('raw private key') logging.info(private_key) logging.info('Symmetric Key') logging.info(self.boot_state['SharedAESKey']) # AES IV logging.info("IV:") logging.info(iv) logging.info("Cipher:") logging.info(ct_bytes) logging.info('Cipher length: ' + str(len(ct_bytes))) # encrypted device private key with temporary symmetric key # ct = b64encode(ct_bytes) response = CertResponse() response.cipher = ct_bytes response.iv = iv cert_bytes = parse_and_check_tl(cert, TypeNumber.DATA) response.id_cert = cert_bytes m_measure_tp1 = time.time() signer = HmacSha256Signer('pre-shared', self.boot_state['SharedSymmetricKey']) self.app.put_data(name, response.encode(), freshness_period=3000, signer=signer) m_measure_tp2 = time.time() logging.debug( F'BOOTSTRAPPING-DATA2-ECDSA+ENCODING: {m_measure_tp2 - m_measure_tp1}' ) self.boot_state["DeviceIdentityName"] = device_name self.boot_state['Success'] = True self.boot_event.set()
def test_signing_suggest(): with TemporaryDirectory() as tmpdirname: pib_file = os.path.join(tmpdirname, 'pib.db') tpm_dir = os.path.join(tmpdirname, 'privKeys') KeychainSqlite3.initialize(pib_file, 'tpm-file', tpm_dir) keychain = KeychainSqlite3(pib_file, TpmFile(tpm_dir)) assert len(keychain) == 0 la_id = keychain.touch_identity('/la') la_cert = la_id.default_key().default_cert().data la_cert_data = parse_certificate(la_cert) la_cert_name = la_cert_data.name la_signer = keychain.get_signer({'cert': la_cert_name}) la_author_id = keychain.touch_identity('/la/author/1') la_author_cert_name, la_author_cert = derive_cert(la_author_id.default_key().name, Component.from_str('la-signer'), la_cert_data.content, la_signer, datetime.utcnow(), 100) keychain.import_cert(la_id.default_key().name, la_author_cert_name, la_author_cert) ny_id = keychain.touch_identity('/ny') ny_cert = ny_id.default_key().default_cert().data ny_cert_data = parse_certificate(ny_cert) ny_cert_name = ny_cert_data.name ny_signer = keychain.get_signer({'cert': ny_cert_name}) ny_author_id = keychain.touch_identity('/ny/author/2') ny_author_cert_name, ny_author_cert = derive_cert(ny_author_id.default_key().name, Component.from_str('ny-signer'), ny_cert_data.content, ny_signer, datetime.utcnow(), 100) keychain.import_cert(ny_id.default_key().name, ny_author_cert_name, ny_author_cert) lvs = r''' #KEY: "KEY"/_/_/_ #article: /"article"/_topic/_ & { _topic: "eco" | "spo" } <= #author #author: /site/"author"/_/#KEY <= #anchor #anchor: /site/#KEY & {site: "la" | "ny" } ''' checker = Checker(compile_lvs(lvs), {}) assert checker.suggest("/article/eco/day1", keychain) == la_author_cert_name assert checker.suggest("/article/life/day1", keychain) is None lvs = r''' #KEY: "KEY"/_/_/_ #LAKEY: "KEY"/_/_signer/_ & { _signer: "la-signer" } #article: /"article"/_topic/_ & { _topic: "eco" | "spo" } <= #author #author: /site/"author"/_/#LAKEY <= #anchor #anchor: /site/#KEY & {site: "la"} ''' checker = Checker(compile_lvs(lvs), {}) assert checker.suggest("/article/eco/day1", keychain) == la_author_cert_name lvs = r''' #KEY: "KEY"/_/_/_version & { _version: $eq_type("v=0") } #article: /"article"/_topic/_ & { _topic: "life" | "fin" } <= #author #author: /site/"author"/_/#KEY & { site: "ny" } <= #anchor #anchor: /site/#KEY & { site: "ny" } ''' checker = Checker(compile_lvs(lvs), DEFAULT_USER_FNS) assert checker.suggest("/article/fin/day1", keychain) == ny_author_cert_name lvs = r''' #KEY: "KEY"/_/_/_version & { _version: $eq_type("v=0") } #NYKEY: "KEY"/_/_signer/_version& { _signer: "ny-signer", _version: $eq_type("v=0")} #article: /"article"/_topic/_ <= #author #author: /site/"author"/_/#NYKEY <= #anchor #anchor: /site/#KEY & {site: "ny"} #site: "ny" ''' checker = Checker(compile_lvs(lvs), DEFAULT_USER_FNS) assert checker.suggest("/article/eco/day1", keychain) == ny_author_cert_name