def get_backup_auth_from_node(self, base_url, auth_token, hive_did): rt, status_code, err = self.post(base_url + '/api/v1/did/backup_auth', {"jwt": auth_token}) if err != None: return None, "Post backup_auth error: " + err token = rt["backup_token"] if token is None: return None, "Token is none." jws = lib.DefaultJWSParser_Parse(token.encode()) if not jws: return None, "Backup token DefaultJWSParser_Parse error: " + ffi.string( lib.DIDError_GetLastErrorMessage()).decode() aud = ffi.string(lib.JWT_GetAudience(jws)).decode() if aud != self.get_did_string(): lib.JWT_Destroy(jws) return None, "Audience is error." issuer = ffi.string(lib.JWT_GetIssuer(jws)).decode() lib.JWT_Destroy(jws) if issuer is None: return None, "Issuer is none." if issuer != hive_did: return None, "Issuer is error." return token, None
def backup_client_auth(self, host_url, challenge_response, backup_service_instance_did): """ for vault /backup & /restore :return backup access token """ body = self.http.post(host_url + URL_DID_BACKUP_AUTH, None, {"challenge_response": challenge_response}) if 'token' not in body or not body["token"]: raise InvalidParameterException( msg='backup_auth: failed to backup auth to backup node.') jws = lib.DefaultJWSParser_Parse(body["token"].encode()) if not jws: raise InvalidParameterException( msg= f'backup_auth: failed to parse token with error {self.get_error_message()}.' ) audience = ffi.string(lib.JWT_GetAudience(jws)).decode() if audience != self.get_did_string(): lib.JWT_Destroy(jws) raise InvalidParameterException( msg=f'backup_auth: failed to get the audience of the challenge.' ) issuer = ffi.string(lib.JWT_GetIssuer(jws)).decode() lib.JWT_Destroy(jws) if issuer != backup_service_instance_did: raise InvalidParameterException( msg=f'backup_auth: failed to get the issuer of the challenge.') return body["token"]
def __get_app_instance_did(self, app_instance_doc): doc_str = json.dumps(app_instance_doc) app_instance_doc = lib.DIDDocument_FromJson(doc_str.encode()) if not app_instance_doc or lib.DIDDocument_IsValid( app_instance_doc) != 1: raise BadRequestException( msg='The did document is invalid in getting app instance did.') app_instance_did = lib.DIDDocument_GetSubject(app_instance_doc) if not app_instance_did: raise BadRequestException( msg='Can not get did from document in getting app instance did.' ) spec_did_str = ffi.string( lib.DID_GetMethodSpecificId(app_instance_did)).decode() try: with open(hive_setting.DID_DATA_LOCAL_DIDS + os.sep + spec_did_str, "w") as f: f.write(doc_str) except Exception as e: logging.getLogger("HiveAuth").error( f"Exception in sign_in:{str(e)} in getting app instance did") return "did:" + ffi.string( lib.DID_GetMethod(app_instance_did)).decode() + ":" + spec_did_str
def backup_client_sign_in(self, host_url, credential, subject): """ for vault /backup & /restore :return challenge_response, backup_service_instance_did """ vc = lib.Credential_FromJson(credential.encode(), ffi.NULL) if not vc: raise InvalidParameterException( msg='backup_sign_in: invalid credential.') doc_str = ffi.string( lib.DIDDocument_ToJson(lib.DIDStore_LoadDID(self.store, self.did), True)).decode() doc = json.loads(doc_str) body = self.http.post(host_url + URL_DID_SIGN_IN, None, {"id": doc}) if 'challenge' not in body or not body["challenge"]: raise InvalidParameterException( msg='backup_sign_in: failed to sign in to backup node.') jws = lib.DefaultJWSParser_Parse(body["challenge"].encode()) if not jws: raise InvalidParameterException( msg= f'backup_sign_in: failed to parse challenge with error {self.get_error_message()}.' ) aud = ffi.string(lib.JWT_GetAudience(jws)).decode() if aud != self.get_did_string(): lib.JWT_Destroy(jws) raise InvalidParameterException( msg= f'backup_sign_in: failed to get the audience of the challenge.' ) nonce = ffi.string(lib.JWT_GetClaim(jws, "nonce".encode())).decode() if nonce is None: lib.JWT_Destroy(jws) raise InvalidParameterException( msg=f'backup_sign_in: failed to get the nonce of the challenge.' ) issuer = ffi.string(lib.JWT_GetIssuer(jws)).decode() lib.JWT_Destroy(jws) if issuer is None: raise InvalidParameterException( msg= f'backup_sign_in: failed to get the issuer of the challenge.') vp_json = self.create_presentation(vc, nonce, issuer) if vp_json is None: raise InvalidParameterException( msg=f'backup_sign_in: failed to create presentation.') challenge_response = self.create_vp_token( vp_json, subject, issuer, hive_setting.AUTH_CHALLENGE_EXPIRED) if challenge_response is None: raise InvalidParameterException( msg=f'backup_sign_in: failed to create the challenge response.' ) return challenge_response, issuer
def verify_order_proof(self, proof, user_did, order_id): # INFO:DefaultJWSParser_Parse will validate the sign information. jws = lib.DefaultJWSParser_Parse(proof.encode()) if not jws: raise BadRequestException( msg=self.get_error_message('parse the proof error')) issuer = lib.JWT_GetIssuer(jws) if not issuer: lib.JWT_Destroy(jws) raise BadRequestException( msg=self.get_error_message('the issue of the proof error')) if self.did_str != ffi.string(issuer).decode(): lib.JWT_Destroy(jws) raise BadRequestException( msg= f'the issue of the proof not match: {ffi.string(issuer).decode()}' ) audience = lib.JWT_GetAudience(jws) if not audience: lib.JWT_Destroy(jws) raise BadRequestException( msg=self.get_error_message('the audience of the proof error')) if user_did != ffi.string(audience).decode(): lib.JWT_Destroy(jws) raise BadRequestException( msg= f'the audience of the proof not match: {ffi.string(audience).decode()}' ) props = lib.JWT_GetClaim(jws, "props".encode()) if not props: lib.JWT_Destroy(jws) raise BadRequestException( msg=self.get_error_message('the claim of the proof error')) props_json = json.loads(ffi.string(props).decode()) if props_json.get('order_id') != order_id: lib.JWT_Destroy(jws) raise BadRequestException( msg= f'the order_id of the proof not match: {props_json.get("order_id")}' ) if hive_setting.PAYMENT_CHECK_EXPIRED: expired = lib.JWT_GetExpiration(jws) now = int(datetime.now().timestamp()) if now > expired: lib.JWT_Destroy(jws) raise BadRequestException( msg=f'the proof is expired (valid for 7 days)') lib.JWT_Destroy(jws)
def __get_auth_token_by_challenge(self, challenge, did: DIDApp): jws = lib.DefaultJWSParser_Parse(challenge.encode()) assert jws, f'Cannot get challenge: {ffi.string(lib.DIDError_GetLastErrorMessage()).decode()}' aud = ffi.string(lib.JWT_GetAudience(jws)).decode() assert aud == self.app_did.get_did_string() nonce = ffi.string(lib.JWT_GetClaim(jws, "nonce".encode())).decode() hive_did = self.__get_issuer_by_challenge2(jws) lib.JWT_Destroy(jws) # auth vc = did.issue_auth(self.app_did) vp_json = self.app_did.create_presentation(vc, nonce, hive_did) return self.app_did.create_vp_token(vp_json, "DIDAuthResponse", hive_did, 60)
def get_did_string_from_did(self, did): if not did: return None method = lib.DID_GetMethod(did) if not method: return None method = ffi.string(method).decode() sep_did = lib.DID_GetMethodSpecificId(did) if not sep_did: return None sep_did = ffi.string(sep_did).decode() return "did:" + method + ":" + sep_did
def get_auth_token_by_sign_in(self, base_url, vc_str, subject): vc = lib.Credential_FromJson(vc_str.encode(), ffi.NULL) if not vc: return None, None, "The credential string is error, unable to rebuild to a credential object." #sign_in doc = lib.DIDStore_LoadDID(self.store, self.did) doc_str = ffi.string(lib.DIDDocument_ToJson(doc, True)).decode() doc = json.loads(doc_str) rt, status_code, err = self.post(base_url + '/api/v1/did/sign_in', {"document": doc}) if err != None: return None, None, "Post sign_in error: " + err jwt = rt["challenge"] if jwt is None: return None, None, "Challenge is none." # print(jwt) jws = lib.DefaultJWSParser_Parse(jwt.encode()) if not jws: return None, None, "Challenge DefaultJWSParser_Parse error: " + ffi.string( lib.DIDError_GetLastErrorMessage()).decode() aud = ffi.string(lib.JWT_GetAudience(jws)).decode() if aud != self.get_did_string(): lib.JWT_Destroy(jws) return None, None, "Audience is error." nonce = ffi.string(lib.JWT_GetClaim(jws, "nonce".encode())).decode() if nonce is None: lib.JWT_Destroy(jws) return None, None, "Nonce is none." hive_did = ffi.string(lib.JWT_GetIssuer(jws)).decode() lib.JWT_Destroy(jws) if hive_did is None: return None, None, "Issuer is none." #auth_token vp_json = self.create_presentation(vc, nonce, hive_did) if vp_json is None: return None, None, "create_presentation error." auth_token = self.create_vp_token(vp_json, subject, hive_did, hive_setting.AUTH_CHALLENGE_EXPIRED) if auth_token is None: return None, None, "create_vp_token error." return auth_token, hive_did, None
def get_info_from_token(token): if token is None: return None, "Then token is none!" token_splits = token.split(".") if token_splits is None: return None, "Then token is invalid!" if (len(token_splits) != 3) or token_splits[2] == "": return None, "Then token is invalid!" jws = lib.DefaultJWSParser_Parse(token.encode()) if not jws: return None, get_error_message("JWS parser") issuer = lib.JWT_GetIssuer(jws) if not issuer: lib.JWT_Destroy(jws) return None, get_error_message("JWT getIssuer") issuer = ffi.string(issuer).decode() if issuer != get_current_node_did_string(): lib.JWT_Destroy(jws) return None, "The issuer is invalid!" expired = lib.JWT_GetExpiration(jws) now = (int)(datetime.now().timestamp()) if now > expired: lib.JWT_Destroy(jws) return None, "Then token is expired!" props = lib.JWT_GetClaim(jws, "props".encode()) if not props: lib.JWT_Destroy(jws) return None, "Then props is none!" props_str = ffi.string(props).decode() props_json = json.loads(props_str) app_instance_did = ffi.string(lib.JWT_GetAudience(jws)).decode() if not app_instance_did: lib.JWT_Destroy(jws) return None, "Then app instance id is none!" props_json[APP_INSTANCE_DID] = app_instance_did lib.JWT_Destroy(jws) # print(props_json) return props_json, None
def create_order_proof(self, user_did, doc_id, amount=0, is_receipt=False): doc = lib.DIDStore_LoadDID(self.store, self.did) if not doc: raise BadRequestException( 'Can not load service instance document in creating order proof.' ) builder = lib.DIDDocument_GetJwtBuilder(doc) if not builder: raise BadRequestException( msg='Can not get builder from doc in creating order proof.') lib.JWTBuilder_SetHeader(builder, "typ".encode(), "JWT".encode()) lib.JWTBuilder_SetHeader(builder, "version".encode(), "1.0".encode()) lib.JWTBuilder_SetSubject(builder, 'ORDER_PROOF'.encode()) lib.JWTBuilder_SetAudience(builder, user_did.encode()) exp = int(datetime.utcnow().timestamp() ) + 7 * 24 * 3600 if not is_receipt else -1 lib.JWTBuilder_SetExpiration(builder, exp) props = {'order_id': doc_id} if is_receipt: props = {'receipt_id': doc_id, 'amount': amount} lib.JWTBuilder_SetClaim(builder, "props".encode(), json.dumps(props).encode()) lib.JWTBuilder_Sign(builder, ffi.NULL, self.storepass) proof = lib.JWTBuilder_Compact(builder) lib.JWTBuilder_Destroy(builder) if not proof: raise BadRequestException( msg='Can not build token in creating order proof.') return ffi.string(proof).decode()
def sign_in(self): doc_c = lib.DIDStore_LoadDID(self.app_did.store, self.app_did.did) doc_str = ffi.string(lib.DIDDocument_ToJson(doc_c, True)).decode() doc = json.loads(doc_str) response = self.http_client.post('/api/v2/did/signin', {"id": doc}, need_token=False, is_skip_prefix=True) assert response.status_code == 201 return response.json()["challenge"]
def __validate_presentation_realm(self, presentation): realm = lib.Presentation_GetRealm(presentation) if not realm: raise BadRequestException(msg='Can not get presentation realm.') realm = ffi.string(realm).decode() if not realm or realm != self.get_did_string(): raise BadRequestException( msg='Invalid presentation realm or not match.')
def __get_presentation_nonce(self, presentation): nonce = lib.Presentation_GetNonce(presentation) if not nonce: raise BadRequestException(msg='Failed to get presentation nonce.') nonce_str = ffi.string(nonce).decode() if not nonce_str: raise BadRequestException(msg='Invalid presentation nonce.') nonce_info = get_did_info_by_nonce(nonce_str) if not nonce_info: raise BadRequestException( msg='Can not get presentation nonce information from database.' ) return nonce_str, nonce_info
def create_presentation(self, vc, nonce, realm): vpid = lib.DIDURL_NewByDid(self.did, "jwtvp".encode()) type0 = ffi.new("char[]", "VerifiablePresentation".encode()) types = ffi.new("char **", type0) vp = lib.Presentation_Create(vpid, self.did, types, 1, nonce.encode(), realm.encode(), ffi.NULL, self.store, self.storepass, 1, vc) lib.DIDURL_Destroy(vpid) # print_err() vp_json = ffi.string(lib.Presentation_ToJson(vp, True)).decode() # print(vp_json) logging.debug(f"vp_json: {vp_json}") return vp_json
def MyDIDLocalResovleHandle(did): spec_did_str = ffi.string(lib.DID_GetMethodSpecificId(did)).decode() doc = ffi.NULL file_path = hive_setting.DID_DATA_LOCAL_DIDS + os.sep + spec_did_str is_exist = os.path.exists(file_path) if is_exist: f = open(file_path, "r") try: doc_str = f.read() doc = lib.DIDDocument_FromJson(doc_str.encode()) finally: f.close() return doc
def __get_values_from_challenge_response(self, challenge_response): challenge_response_cstr = lib.DefaultJWSParser_Parse( challenge_response.encode()) if not challenge_response_cstr: raise BadRequestException(msg='Invalid challenge response.') presentation_cstr = lib.JWT_GetClaimAsJson(challenge_response_cstr, "presentation".encode()) lib.JWT_Destroy(challenge_response_cstr) if not presentation_cstr: raise BadRequestException(msg='Can not get presentation cstr.') presentation = lib.Presentation_FromJson(presentation_cstr) if not presentation or lib.Presentation_IsValid(presentation) != 1: raise BadRequestException(msg='The presentation is invalid.') if lib.Presentation_GetCredentialCount(presentation) < 1: raise BadRequestException(msg='No presentation credential exists.') self.__validate_presentation_realm(presentation) nonce, nonce_info = self.__get_presentation_nonce(presentation) return json.loads( ffi.string(presentation_cstr).decode()), nonce, nonce_info
def __create_challenge(self, app_instance_did, nonce, expire_time): """ Create challenge for sign in response. """ builder = lib.DIDDocument_GetJwtBuilder( self.doc) # service instance doc if not builder: raise BadRequestException( msg= f'Can not get challenge builder: {self.get_error_message()}.') lib.JWTBuilder_SetHeader(builder, "type".encode(), "JWT".encode()) lib.JWTBuilder_SetHeader(builder, "version".encode(), "1.0".encode()) lib.JWTBuilder_SetSubject(builder, "DIDAuthChallenge".encode()) lib.JWTBuilder_SetAudience(builder, app_instance_did.encode()) lib.JWTBuilder_SetClaim(builder, "nonce".encode(), nonce.encode()) lib.JWTBuilder_SetExpiration(builder, expire_time) lib.JWTBuilder_Sign(builder, ffi.NULL, self.storepass) token = lib.JWTBuilder_Compact(builder) lib.JWTBuilder_Destroy(builder) if not token: raise BadRequestException(msg="Failed to create challenge token.") return ffi.string(token).decode()
def create_vp_token(self, vp_json, subject, hive_did, expire): doc = lib.DIDStore_LoadDID(self.store, self.did) builder = lib.DIDDocument_GetJwtBuilder(doc) ticks = int(datetime.now().timestamp()) iat = ticks nbf = ticks exp = ticks + expire lib.JWTBuilder_SetHeader(builder, "type".encode(), "JWT".encode()) lib.JWTBuilder_SetHeader(builder, "version".encode(), "1.0".encode()) lib.JWTBuilder_SetSubject(builder, subject.encode()) lib.JWTBuilder_SetAudience(builder, hive_did.encode()) lib.JWTBuilder_SetIssuedAt(builder, iat) lib.JWTBuilder_SetExpiration(builder, exp) lib.JWTBuilder_SetNotBefore(builder, nbf) lib.JWTBuilder_SetClaimWithJson(builder, "presentation".encode(), vp_json.encode()) lib.JWTBuilder_Sign(builder, ffi.NULL, self.storepass) token = ffi.string(lib.JWTBuilder_Compact(builder)).decode() lib.JWTBuilder_Destroy(builder) # print(token) return token
def __create_access_token(self, credential_info, subject): doc = lib.DIDStore_LoadDID(self.store, self.did) if not doc: raise BadRequestException( 'Can not load service instance document in creating access token.' ) builder = lib.DIDDocument_GetJwtBuilder(doc) if not builder: raise BadRequestException( msg='Can not get builder from doc in creating access token.') lib.JWTBuilder_SetHeader(builder, "typ".encode(), "JWT".encode()) lib.JWTBuilder_SetHeader(builder, "version".encode(), "1.0".encode()) lib.JWTBuilder_SetSubject(builder, subject.encode()) lib.JWTBuilder_SetAudience(builder, credential_info["id"].encode()) lib.JWTBuilder_SetExpiration(builder, credential_info["expTime"]) props = { k: credential_info[k] for k in credential_info if k not in ['id', 'expTime'] } if not lib.JWTBuilder_SetClaim(builder, "props".encode(), json.dumps(props).encode()): lib.JWTBuilder_Destroy(builder) raise BadRequestException( msg='Can not set claim in creating access token.') lib.JWTBuilder_Sign(builder, ffi.NULL, self.storepass) token = lib.JWTBuilder_Compact(builder) lib.JWTBuilder_Destroy(builder) if not token: raise BadRequestException( msg='Can not build token in creating access token.') return ffi.string(token).decode()
def test_auth_common(self, user_did, app_did): # sign_in doc = lib.DIDStore_LoadDID(app_did.store, app_did.did) doc_str = ffi.string(lib.DIDDocument_ToJson(doc, True)).decode() logging.getLogger("test_auth_common").debug(f"\ndoc_str: {doc_str}") doc = json.loads(doc_str) rt, s = self.parse_response( self.test_client.post('/api/v1/did/sign_in', data=json.dumps({ "document": doc, }), headers=self.json_header)) self.assert200(s) self.assertEqual(rt["_status"], "OK") jwt = rt["challenge"] # print(jwt) jws = lib.DefaultJWSParser_Parse(jwt.encode()) # if not jws: # print(ffi.string(lib.DIDError_GetLastErrorMessage()).decode()) aud = ffi.string(lib.JWT_GetAudience(jws)).decode() self.assertEqual(aud, app_did.get_did_string()) nonce = ffi.string(lib.JWT_GetClaim(jws, "nonce".encode())).decode() hive_did = ffi.string(lib.JWT_GetIssuer(jws)).decode() lib.JWT_Destroy(jws) # auth vc = user_did.issue_auth(app_did) vp_json = app_did.create_presentation(vc, nonce, hive_did) auth_token = app_did.create_vp_token(vp_json, "DIDAuthResponse", hive_did, 60) # print(auth_token) logging.getLogger("test_auth_common").debug(f"\nauth_token: {auth_token}") rt, s = self.parse_response( self.test_client.post('/api/v1/did/auth', data=json.dumps({ "jwt": auth_token, }), headers=self.json_header)) self.assert200(s) self.assertEqual(rt["_status"], "OK") token = rt["access_token"] jws = lib.DefaultJWSParser_Parse(token.encode()) aud = ffi.string(lib.JWT_GetAudience(jws)).decode() self.assertEqual(aud, app_did.get_did_string()) issuer = ffi.string(lib.JWT_GetIssuer(jws)).decode() lib.JWT_Destroy(jws) # print(token) logging.getLogger("test_auth_common").debug(f"\ntoken: {token}") app_did.set_access_token(token) # auth_check # token = test_common.get_auth_token() self.json_header = [ ("Authorization", "token " + token), self.content_type, ] rt, s = self.parse_response( self.test_client.post('/api/v1/did/check_token', headers=self.json_header)) self.assert200(s) self.assertEqual(rt["_status"], "OK") return token, hive_did
def get_backup_credential(self, backup_node_did): """ INFO: current url to backup url and node did. """ vc = self.get_current_user_did().issue_backup_auth(self.get_node_did(), self.test_config.backup_url, backup_node_did) return ffi.string(lib.Credential_ToString(vc, True)).decode()
def __get_issuer_by_challenge2(self, jws): node_did = ffi.string(lib.JWT_GetIssuer(jws)).decode() assert node_did, 'Invalid hive did' self.test_config.save_node_did(self.http_client.base_url, node_did) return node_did
def get_error_message(): return str(ffi.string(lib.DIDError_GetLastErrorMessage()), encoding='utf-8')
def get_error_message(self, prompt): err_message = ffi.string(lib.DIDError_GetLastErrorMessage()).decode() if not prompt is None: err_message = prompt + " error: " + err_message return err_message
def get_error_message(self, prompt=None): """ helper method to get error message from did.so """ err_message = ffi.string(lib.DIDError_GetLastErrorMessage()).decode() return err_message if not prompt else f'[{prompt}] {err_message}'