• Nenhum resultado encontrado

Cap´ıtulo 5

5.1 Servidor do operador

Figura 5.1: M´odulos que constituem o prot´otipo do servidor do operador.

O prot´otipo do servidor do operador foi implementado, em Python, com os m´odulos apre-sentados na Figura 5.1. A descri¸c˜ao do prot´otipo come¸car´a por incidir sobre a comunica¸c˜ao entre a framework Web Django e o complemento da framework Django REST com a base

Figura 5.2: Modelo de dados do servidor.

de dados MySQL e, mais `a frente, ser´a descrita a rela¸c˜ao com os m´odulos “hedera-sdk-py” e

“starkbank-ecdsa”.

Antes de descrever o funcionamento deste servidor, ´e necess´ario ter em conta o conceito de modelos (models),ModelSerializers e views.

Inicialmente foram definidos os modelos que permitem organizar a estrutura da base de dados MySQL, onde um modelo representa uma tabela e um campo de um modelo representa uma coluna dessa tabela. O modelo de dados do servidor est´a representado na Figura 5.2, onde se encontram 9 tabelas diferentes em que: a tabelaHederaAccount cont´em uma lista de IDs de contas Hedera Hashgraph pertencentes ao operador que se encarregaram de receber e/ou enviar transa¸c˜oes para contas Hedera Hashgraph de passageiros; a tabelaStop cont´em informa¸c˜oes acerca de cada uma das paragens pertencentes `a rota de um ve´ıculo, com o seu ID, nome, custo da viagem partindo da mesma etimestampcom o hor´ario previsto da chegada do ve´ıculo (por motivos de simplifica¸c˜ao foi simulado apenas um ve´ıculo); a tabelaUBKeycont´em os IDs e aspublic keys correspondentes a todas as unidades de bordo (cada uma pertencente a um ve´ıculo), de forma a desencriptar informa¸c˜ao gerada pelas mesmas; e a CustomUser salvaguarda a informa¸c˜ao dos passageiros que se registam no sistema. Durante uma viagem, os passageiros ficam associados `as tabelasTravellingPassenger ePendingRefund quando efetuam check-ins, enquanto que as tabelas FinishedTransaction e FinishedTrip recebem uma nova entrada cada vez que um passageiro efetua umcheck-out. Por fim, a tabelaSession faz parte da autentica¸c˜ao de passageiros no sistema, explorado mais `a frente.

Um ModelSerializer ´e uma ferramenta que permite facilitar a comunica¸c˜ao entre mode-los/tabelas e os requests HTTP, que permitem o acesso de dispositivos externos, como o smartphone de um passageiro, aos dados do servidor. As views, no caso deste servidor, s˜ao fun¸c˜oes que processam requests HTTP (POST/GET) e fazem o return de objetos de uma classe Response. Esta funcionalidade permite-lhes servir de intermedi´arios entre o acesso de dispositivos externos ao servidor e os pr´oprios ModelSerializers, al´em de permitirem ao ser-vidor interagir diretamente com a sua base de dados. A cada classe que cont´em fun¸c˜oesview foi designado um URL para permitir a comunica¸c˜ao da aplica¸c˜ao dos passageiros atrav´es de HTTPrequests.

5.1.1 Registo do utilizador

Para registar um passageiro na base de dados do servidor, a aplica¸c˜ao do passageiro realiza um POST HTTP com a informa¸c˜ao de registo (nome de utilizador, email, palavra-passe, n´umero de telem´ovel e ID da sua conta Hedera Hashgraph) que, caso seja enviado com sucesso, alcan¸ca o servidor sob o formato:

"POST /api/register/ HTTP/1.1" 200

Assim, o servidor acede a uma fun¸c˜aopost() da classe com este URL (neste caso a “Regis-terAPI”), passa a informa¸c˜ao recebida pelo ModelSerializer “RegisterSerializer”, que valida a informa¸c˜ao recebida, converte a informa¸c˜ao num modelo “CustomUser” e guarda o novo utilizador na base de dados. Caso o registo tenha sido bem sucedido, o servidor envia a seguinte resposta:

