GroupSenderCtx.java

/*******************************************************************************
 * Copyright (c) 2023 RISE SICS 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:
 *    Rikard Höglund (RISE SICS)
 *    
 ******************************************************************************/
package org.eclipse.californium.oscore.group;

import java.util.HashMap;
import java.util.Map.Entry;



import org.eclipse.californium.cose.AlgorithmID;
import org.eclipse.californium.cose.CoseException;
import org.eclipse.californium.cose.OneKey;
import org.eclipse.californium.elements.util.Base64;
import org.eclipse.californium.elements.util.Bytes;
import org.eclipse.californium.oscore.ByteId;
import org.eclipse.californium.oscore.OSCoreCtx;
import org.eclipse.californium.oscore.OSException;

/**
 * Class implementing a Group OSCORE sender context.
 *
 */
public class GroupSenderCtx extends OSCoreCtx {

	private final static int DEFAULT_MAX_UNFRAGMENTED_SIZE = 4096;

	GroupCtx commonCtx;
	OneKey ownPrivateKey;
	byte[] ownPublicKeyRaw = Bytes.EMPTY;

	HashMap<ByteId, byte[]> pairwiseSenderKeys;

	GroupSenderCtx(byte[] master_secret, boolean client, AlgorithmID alg, byte[] sender_id, byte[] recipient_id,
			AlgorithmID kdf, Integer replay_size, byte[] master_salt, byte[] contextId, OneKey ownPrivateKey,
			byte[] ownPublicKeyRaw, GroupCtx commonCtx) throws OSException {
		// Build OSCORE Context using OSCoreCtx constructor
		super(master_secret, client, alg, sender_id, recipient_id, kdf, replay_size, master_salt, contextId,
				DEFAULT_MAX_UNFRAGMENTED_SIZE);

		this.commonCtx = commonCtx;
		this.ownPrivateKey = ownPrivateKey;
		if (ownPublicKeyRaw != null) {
			this.ownPublicKeyRaw = ownPublicKeyRaw;
		}

		pairwiseSenderKeys = new HashMap<ByteId, byte[]>();
	}

	/**
	 * Derive pairwise keys for this sender context and all associated recipient
	 * contexts
	 */
	void derivePairwiseKeys() {

		for (Entry<ByteId, GroupRecipientCtx> entry : commonCtx.recipientCtxMap.entrySet()) {
			GroupRecipientCtx recipientCtx = entry.getValue();

			ByteId rid = new ByteId(recipientCtx.getRecipientId());
			
			// If the key has already been generated skip it
			if (pairwiseSenderKeys.get(rid) != null) {
				continue;
			}

			byte[] pairwiseSenderKey = commonCtx.derivePairwiseSenderKey(recipientCtx.getRecipientId(),
					recipientCtx.getRecipientKey(), recipientCtx.getPublicKey(), recipientCtx.getPublicKeyRaw());
			pairwiseSenderKeys.put(rid, pairwiseSenderKey);

		}
	}

	/**
	 * Get if responses should use pairwise mode. // TODO: Implement elsewhere
	 * to avoid cast?
	 * 
	 * @return if responses should use pairwise mode
	 */
	public boolean getPairwiseModeResponses() {
		return commonCtx.pairwiseModeResponses;
	}

	// TODO: Implement elsewhere to avoid cast?
	@Deprecated
	public boolean getPairwiseModeRequests() {
		return commonCtx.pairwiseModeRequests;
	}

	/**
	 * Get the pairwise sender key for this context for a specific other
	 * recipient.
	 * 
	 * @param recipientId the recipient ID of the other party
	 * @return the pairwise sender key to recipient
	 */
	public byte[] getPairwiseSenderKey(byte[] recipientId) {
		return pairwiseSenderKeys.get(new ByteId(recipientId));
	}

	// Just for interop tests
	public void setAsymmetricSenderKey(OneKey key) {
		ownPrivateKey = key;
	}

	/**
	 * Get the alg sign value.
	 * 
	 * @return the alg sign value
	 */
	public AlgorithmID getAlgSign() {
		return commonCtx.algSign;
	}

	/**
	 * Get the alg sign enc value.
	 * 
	 * @return the alg sign enc value
	 */
	public AlgorithmID getAlgSignEnc() {
		return commonCtx.algSignEnc;
	}

	/**
	 * Get the alg pairwise key agreement value.
	 * 
	 * @return the alg pairwise key agreement value.
	 */
	public AlgorithmID getAlgKeyAgreement() {
		return commonCtx.algKeyAgreement;
	}

	/**
	 * Get the length of the countersignature depending on the countersignature
	 * algorithm currently used.
	 * 
	 * @return the length of the countersiganture
	 */
	public int getCountersignatureLen() {
		return commonCtx.getCountersignatureLen();
	}

	/**
	 * Get the par countersign value for the external aad.
	 * 
	 * @return the par countersign value
	 */
	public int[][] getParCountersign() {
		return commonCtx.parCountersign;
	}

