0.3.2 • Published 4 years ago

hamlib-common v0.3.2

Weekly downloads
1
License
MIT
Repository
github
Last release
4 years ago

Ham lib

This is a library which is used in tix project

The main reason of this repository is to have a library in javascript which implements the communication protocol and all necessary functions of cryptography for tix project.

Getting Started

This library has two main files. crypto can handle all the required cryptography functions and serialize which handles all the serializing and deserializing packets. This library is using proto3 syntax to define packets.

At first all the actors in tix project (Requester, Subject, Prover) should generate two kinds of pair keys for further use.

import * as crypto from "../src/crypto";

const [pk, pubk] = crypto.createKey();

const [pkECDH, pubkECDH] = crypto.createKeyECDH();

After generating all pair keys, applications should save both private keys locally(encrypted by a password or not) and register their public keys to DID server.

pk and pubk are EDDSA pair of keys which is going to be used for signing the packets and pkECDH and pubkECDH is going to be used to encrypt and decrypt the communication channel. To encrypt or decrypt the pk with a password you can use aesEncrypt function in crypto.

const encrypted = crypto.aesEncrypt(password, pk);

const pk = crypto.aesDecrypt(password, encrypted); 

From now on all applications should have their private keys for EdDSA and ECDH and they can look for any DID in DID server to retrieve their public keys.

Create a Query

In Requester application if you want to create a query you should specify: 1. Which question you want to ask? - Types of questions are defined in types.ts as an enum - Relation, Position, Certificate, Permission - According to the type of question the query can have queryArgs which is an array of string. 2. Which prover can approve subject's answer? - The prover DID should be in the query

Notice: Version is a field in this packet which can be used for backward compatibility

const encoded = serialize.encodeRequesterPacket(pk, requesterDID, proverDID, 
                                                    questionType, queryArgs); 

The result is a Uint8Array and requester application should use this to generate the QR code with.

Subject reads QR code

In Subject application after scanning the QR code the application should decode the bytes.

const decoded = serialize.decodeRequesterPacket(encoded);

After decoding the packet application can use decoded.query.requesterDID to retrieve the requester public key by searching in DID Server and verify the signature by:

serialize.verifyRequesterPacket(requesterPublicKey, decoded)

After seeing the question subject answers it and retrieves the appropriate prover's ECDH public key to create a packet for prover:

//startValidationTime and endValidationTime are epoch time 
const encodedPacket = serialize.encodeSubjectPacket(subjectPrivateKey, decoded, subjectDID, proverDID, startValidationTime, endValidationTime, answerArgs);
const encryptedPacket = serialize.encodeEncryptPacket(subjectECDHPrivateKey, proverECDHPublicKey, PacketType.Subject, subjectDID, encodedPacket);
// here subject can send the encrypted packet to prover.

Approve subject's answer

When prover's application receives the encrypted packet:

  1. It should decode it to a packet:
    const decodedPacket = serialize.decodeEncryptPacket(encryptedPacket);
  2. Look for ECDH public key of decodedPacket.did
    const [packetType, encodedData, decryptedPacket] = serialize.decryptPacket(proverECDHPrivateKey, subjectECDHPublicKey, decodedPacket);
    // packetType is the type of packet: Requester, Subject, Prover
    // decryptedPacket is the json format of the packet
    // encodedData is the encoded compressed version of decryptedPacket 
  3. If this packet is not a prover packet (decodedPacket.type != PacketType.Prover)
    1. Here prover can confirm the subject's answer and create the encryptedPacket:
      // decryptedPacket is the subject packet which this prover has just received.
      // proverStartValidationTime, proverEndValidationTime, proverAnswerArgs are the prover's answer if prover does
      // status is a ConfirmStatus which can be Reject, Confirm or Modify.
      // if the status is Modify answerArgs and validation times can be different from subject's answer.
      const proverEncodedPacket = serialize.encodeProverPacket(proverPrivateKey, decryptedPacket, [], [], proverDID, status, proverStartValidationTime, proverEndValidationTime, proverAnswerArgs); 
    2. Should I send the answer back to subject (Single Prover)

      const encryptedPacket = serialize.encodeEncryptPacket(proverECDHPrivateKey, subjectECDHPublicKey, PacketType.Prover, proverDID, proverEncodedPacket);
      // now prover can send encryptedPacket to the subject.  
    3. Should I send it to another prover (Multiple Prover)

      const encryptedPacket = serialize.encodeEncryptPacket(proverECDHPrivateKey, nextProverECDHPublicKey, PacketType.Prover, proverDID, proverEncodedPacket);
      // now prover can send encryptedPacket to next prover.  
  4. If this packet is a prover packet (Multiple prover scenario)

    1. If I'm the prover that requester wanted me to approve the query and I should send the result to subject or if my confirmation is already in this packet and I should send it back to previous prover:
      const encryptedPacket = serialize.encodeEncryptPacket(proverECDHPrivateKey, subjectECDHPublicKey or prevoiusECDHPublicKey, PacketType.Prover, proverDID, encodedData);
      // now prover can send encryptedPacket to the subject or previous prover.  
    2. If not, Answer the question
      const proverEncodedPacket = serialize.encodeProverPacket(proverPrivateKey, decryptedPacket, [], [], proverDID, status, proverStartValidationTime, proverEndValidationTime, proverAnswerArgs); 
    3. Send it to another prover
      const encryptedPacket = serialize.encodeEncryptPacket(proverECDHPrivateKey, nextProverECDHPublicKey, PacketType.Prover, proverDID, proverEncodedPacket);
      // now prover can send encryptedPacket to next prover.  