{"response":"Registration Successfull"}

De salientar ainda que o modelo “CustomUser” foi adicionado ao servidor com recurso `a subclasse AbstractUser da framework Django, pela necessidade de complementar o modelo User com os campos: n´umero de telem´ovel e ID da conta Hedera Hashgraph.

5.1.2 Autentica¸c˜ao do utilizador

Com o registo conclu´ıdo, o passo seguinte ´e a autentica¸c˜ao dos utilizadores registados na base de dados para permitir que efetuem o login nas respetivas aplica¸c˜oes. Esta classe recebe da aplica¸c˜ao um POST HTTP com o nome de utilizador e respetiva palavra-passe. A informa¸c˜ao passa peloSerializer AuthTokenSerializer proveniente da subclasse ObtainAuth-Token da framework Django REST, verifica se a informa¸c˜ao est´a de acordo com o modelo User e, caso a informa¸c˜ao seja v´alida, estes campos passam a ser argumentos de uma fun¸c˜ao authenticate():

username = serializer.validated_data[’username’]

password = serializer.validated_data[’password’]

user = authenticate(username=username, password=password)

Esta fun¸c˜ao verifica se as credenciais fazem parte de algum utilizador registado no servidor e, caso isso se verifique, a fun¸c˜ao devolve o objeto User correspondente. O request HTTP proveniente da aplica¸c˜ao do utilizador (POST ’/api-token-auth/’) e o objeto User, passam a ser argumentos de uma fun¸c˜aologin(), que cria uma key para a sess˜ao ativa (“sessionid”):

login(request,user)

data[’sessionid’] = request.session.session_key

terminando com uma resposta com o formato do seguinte exemplo:

{"response":"Login Successfull",

"sessionid":"oavj31em72waamdf1ags96hl5xeo183c"}.

Esta String de 32 caracteres ASCII aleat´orios ´e guardada na base de dados do servidor e associada ao ID do passageiro, com uma validade de 14 dias (na tabela Session). Esta permite restringir o acesso `as restantes classes apenas a quem est´a a fazer requests a partir de um login ativo, sendo transmitida no cabe¸calho de todos osrequests HTTP enviados pela aplica¸c˜ao do passageiro. O processo de logout ´e um desses exemplos, tendo em conta que come¸ca por identificar a validade da “sessionid” recebida e, s´o no caso de esta ser v´alida, procede `a elimina¸c˜ao dessa sess˜ao da base de dados (session.delete()), terminando com a resposta:

{"response":"Logout Successfull"}

O utilizador s´o volta a ter uma sess˜ao ativa (“sessionid” atribu´ıdo ao seu ID na base de dados) assim que efetuar um novo login.

5.1.3 Autentica¸c˜ao dos beacons

Tal como foi referido na arquitetura descrita no Cap´ıtulo 4, uma das fun¸c˜oes do servidor do operador ´e desencriptar as assinaturas digitais dos an´uncios de paragem recebidos pela aplica¸c˜ao dos passageiros de modo a validar a sua autenticidade e evitar situa¸c˜oes despoofing ehijacking.

Antes de dar in´ıcio `a desencripta¸c˜ao de uma mensagem, ´e necess´ario que a tabelaUBKey da base de dados tenha o ID da Unidade de Bordo que encriptou essa mensagem associado `a correspondentepublic key, bem como o ID da paragem onde a mensagem foi encriptada asso-ciado ao nome dessa paragem (tabela Stop). No caso da mensagem encriptada apresentada no cap´ıtulo 4, a base de dados teria uma entrada na tabelaStop com o ID da paragem “00A1”

associado ao nome dessa paragem e, na tabelaUBKey, o ID da UB “000018” associado a uma public key como a seguinte:

---BEGIN PUBLIC

KEY---MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEO3radhXVo6wzk2f8QbH0fHCnQsKW1uhC OZqOoPVNaTJN+p1lvT+3zWRKXC3F4SEm2+Bn1JGQIDYI9bdQWqsGlA==

---END PUBLIC

KEY---Dado que a desencripta¸c˜ao ECDSA, com parˆametros secp256k1, ´e necess´aria apenas para an´uncios de paragem, foi criada uma classe “AnnouncementsAPI”, em que a sua fun¸c˜aopost() visa desencriptar qualquer an´uncio de paragem recebido pelo servidor e, atrav´es de umaflag

“status”, o servidor determina se est´a na presen¸ca de uma situa¸c˜ao de check-in, paragem interm´edia ou check-out. Para isso, a classe recebe POSTs HTTP, que contˆem cinco campos distintos: o ID da sess˜ao, o timestamp do smartphone do passageiro, a flag, a mensagem com os dados desencriptados do an´uncio de paragem e a assinatura digital (com os dados encriptados).

Essencialmente, o processo de valida¸c˜ao do an´uncio consiste em identificar o ID da UB incorporado na mensagem, identificar a public key associada e, atrav´es dela, desencrip-tar a assinatura digital. Para isso, foi usada o m´odulo “starkbank-ecdsa”, que converte as Strings da assinatura digital e public key em “ellipticcurve.signature.Signature” e “el-lipticcurve.publicKey.PublicKey”, respetivamente, que passam a ser argumentos da fun¸c˜ao

“Ecdsa.verify()” junto da mensagem desencriptada:

Ecdsa.verify(message, signature, pubkey)

Caso a desencripta¸c˜ao da assinatura digital com a public key resulte numa mensagem igual

`

