EdhocSession.java

/*******************************************************************************
 * Copyright (c) 2020 RISE and others.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 * 
 * The Eclipse Public License is available at
 *    http://www.eclipse.org/legal/epl-v20.html
 * and the Eclipse Distribution License is available at
 *    http://www.eclipse.org/org/documents/edl-v10.html.
 * 
 * Contributors:
 *    Marco Tiloca (RISE)
 *    Rikard Höglund (RISE)
 *    
 ******************************************************************************/

package org.eclipse.californium.edhoc;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import org.eclipse.californium.cose.AlgorithmID;
import org.eclipse.californium.cose.KeyKeys;
import org.eclipse.californium.cose.OneKey;
import org.eclipse.californium.oscore.HashMapCtxDB;

import com.upokecenter.cbor.CBORObject;
import com.upokecenter.cbor.CBORType;

public class EdhocSession {
	
	// Authentication credentials of this peer
	//
    // At the top level, authentication credential are sorted by key usage of the authentication keys.
    // The outer map has label SIGNATURE_KEY or ECDH_KEY for distinguishing the two key usages. 
    
    // The asymmetric key pairs of this peer (one per supported curve)
	private HashMap<Integer, HashMap<Integer, OneKey>> keyPairs = new HashMap<Integer, HashMap<Integer, OneKey>>();
    
    // The identifiers of the authentication credentials of this peer
	private HashMap<Integer, HashMap<Integer, CBORObject>> idCreds = new HashMap<Integer, HashMap<Integer, CBORObject>>();
    
    // The authentication credentials of this peer (one per supported curve)
	private HashMap<Integer, HashMap<Integer, CBORObject>> creds = new HashMap<Integer, HashMap<Integer, CBORObject>>();

	// The side processor to use with this session
	//
	// It is required to be also in the EDHOC session, to be accessible
	// also by the EDHOC layer, when receiving an EDHOC+OSCORE request
	// targeting a different resource than an EDHOC resource. 
	private SideProcessor sideProcessor;
	
	// The database of OSCORE Security Contexts.
	// It can be null, if the EDHOC session has occurred
	// with an EDHOC resource not used to key OSCORE
	private HashMapCtxDB db;
	
	private int currentStep;
	
	private boolean initiator;
	private boolean clientInitiated;
	private int method;
	private int selectedCipherSuite;
	private byte[] connectionId;
	private OneKey keyPair;
	private CBORObject idCred;
	private byte[] cred; // This is the serialization of a CBOR object
	private OneKey ephemeralKey;
	
	private List<Integer> supportedCipherSuites;
	private Set<Integer> supportedEADs;
	private AppProfile appProfile;
	private int trustModel;
	
	private byte[] peerConnectionId;
	private CBORObject peerIdCred = null;
	private byte[] peerCred = null; // This is the serialization of a CBOR object
	
	private OneKey peerLongTermPublicKey = null;
	private OneKey peerEphemeralPublicKey = null;
	private List<Integer> peerSupportedCipherSuites = null;
	
	// Stored hash of EDHOC Message 1
	private byte[] hashMessage1 = null;
	
	// Stored PLAINTEXT_2, as serialized CBOR sequence
	private byte[] plaintext2 = null;
	
	// Inner Key-Derivation Keys
	private byte[] prk_2e = null;
	private byte[] prk_3e2m = null;
	private byte[] prk_4e3m = null;
	
	// Transcript Hashes
	private byte[] TH2 = null;
	private byte[] TH3 = null;
	private byte[] TH4 = null;
	
	// Key to store after a successful EDHOC execution
	private byte[] prk_out = null;
	private byte[] prk_exporter = null;
	
	// EDHOC message_3 , to be used for building an EDHOC+OSCORE request
	private byte[] message3 = null;
	