Notice: answerArgs and queryArgs are designed generally to support all kind of scenarios.

Subject receives prover packet

  1. It should decode it to a packet:
    const decodedPacket = serialize.decodeEncryptPacket(encryptedPacket);
  2. Look for ECDH public key of decodedPacket.did
    const [packetType, encodedData, decryptedPacket] = serialize.decryptPacket(subjectECDHPrivateKey, proverECDHPublicKey, decodedPacket); 
  3. Decode the packet and check if everything is alright, subject can generate a QR code from encodedData and show it to the requester.

Requester should check all signatures

After scanning subject's QR code, now requester should decode the bytes to readable json format:

const packet = serialize.decodeProverPacket(qrBytes);

// check if every signature is ok or not
serialize.verifyProverPacket([all provers public key which can be retreived from packet.confirmations], packet, true, requestPublicKey, subjectPublicKey);  

Register To DID

Prover, Subject and Requester should register to DID server via comminication.ts. To use communication you should specify address of did server and two functions for loading did from cache and saving the did to cache.

import {Communication} from "communication"

const communication = new Communication("http://127.0.0.1", did => {
        // save did document in local database
    }, (did: string) => {
        // return did document if it is in local database
    });

After having communication object you can easily register to did server according to the role of the application with appropriate methods.

// prover can register to did server
// prover is listening on subscribeUrl to push data to subjects, default is tcp://{ip address}:7200
// prover is listening on replyUrl to receive data from subjects or provers tcp://{ip address}:7201
communication.registerProverDID(name, ecdhPublicKey, publicIp);

// subjects can register to did server
communication.registerSubjectDID(name, ecdhPublicKey, eddsaPublicKey);

//requester can register to did server
communication.registerRequesterDID(name, ecdhPublicKey, eddsaPublicKey); 

To get the json before submitting it to DID server you can use communication.mockRegisterProverDID, communication.mockRegisterSubjectDID and communication.mockRegisterRequesterDID.

Sending and receiving data

After scanning the QR code, subject should decode it by serialize.decodeRequesterPacket and show the json via UI to the user. User can answer the query by serialize.encodeSubjectPacket and then to send the answer to the prover:

communication.sendAnswerToProver(privateKeyECDH, proverDID, myDID, encodedPacket);

// for receiving data from prover subject should connect to prover as well

communication.connectToProver(privateKeyECDH, proverDID, myDID, 
                (packetId, validity, type, encodedMessage, packet) => {
                      // packetId is unique identifier of this packet
                      // validity is true if every signature in packet is valid
                      // type is one of Subject, Prover
                      // according to type you can cast packet to SubjectPacketType or ProverPacketType
                      // in last step if you want to create a QR code for requester, you can generate QR code from encodedMessage 
                });

That was for subject but prover application should call communication.startProver at the start of application.

// default port for subscribe is 7200 (pushing data to subjects)
// default port for reply is 7201 (receiving data from other provers or subjects)
communication.startProver(privateKeyECDH, 
                            (packetId, validity, type, encodedMessage, packet) => {
                            // packetId is unique identifier of this packet
                            // validity is true if every signature in packet is valid
                            // type is one of Subject, Prover
                            // according to type you can cast packet to SubjectPacketType or ProverPacketType                             
                            });

In prover application, after receiving subject packet, prover should decide to confirm or reject the packet or forward it to another prover. To forward it to another prover you should call communication.forwardProver and to confirm or reject the packet and sends it back to subject you should call communication.sendConfirmationToSubject.

Notice In multiple prover story the last prover should call communication.sendConfirmationToSubject as well. The packet will get forwarded to previous prover

##Question Types Query packet structure is:

{
    version: number;
    requesterDID: string;
    proverDID: string;
    type: number;
    queryArgs: string[];
}

Requester can generate the query by the help of serialize.encodeRequesterPacket function. This function returns the encodedBytes as Uint8Array with which you can generate the QR code and you can have the size of bytes by using encodedBytes.length. This function needs privateKey, requesterDID, proverDID, type and queryArgs as arguments. PrivateKey, requesterDID and proverDID are obvious but for type and query args you can use table below for reference.

QuestiontypequeryArgs
What relation do you have with organization x?QuestionType.Relationorganization name in text
What position/responsibility x do you have for organization y?QuestionType.Positionorganization name in text, position text like: CTO, CEO, etc.
Do you have certificate X from organization y?QuestionType.Certificateorganization name in text, certificate text like: CCNA, MCSA, etc.
Do you have permission X from organization y?QuestionType.Permissionorganization name in text, clearance level text like: RS, ERS, etc.

Subject's answer packet's structure is:

{
    subjectDID: string;
    answerArgs: string[];
    startValidationTime: number;
    endValidationTime: number;
}

subjectDID, startValidationTime and endValidationTime are obvious but to set the answerArgs you can use following table as reference.

typeanswerArgs
QuestionType.Relationrelation text like: employee, representative, etc.
QuestionType.Positionposition text like: CTO, CEO, etc.
QuestionType.Certificatecertificate text like: CCNA, MCSA, etc.
QuestionType.Permissionclearance level text like: RS, ERS, etc.

Notice Since queryArgs and answerArgs are general and their values do not have any effect on the protocol, to minimize the size of packets you can use abbreviations. For example instead of using employee you can use e, etc.