a sec¸c˜ao de dados, faz return de uma vari´avelboolean “True”, confirmando que os dados n˜ao sofreram altera¸c˜oes desde a sa´ıda da Unidade de Bordo at´e `a chegada ao servidor. O servidor verifica ainda se o an´uncio foi recebido pelo passageiro dentro de um intervalo de tempo vi´avel, para que este n˜ao seja prejudicado por uma r´eplica desse an´uncio de paragem, por exemplo no dia seguinte, por uma entidade maliciosa que pretenda que o utilizador transfira dinheiro sem estar a viajar (spoofing).

Este processo ´e realizado atrav´es da identifica¸c˜ao do ID da paragem representada na men-sagem, que na base de dados tem umtimestamp previsto associado `a chegada do transporte p´ublico a essa paragem, obt´em tamb´em otimestampprevisto da chegada `a paragem seguinte e calcula o tempo estimado entre elas. Caso a diferen¸ca entre o timestamp enviado pela aplica¸c˜ao do passageiro e otimestamp da cria¸c˜ao do an´uncio pela UB seja superior ao tempo estimado entre paragens, o an´uncio ´e descartado.

5.1.4 Check-in

A classe “AnnouncementsAPI”, quando recebe um POST request de um an´uncio de pa-ragem relativo a um check-in, procede `a valida¸c˜ao do an´uncio de paragem, guarda o nome da paragem atual com o timestamp correspondente e associa esta informa¸c˜ao ao nome do passageiro, no modelo “TravelingPassenger”. A fun¸c˜aopost() da classe, termina com oreturn do nome da paragem atual, nome da paragem seguinte, pre¸co a pagar pelo passageiro, o ID da conta Hedera Hashgraph da empresa de transporte p´ublico, para o qual o passageiro ter´a de enviar o montante, e uma mensagem que indica que o check-in foi bem sucedido, como demonstra o exemplo de resposta da Figura 5.3.

Figura 5.3: Resposta a umrequest HTTP de check-in.

O processo de check-in, al´em do request `a classe AnnouncementsAPI, tamb´em faz um POST dirigido `a classe PendingRefundAPI. A aplica¸c˜ao do passageiro, ap´os receber o va-lor da quantia a transferir atrav´es do elemento “price” representado na Figura 5.3, trans-fere o montante para a conta Hedera Hashgraph do operador de transporte p´ublico e faz um request HTTP para esta classe, para que o servidor guarde o pedido de reembolso com o nome do passageiro, a quantia paga pelo mesmo e o ID da transa¸c˜ao, por exemplo