	public EdhocSession(boolean initiator, boolean clientInitiated, int method, byte[] connectionId,
						HashMap<Integer, HashMap<Integer, OneKey>> keyPairs,
						HashMap<Integer, HashMap<Integer, CBORObject>> idCreds,
						HashMap<Integer, HashMap<Integer, CBORObject>> creds,
						List<Integer> cipherSuites, List<Integer> peerCipherSuites,
						Set<Integer> eads, AppProfile appProfile, int trustModel, HashMapCtxDB db) {
		
		this.initiator = initiator;
		this.clientInitiated = clientInitiated;
		this.method = method;
		this.connectionId = connectionId;
		
		this.keyPairs = keyPairs;
		this.idCreds = idCreds;
		this.creds = creds;
		
		this.keyPair = null;
		this.idCred = null;
		this.cred = null;
		this.ephemeralKey = null;
		
		this.supportedCipherSuites = cipherSuites;
		this.supportedEADs = eads;
		this.appProfile = appProfile;
		this.trustModel = trustModel;
		this.db = db;
		
		this.selectedCipherSuite = -1;
		
		this.peerConnectionId = null;
		this.peerSupportedCipherSuites = peerCipherSuites;
		
		currentStep = initiator ? Constants.EDHOC_BEFORE_M1 : Constants.EDHOC_BEFORE_M2;
		
	}
	
	/**
	 * Delete all ephemeral keys and other temporary material used during the session
	 */
	public void deleteTemporaryMaterial() {
		
		this.ephemeralKey = null;
		this.peerEphemeralPublicKey = null;
		this.prk_2e = null;
		this.prk_3e2m = null;
		this.TH2 = null;
		this.TH3 = null;
		
		if (this.appProfile.getUseMessage4() == false) {
			this.prk_4e3m = null;
			this.TH4 = null;
		}
		
	}
	
	/**
	 * @return  True if this peer is the initiator, or False otherwise 
	 */
	public boolean isInitiator() {
		return this.initiator;
	}
	
	/**
	 * @return  the authentication method of this peer 
	 */
	public int getMethod() {
		return this.method;
	}
	
	/**
	 * @return  True if the CoAP client is the initiator, or False otherwise 
	 */
	public boolean isClientInitiated() {
		return this.clientInitiated;
	}
		
	/**
	 * @return  the Connection Identifier of this peer
	 */
	public byte[] getConnectionId() {
		return this.connectionId;
	}	
	
	/**
	 * @return  the key pair of this peer 
	 */
	public OneKey getKeyPair() {
		
		return this.keyPair;
		
	}
	
	/**
	 * @return  the ID_CRED for the long term key of this peer  
	 */
	public CBORObject getIdCred() {
		
		return this.idCred;
		
	}
	
	/**
	 * @return  the CRED of this peer
	 */
	public byte[] getCred() {
		
		return this.cred;
		
	}
	
	/** 
	 */
	public void setAuthenticationCredential() {
		
		int keyUsage = -1;
		int curve = -1;
		
		if (this.method == Constants.EDHOC_AUTH_METHOD_0) {
			keyUsage = Constants.SIGNATURE_KEY;
		}
		if (this.method == Constants.EDHOC_AUTH_METHOD_1) {
			keyUsage = initiator ? Constants.SIGNATURE_KEY : Constants.ECDH_KEY;
		}
		if (this.method == Constants.EDHOC_AUTH_METHOD_2) {
			keyUsage = initiator ? Constants.ECDH_KEY : Constants.SIGNATURE_KEY;
		}
		if (this.method == Constants.EDHOC_AUTH_METHOD_3) {
			keyUsage = Constants.ECDH_KEY;
		}
		
		if (this.selectedCipherSuite == Constants.EDHOC_CIPHER_SUITE_0 || this.selectedCipherSuite == Constants.EDHOC_CIPHER_SUITE_1) {
			
			if (this.method == Constants.EDHOC_AUTH_METHOD_0) {
				curve = Constants.CURVE_Ed25519;
			}
			if (this.method == Constants.EDHOC_AUTH_METHOD_1) {
				curve = initiator ? Constants.CURVE_Ed25519 : Constants.CURVE_X25519;
			}
			if (this.method == Constants.EDHOC_AUTH_METHOD_2) {
				curve = initiator ? Constants.CURVE_X25519 : Constants.CURVE_Ed25519;
			}
			if (this.method == Constants.EDHOC_AUTH_METHOD_3) {
				curve = Constants.CURVE_X25519;
			}
			
		}
		if (this.selectedCipherSuite == Constants.EDHOC_CIPHER_SUITE_2 || this.selectedCipherSuite == Constants.EDHOC_CIPHER_SUITE_3) {
				curve = Constants.CURVE_P256;
		}
		
		this.keyPair = this.keyPairs.get(Integer.valueOf(keyUsage)).
									 get(Integer.valueOf(curve));
		this.cred = this.creds.get(Integer.valueOf(keyUsage)).
							   get(Integer.valueOf(curve)).GetByteString();
		this.idCred = this.idCreds.get(Integer.valueOf(keyUsage)).
								   get(Integer.valueOf(curve));
				
	}
	