	/**
	 * Get the alg countersign key value for the external aad.
	 * 
	 * @return the alg countersign key value
	 */
	public int[] getParCountersignKey() {
		return commonCtx.parCountersign[1];
	}

	/**
	 * Get the private key associated to this sender context, meaning your own
	 * private key.
	 * 
	 * @return the private key
	 */
	public OneKey getPrivateKey() {
		return ownPrivateKey;
	}

	/**
	 * Get the raw bytes of the public key associated to this sender context,
	 * meaning your own public key.
	 * 
	 * @return the bytes of the public key
	 */
	public byte[] getPublicKeyRaw() {
		return ownPublicKeyRaw;
	}

	@Override
	protected GroupSenderCtx getSenderCtx() {
		return this;
	}

	/**
	 * Get the common context associated to this GroupSenderCtx.
	 * 
	 * @return the common context associated to this GroupSenderCtx
	 */
	public GroupCtx getCommonCtx() {
		return commonCtx;
	}

	// ------- TODO: Remove methods below -------

	public OneKey getPublicKey() {
		StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
		System.err.println("Bad call to getPublicKey on GroupSenderCtx" + stackTraceElements[2].toString());
		return null;
	}

	// Rikard: Generate a key to be used for Countersignatures
	public static void generateCounterSignKey(AlgorithmID alg) throws CoseException {
		OneKey myKey = OneKey.generateKey(alg);

		// Print base64 encoded version with both public & private keys
		byte[] keyObjectBytes = myKey.EncodeToBytes();
		String base64_encoded = Base64.encodeBytes(keyObjectBytes);
		System.out.println("Public & Private: " + base64_encoded);

		// Print base64 encoded version with only public keys
		OneKey publicKey = myKey.PublicKey();

		keyObjectBytes = publicKey.EncodeToBytes();
		base64_encoded = Base64.encodeBytes(keyObjectBytes);
		System.out.println("Public only: " + base64_encoded);

	}


	/**
	 * @return size of recipient replay window
	 */
	@Override
	public int getRecipientReplaySize() {
		System.err.println("Bad call to getRecipientReplaySize");
		StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
		System.err.println("Bad call to getRecipientReplaySize on GroupSenderCtx" + stackTraceElements[2].toString());
		System.exit(0);
		return recipient_replay_window_size;
	}

	/**
	 * @return recipient replay window
	 */
	@Override
	public int getRecipientReplayWindow() {
		System.err.println("Bad call to getRecipientReplayWindow");
		StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
		System.err.println("Bad call to getRecipientReplayWindow on GroupSenderCtx" + stackTraceElements[2].toString());
		System.exit(0);
		return recipient_replay_window;
	}

	@Override
	public void setRecipientKey(byte[] recipientKey) {
		StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
		System.err.println("Bad call to setRecipientKey on GroupSenderCtx" + stackTraceElements[2].toString());
		System.exit(0);
		System.exit(0);
		super.setRecipientKey(recipientKey);
	}

	/**
	 * @param seq the recipient sequence number to set
	 */
	public synchronized void setReceiverSeq(int seq) {
		StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
		System.err.println("Bad call to setReceiverSeq on GroupSenderCtx" + stackTraceElements[2].toString());
		System.exit(0);
		super.setReceiverSeq(seq);
	}

	public int rollbackRecipientSeq() {
		StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
		System.err.println("Bad call to rollbackRecipientSeq on GroupSenderCtx" + stackTraceElements[2].toString());
		System.exit(0);
		return super.rollbackRecipientSeq();
	}

	public int rollbackRecipientReplay() {
		StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
		System.err.println("Bad call to rollbackRecipientReplay on GroupSenderCtx" + stackTraceElements[2].toString());
		System.exit(0);
		return super.rollbackRecipientReplay();
	}

	/**
	 * @return the repipient's identifier
	 */
	public byte[] getRecipientId() {
		StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
		// System.err.println("Bad call to getRecipientId on GroupSenderCtx. " +
		// stackTraceElements[1].toString());
		System.err.println("Bad call to getRecipientId on GroupSenderCtx. " + stackTraceElements[2].toString());
		// System.err.println("Bad call to getRecipientId on GroupSenderCtx. " +
		// stackTraceElements[3].toString());
		System.exit(0);
		return super.getRecipientId();
	}

	/**
	 * @return get the receiver sequence number
	 */
	public synchronized int getReceiverSeq() {
		StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
		System.err.println("Bad call to getReceiverSeq on GroupSenderCtx" + stackTraceElements[2].toString());
		System.exit(0);
		return super.getReceiverSeq();
	}

	/**
	 * @return get the recipient key
	 */
	public byte[] getRecipientKey() {
		StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
		System.err.println("Bad call to getRecipientKey on GroupSenderCtx" + stackTraceElements[2].toString());
		System.exit(0);
		return super.getRecipientKey();
	}

}