[email protected]”, na tabela PendingRefund.

5.1.5 Paragem interm´edia

No caso de uma paragem interm´edia, ap´os a valida¸c˜ao do an´uncio de paragem, o servidor apenas responde com os nomes das paragens atual e seguinte, junto de uma mensagem a confirmar que o pedido foi bem sucedido.

5.1.6 Check-out

Em rela¸c˜ao aos an´uncios de paragem em situa¸c˜oes de check-out, o servidor come¸ca o processo por apagar da base de dados o objeto da tabela TravelingPassenger que foi criado nocheck-in. Caso encontre um objeto em PendingRefund que corresponda a um pagamento efetuado pelo utilizador, d´a por terminada a viagem registando-a na tabela FinishedTrip, como demonstra o exemplo da Figura 5.4, com o nome do passageiro (ex.:“a0”), o custo da viagem e os nomes das paragens de check-in e check-out com os respetivos timestamps dos an´uncios. Esta tabela permite aos utilizadores, atrav´es de um request GET enviado `a classe AnnouncementsAPI, obterem os seus hist´oricos de viagens. Neste caso, o servidor usa o nome do utilizador para filtrar as entradas de viagens completadas pelo mesmo e responde ao pedido da seguinte forma:

history = FinishedTrip.objects.filter(user=user).

Figura 5.4: Registo de uma viagem completa na base de dados.

Figura 5.5: Registo das transa¸c˜oes de uma viagem completa na base de dados.

values(’stopnameCI’,’timestampCI’,’stopnameCO’,’timestampCO’,’cost’) return Response(history)

Caso o check-out n˜ao tenha sido efetuado na ´ultima paragem, o servidor d´a in´ıcio ao processo de reembolso da quantia que o passageiro pagou a mais, nocheck-in.

Tendo em conta que at´e ao momento a Hedera Hashgraph apenas disponibiliza SDKs1 nativos em Java, JavaScript e Go, para efetuar o reembolso neste servidor desenvolvido em Python, foi usado umwrapper em Python do Hedera Java SDK, chamado “hedera-sdk-py”.

Este wrapper efetua a transa¸c˜ao atrav´es da fun¸c˜ao TransferTransaction(), que tem como argumentos: o ID da conta que vai reembolsar (ID da conta Hedera Hashgraph da empresa de transporte p´ublico), o ID da conta que vai ser reembolsada (ID da conta do passageiro), a quantia a reembolsar (custo a partir da paragem decheck-out) eclient da Testnet, que d´a acesso `a conta Hedera Hashgraph da empresa de transporte p´ublico:

resp = TransferTransaction().

addHbarTransfer(operatorID, amount.negated()).

addHbarTransfer(passengerID, amount).execute(client)

Conclu´ıda a transa¸c˜ao, o servidor acede ao “status” contido no recibo da transa¸c˜ao:

receipt=resp.getReceipt(client) rstatus=receipt.status.toString() data[’response’] = rstatus

que no caso de ter sido bem sucedida, apresenta a String “SUCCESS”, que ´e inclu´ıda na resposta com o nome da paragem de check-out e nome da paragem seguinte, que no caso ´e um h´ıfen a representar que n˜ao existe.

Por fim, o servidor procede em apagar o objeto da tabela PendingRefund e guardar na tabela FinishedTransaction, os campos representados na Figura 5.5: nome do passageiro, a paragem de check-in, paragem de check-out, ID da transa¸c˜ao recebida, ID da transa¸c˜ao enviada e respetivas quantias transferidas, dando por terminado o processo de reembolso.

Figura 5.6: POSTs recebidos pelo servidor.

A Figura 5.6 representa os POSTs de uma viagem completa recebidos pelo servidor, no caso de um passageiro efetuar o seu login e dar in´ıcio a uma viagem com apenas 1 paragem interm´edia.

Documentos relacionados