	/**
	 * @param ek  the ephemeral key pair of this peer 
	 */
	public void setEphemeralKey(OneKey ek) {
		
		this.ephemeralKey = ek;
		
	}
	
	/**
	 * @param ek  the ephemeral key pair of this peer 
	 */
	public void setEphemeralKey() {
		
		OneKey ek = null;
		if (this.selectedCipherSuite == Constants.EDHOC_CIPHER_SUITE_0 || this.selectedCipherSuite == Constants.EDHOC_CIPHER_SUITE_1)
			ek = Util.generateKeyPair(KeyKeys.OKP_X25519.AsInt32());
		else if (this.selectedCipherSuite == Constants.EDHOC_CIPHER_SUITE_2 || this.selectedCipherSuite == Constants.EDHOC_CIPHER_SUITE_3)
			ek = Util.generateKeyPair(KeyKeys.EC2_P256.AsInt32());
		
		setEphemeralKey(ek);
		
	}

	
	/**
	 * @return  the ephemeral key pair of this peer 
	 */
	public OneKey getEphemeralKey() {
		
		return this.ephemeralKey;
		
	}
	
	/**
	 * @param cipherSuites  the supported cipher suites to indicate in EDHOC messages
	 */
	public void setSupportedCipherSuites(List<Integer> cipherSuites) {

		this.supportedCipherSuites = cipherSuites;
		
	}
	
	/**
	 * @return  the supported cipher suites to indicate in EDHOC messages
	 */
	public List<Integer> getSupportedCipherSuites() {

		return this.supportedCipherSuites;
		
	}
	
	/**
	 * @return  the supported EAD items
	 */
	public Set<Integer> getSupportedEADs() {

		return this.supportedEADs;
		
	}
	
	/**
	 * @return  the application profile used for this session
	 */
	public AppProfile getApplicationProfile() {

		return this.appProfile;
		
	}
	
	/**
	 * @return  the used trust model for validating authentication credentials of the other peer
	 */
	public int getTrustModel() {

		return this.trustModel;
		
	}	
	
	/**
	 * @return  the side processor object for this session
	 */
	public SideProcessor getSideProcessor() {
		
		return this.sideProcessor;
		
	}
	
	/**
	 * Set the side processor object for this session
	 * @param  the side processor object for this session
	 */
	public void setSideProcessor(SideProcessor sideProcessor) {
		
		this.sideProcessor = sideProcessor;
		
	}
	
	/**
	 * @return  the database of OSCORE Security Contexts
	 */
	public HashMapCtxDB getOscoreDb() {
		return this.db;
	}

	/**
	 * @return  the current step in the execution of the EDHOC protocol 
	 */
	public int getCurrentStep() {
		return this.currentStep;
	}
	
	/**
	 * Set the current step in the execution of the EDHOC protocol
	 * @param newStep   the new step to set 
	 */
	public void setCurrentStep(int newStep) {
		this.currentStep = newStep;
	}
	
	/**
	 * @return  the selected cipher suite for this EDHOC session 
	 */
	public int getSelectedCipherSuite() {
		return this.selectedCipherSuite;
	}
	
	/**
	 * Set the selected cipher suite for this EDHOC session
	 * @param cipherSuite   the selected cipher suite 
	 */
	public void setSelectedCipherSuite(int cipherSuite) {
		this.selectedCipherSuite = cipherSuite;
	}
	
	/**
	 * @return  the Connection Identifier of the other peer
	 */
	public byte[] getPeerConnectionId() {
		return this.peerConnectionId;
	}
	
	/**
	 * Set the Connection Identifier of the other peer
	 * @param peerId   the Connection Id of the other peer
	 */
	public void setPeerConnectionId(byte[] peerId) {
		this.peerConnectionId = peerId;
	}
	
	/**
	 * @return  the list of the cipher suites supported by the peer
	 */
	public List<Integer> getPeerSupportedCipherSuites() {
		return this.peerSupportedCipherSuites;
	}
	
	/**
	 * @return  the CRED of the other peer
	 */
	public byte[] getPeerCred() {
		return this.peerCred;
	}
	
	/**
	 * Set the CRED of the other peer
	 * @param peerCred   the CRED of the other peer 
	 */
	public void setPeerCred(byte[] peerCred) {
		this.peerCred = peerCred;
	}

	/**
	 * @return  the long-term public key of the other peer
	 */
	public OneKey getPeerLongTermPublicKey() {
		return this.peerLongTermPublicKey;
	}
	
	/**
	 * Set the long-term public key of the other peer
	 * @param peerKey   the long-term public key of the other peer 
	 */
	public void setPeerLongTermPublicKey(OneKey peerKey) {
		this.peerLongTermPublicKey = peerKey;
	}

	/**
	 * @return  the ID_CRED of the long-term public key of the other peer
	 */
	public CBORObject getPeerIdCred() {
		return this.peerIdCred;
	}
	
	/**
	 * Set the ID_CRED of the long-term public key of the other peer
	 */
	public void setPeerIdCred(CBORObject idCred) {
		this.peerIdCred = idCred;
	}

	/**
	 * @return  the ephemeral public key of the other peer
	 */
	public OneKey getPeerEphemeralPublicKey() {
		return this.peerEphemeralPublicKey;
	}
	
	/**
	 * Set the ephemeral public key of the other peer
	 * @param peerKey   the ephemeral public key of the other peer 
	 */
	public void setPeerEphemeralPublicKey(OneKey peerKey) {
		this.peerEphemeralPublicKey = peerKey;
	}

	/**
	 * @return  the inner key PRK_2e
	 */
	public byte[] getPRK2e() {
		return this.prk_2e;
	}
	
	/**
	 * @param prk2e   the inner key PRK_2e
	 */
	public void setPRK2e(byte[] prk2e) {
		this.prk_2e = new byte[prk2e.length];
		System.arraycopy(prk2e,  0, this.prk_2e, 0, prk2e.length);
	}

	/**
	 * @return  the inner key PRK_3e2m
	 */
	public byte[] getPRK3e2m() {
		return this.prk_3e2m;
	}
	
	/**
	 * @param prk3e2m   the inner key PRK_3e2m
	 */
	public void setPRK3e2m(byte[] prk3e2m) {
		this.prk_3e2m = new byte[prk3e2m.length];
		System.arraycopy(prk3e2m,  0, this.prk_3e2m, 0, prk3e2m.length);
	}

	/**
	 * @return  the inner key PRK_4e3m
	 */
	public byte[] getPRK4e3m() {
		return this.prk_4e3m;
	}
	
	/**
	 * @param prk4e3m   the inner key PRK_4e3m
	 */
	public void setPRK4e3m(byte[] prk4e3m) {
		if (prk4e3m == null)
			this.prk_4e3m = null;
		else {
			this.prk_4e3m = new byte[prk4e3m.length];
			System.arraycopy(prk4e3m,  0, this.prk_4e3m, 0, prk4e3m.length);
		}
	}
	
	/**
	 * @return  the Transcript Hash TH2
	 */
	public byte[] getTH2() {
		return this.TH2;
	}
	
	/**
	 * Set the Transcript Hash TH2 
	 * @param inputTH   the Transcript Hash TH2
	 */
	public void setTH2(byte[] inputTH) {
		this.TH2 = inputTH;
	}
		
	/**
	 * @return  the Transcript Hash TH3
	 */
	public byte[] getTH3() {
		return this.TH3;
	}
	
	/**
	 * Set the Transcript Hash TH3 
	 * @param inputTH   the Transcript Hash TH3
	 */
	public void setTH3(byte[] inputTH) {
		this.TH3 = inputTH;
	}
	
	/**
	 * @return  the Transcript Hash TH4
	 */
	public byte[] getTH4() {
		return this.TH4;
	}
	
	/**
	 * Set the Transcript Hash TH4
	 * @param inputTH   the Transcript Hash TH4
	 */
	public void setTH4(byte[] inputTH) {
		this.TH4 = inputTH;
	}

	/**
	 * @return  the key PRK_out
	 */
	public byte[] getPRKout() {
		return this.prk_out;
	}
	
	/**
	 * @param prkOut   the key PRK_out
	 */
	public void setPRKout(byte[] prkOut) {
		this.prk_out = new byte[prkOut.length];
		System.arraycopy(prkOut,  0, this.prk_out, 0, prkOut.length);
	}
	
	/**
	 * @return  the key PRK_exporter
	 */
	public byte[] getPRKexporter() {
		return this.prk_exporter;
	}
	
	/**
	 * @param prkOut   the key PRK_exporter
	 */
	public void setPRKexporter(byte[] prkExporter) {
		this.prk_exporter = new byte[prkExporter.length];
		System.arraycopy(prkExporter,  0, this.prk_exporter, 0, prkExporter.length);
	}
	
	/**
	 * @return  the hash of EDHOC Message 1
	 */
	public byte[] getHashMessage1() {
		return this.hashMessage1;
	}
	
	/**
	 * @param msg  an EDHOC Message 1 of which to store the hash for later computation of TH2
	 * @return  true in case of success, or false in case of error 
	 */
	public boolean setHashMessage1(byte[] msg) {
		
		byte[] hash = null;
		String hashAlgorithm = null;
		
		int selectedCipherSuite = getSelectedCipherSuite();
		hashAlgorithm = getEdhocHashAlg(selectedCipherSuite);

		try {
			hash = Util.computeHash(msg, hashAlgorithm);
		} catch (NoSuchAlgorithmException e) {
		    System.err.println("Invalid hash algorithm when computing the hash of EDHOC Message 1\n" + e.getMessage());
		    return false;
		}
		
		if (hash == null) {
		    System.err.println("Error when computing the hash of EDHOC Message 1\n");
			return false;
		}
		
		this.hashMessage1 = new byte[hash.length];
		System.arraycopy(hash, 0, this.hashMessage1, 0, hash.length);
		return true;
	}
	
    /**
     *  Clean up the stored hash of EDHOC Message 1
     */
	public void cleanMessage1() {
		this.hashMessage1 = null;
	}
	
    /**
     * @return  the EDHOC Message 3
     */
	public byte[] getMessage3() {
		return this.message3;
	}
	
    /**
     * @param msg   The EDHOC message_3 to store, before an EDHOC+OSCORE request
     */
	public void setMessage3(byte[] msg) {
		this.message3 = new byte[msg.length];
		System.arraycopy(msg, 0, this.message3, 0, msg.length);
	}
	
    /**
     *  Clean up the stored EDHOC Message 3
     */
	public void cleanMessage3() {
		this.message3 = null;
	}
	
	/**
	 * @return  the PLAINTEXT_2
	 */
	public byte[] getPlaintext2() {
		return this.plaintext2;
	}

	/**
	 * @param pt  store a PLAINTEXT_2 for the later computation of TH3
	 */
	public void setPlaintext2(byte[] pt) {
		this.plaintext2 = new byte[pt.length];
		System.arraycopy(pt, 0, this.plaintext2, 0, pt.length);
	}
	
	/**
	 * EDHOC-Exporter function, to derive application keys
	 * @param label   The exporter_label to use to derive the OKM
	 * @param context   The context to use to derive the OKM, as a CBOR byte string
	 * @param len   The intended length of the OKM to derive, in bytes
	 * @return  the application key, or null in case of errors
	 */
	public byte[] edhocExporter(int label, CBORObject context, int len) throws InvalidKeyException, NoSuchAlgorithmException {
		
		if (label < 0 || context.getType() != CBORType.ByteString || len < 0)
			return null;
		
		if (this.currentStep != Constants.EDHOC_AFTER_M3 && this.currentStep != Constants.EDHOC_SENT_M3)
			return null;
	
		return edhocKDF(this.prk_exporter, label, context, len);
		
	}
	
	/**
	 * EDHOC-KeyUpdate function, to update the keys PRK_out and PRK_exporter
	 * @param context   The context to use, as a CBOR byte string
	 * @return  true in case of success, or false otherwise
	 */
	public boolean edhocKeyUpdate(CBORObject context) throws InvalidKeyException, NoSuchAlgorithmException {
		
		// The EDHOC execution is not completed yet
		if (this.currentStep != Constants.EDHOC_AFTER_M3)
			return false;
		
		// The provided context is not valid
		if (context == null || context.getType() != CBORType.ByteString)
			return false;
	
		// Update PRK_out
		int length = EdhocSession.getEdhocHashAlgOutputSize(this.selectedCipherSuite);
		try {
			this.prk_out = edhocKDF(this.prk_out, Constants.KDF_LABEL_PRK_OUT_KEY_UPDATE, context, length);
		} catch (InvalidKeyException e) {
			System.err.println("Error when updating PRK_out\n" + e.getMessage());
			return false;
		} catch (NoSuchAlgorithmException e) {
			System.err.println("Error when updating PRK_out\n" + e.getMessage());
			return false;
		}
	    Util.nicePrint("PRK_out (updated)", this.prk_out);
		
		// Update PRK_exporter
	    this.prk_exporter = MessageProcessor.computePRKexporter(this, this.prk_out);
	    if (prk_exporter == null) {
			System.err.println("Error when updating PRK_exporter\n");
			return false;
		}
	    Util.nicePrint("PRK_exporter (updated)", this.prk_exporter);

		return true;

	}
	
	/**
	 * EDHOC-KDF
	 * @param prk   The Pseudo Random Key
	 * @param label   The info_label to use to derive the OKM
	 * @param context   The context to use to derive the OKM, as a CBOR byte string
	 * @param length   The intended length of the OKM to derive, in bytes
	 * @return  the OKM generated by HKDF-Expand
	 */
	public byte[] edhocKDF(byte[] prk, int label, CBORObject context, int length)
			throws InvalidKeyException, NoSuchAlgorithmException {
		
		if (prk == null || context == null)
			return null;
		
		if (context.getType() != CBORType.ByteString)
			return null;
		
        // Prepare the 'info' CBOR sequence
        List<CBORObject> objectList = new ArrayList<>();
        objectList.add(CBORObject.FromObject(label));
        objectList.add(context);
        objectList.add(CBORObject.FromObject(length));
		byte[] info = Util.buildCBORSequence(objectList);
		
		byte[] okm = null;
		String hashAlgorithm = EdhocSession.getEdhocHashAlg(selectedCipherSuite);
		
		if (hashAlgorithm.equals("SHA-256") || hashAlgorithm.equals("SHA-384") || hashAlgorithm.equals("SHA-512")) {
			okm = Hkdf.expand(prk, info, length);
		}
		
		return okm;
		
	}

    /**
     *  Get an OSCORE Master Secret using the EDHOC-Exporter
     * @param session   The used EDHOC session
     * @return  the OSCORE Master Secret, or null in case of errors
     */
	public static byte[] getMasterSecretOSCORE(EdhocSession session) {

	    byte[] masterSecret = null;
	    int selectedCipherSuite = session.getSelectedCipherSuite();
	    
	    CBORObject context = CBORObject.FromObject(new byte[0]);
	    int keyLength = getKeyLengthAppAEAD(selectedCipherSuite);
	    
	    try {
			masterSecret = session.edhocExporter(Constants.EXPORTER_LABEL_OSCORE_MASTER_SECRET, context, keyLength);
		} catch (InvalidKeyException e) {
			System.err.println("Error when the OSCORE Master Secret" + e.getMessage());
		} catch (NoSuchAlgorithmException e) {
			System.err.println("Error when the OSCORE Master Secret" + e.getMessage());
		}
	    
	    return masterSecret;
		
	}
	
    /**
     *  Get an OSCORE Master Salt using the EDHOC-Exporter
     * @param session   The used EDHOC session
     * @return  the OSCORE Master Salt, or null in case of errors
     */
	public static byte[] getMasterSaltOSCORE(EdhocSession session) {

	    byte[] masterSalt = null;
	    CBORObject context = CBORObject.FromObject(new byte[0]);
	    
	    try {
			masterSalt = session.edhocExporter(Constants.EXPORTER_LABEL_OSCORE_MASTER_SALT, context, 8);
		} catch (InvalidKeyException e) {
			System.err.println("Error when the OSCORE Master Salt" + e.getMessage());
		} catch (NoSuchAlgorithmException e) {
			System.err.println("Error when the OSCORE Master Salt" + e.getMessage());
		}
	    
	    return masterSalt;
		
	}
	
    /**
     *  Get the EDHOC AEAD algorithm associated to the selected cipher suite
     * @param cipherSuite   The selected cipher suite
     * @return  the EDHOC AEAD algorithm associated to the selected cipher suite
     */
	public static AlgorithmID getEdhocAEADAlg(int cipherSuite) {

		AlgorithmID alg = null;
	    
    	switch (cipherSuite) {
			case Constants.EDHOC_CIPHER_SUITE_0:
			case Constants.EDHOC_CIPHER_SUITE_2:
				alg = AlgorithmID.AES_CCM_16_64_128;
				break;
			case Constants.EDHOC_CIPHER_SUITE_1:
			case Constants.EDHOC_CIPHER_SUITE_3:
				alg = AlgorithmID.AES_CCM_16_128_128;
				break;
		}
	    
	    return alg;
		
	}
	
    /**
     *  Get the key length (in bytes) for the EDHOC AEAD algorithm associated to the selected cipher suite
     * @param cipherSuite   The selected cipher suite
     * @return  the key length (in bytes) for the EDHOC AEAD algorithm associated to the selected cipher suite
     */
	public static int getKeyLengthEdhocAEAD(int cipherSuite) {

		int keyLength = 0;
	    
		switch (cipherSuite) {
	    	case Constants.EDHOC_CIPHER_SUITE_0:
	    	case Constants.EDHOC_CIPHER_SUITE_1:
	    	case Constants.EDHOC_CIPHER_SUITE_2:
	    	case Constants.EDHOC_CIPHER_SUITE_3:
	    		keyLength = 16;
		}
	    
	    return keyLength;
		
	}
	
    /**
     *  Get the IV length (in bytes) for the EDHOC AEAD algorithm associated to the selected cipher suite
     * @param cipherSuite   The selected cipher suite
     * @return  the IV length (in bytes) for the EDHOC AEAD algorithm associated to the selected cipher suite
     */
	public static int getIvLengthEdhocAEAD(int cipherSuite) {

		int ivLength = 0;
	    
		switch (cipherSuite) {
	    	case Constants.EDHOC_CIPHER_SUITE_0:
	    	case Constants.EDHOC_CIPHER_SUITE_1:
	    	case Constants.EDHOC_CIPHER_SUITE_2:
	    	case Constants.EDHOC_CIPHER_SUITE_3:
	    		ivLength = 13;
		}
	    
	    return ivLength;
		
	}
	
    /**
     *  Get the Tag length (in bytes) for the EDHOC AEAD algorithm associated to the selected cipher suite
     * @param cipherSuite   The selected cipher suite
     * @return  the Tag length (in bytes) for the EDHOC AEAD algorithm associated to the selected cipher suite
     */
	public static int getTagLengthEdhocAEAD(int cipherSuite) {

		int tagLength = 0;
	    
		switch (cipherSuite) {
	    	case Constants.EDHOC_CIPHER_SUITE_0:
	    	case Constants.EDHOC_CIPHER_SUITE_2:
	    		tagLength = 8;
	    		break;
	    	case Constants.EDHOC_CIPHER_SUITE_1:
	    	case Constants.EDHOC_CIPHER_SUITE_3:
	    		tagLength = 16;
		}
	    
	    return tagLength;
		
	}
	
    /**
     *  Get the EDHOC Hash algorithm associated to the selected cipher suite
     * @param cipherSuite   The selected cipher suite
     * @return  the EDHOC Hash algorithm associated to the selected cipher suite
     */
	public static String getEdhocHashAlg(int cipherSuite) {

		String hashAlg = null;
	    
		switch (cipherSuite) {
			case Constants.EDHOC_CIPHER_SUITE_0:
			case Constants.EDHOC_CIPHER_SUITE_1:
			case Constants.EDHOC_CIPHER_SUITE_2:
			case Constants.EDHOC_CIPHER_SUITE_3:
				hashAlg = "SHA-256";
		}
	    
	    return hashAlg;
		
	}
	
    /**
     *  Get the output size (in bytes) of the EDHOC Hash algorithm associated to the selected cipher suite
     * @param cipherSuite   The selected cipher suite
     * @return  the EDHOC output size (in bytes) of the Hash algorithm associated to the selected cipher suite
     */
	public static int getEdhocHashAlgOutputSize(int cipherSuite) {

		int outputSize = 0;
	    
		switch (cipherSuite) {
			case Constants.EDHOC_CIPHER_SUITE_0:
			case Constants.EDHOC_CIPHER_SUITE_1:
			case Constants.EDHOC_CIPHER_SUITE_2:
			case Constants.EDHOC_CIPHER_SUITE_3:
				outputSize = 32;
		}
	    
	    return outputSize;
		
	}
	
    /**
     *  Get the length (in bytes) of the ephemeral keys for the EDHOC key exchange algorithm
     *  (ECDH curve) associated to the selected cipher suite
     * @param cipherSuite   The selected cipher suite
     * @return  the length (in bytes) of the ephemeral keys for the EDHOC key exchange algorithm
     *          (ECDH curve) associated to the selected cipher suite
     */
	public static int getEphermeralKeyLength(int cipherSuite) {

		int keyLength = 0;
	    
		switch (cipherSuite) {
			case Constants.EDHOC_CIPHER_SUITE_0:
			case Constants.EDHOC_CIPHER_SUITE_1:
			case Constants.EDHOC_CIPHER_SUITE_2:
			case Constants.EDHOC_CIPHER_SUITE_3:
				keyLength = 32;
		}
	    
	    return keyLength;
		
	}
	
    /**
     *  Get the application AEAD algorithm associated to the selected cipher suite
     * @param cipherSuite   The selected cipher suite
     * @return  the application AEAD algorithm associated to the selected cipher suite
     */
	public static AlgorithmID getAppAEAD(int cipherSuite) {

		AlgorithmID alg = null;
	    
		switch (cipherSuite) {
			case Constants.EDHOC_CIPHER_SUITE_0:
			case Constants.EDHOC_CIPHER_SUITE_1:
			case Constants.EDHOC_CIPHER_SUITE_2:
			case Constants.EDHOC_CIPHER_SUITE_3:
				alg = AlgorithmID.AES_CCM_16_64_128;
		}
	    
	    return alg;
		
	}
	
    /**
     *  Get the key length (in bytes) for the application AEAD algorithm associated to the selected cipher suite
     * @param cipherSuite   The selected cipher suite
     * @return  the key length (in bytes) for the application AEAD algorithm associated to the selected cipher suite
     */
	public static int getKeyLengthAppAEAD(int cipherSuite) {

		int keyLength = 0;
	    
		switch (cipherSuite) {
	    	case Constants.EDHOC_CIPHER_SUITE_0:
	    	case Constants.EDHOC_CIPHER_SUITE_1:
	    	case Constants.EDHOC_CIPHER_SUITE_2:
	    	case Constants.EDHOC_CIPHER_SUITE_3:
	    		keyLength = 16;
		}
	    
	    return keyLength;
		
	}
	
    /**
     *  Get the application HKDF algorithm associated to the application hash algorithm of the selected cipher suite
     * @param cipherSuite   The selected cipher suite
     * @return  the application hkdf algorithm associated to the selected cipher suite
     */
	public static AlgorithmID getAppHkdf(int cipherSuite) {

		AlgorithmID alg = null;
	    
		switch (cipherSuite) {
			case Constants.EDHOC_CIPHER_SUITE_0:
			case Constants.EDHOC_CIPHER_SUITE_1:
			case Constants.EDHOC_CIPHER_SUITE_2:
			case Constants.EDHOC_CIPHER_SUITE_3:
				alg = AlgorithmID.HKDF_HMAC_SHA_256;
		}
	    
	    return alg;
		
	}
	
}