Recipient.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.eclipse.californium.cose;
import com.upokecenter.cbor.CBORObject;
import com.upokecenter.cbor.CBORType;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
/**
*
* @author jimsch
*/
public class Recipient extends Message {
OneKey privateKey;
private OneKey senderKey;
byte[] rgbEncrypted;
List<Recipient> recipientList;
@Override
public void DecodeFromCBORObject(CBORObject objRecipient) throws CoseException {
if ((objRecipient.size() != 3) && (objRecipient.size() !=4)) throw new CoseException("Invalid Recipient structure");
if (objRecipient.get(0).getType() == CBORType.ByteString) {
if (objRecipient.get(0).GetByteString().length == 0) objProtected = CBORObject.NewMap();
else objProtected = CBORObject.DecodeFromBytes(objRecipient.get(0).GetByteString());
}
else throw new CoseException("Invalid Recipient structure");
if (objRecipient.get(1).getType() == CBORType.Map) objUnprotected = objRecipient.get(1);
else throw new CoseException("Invalid Recipient structure");
if (objRecipient.get(2).getType() == CBORType.ByteString) rgbEncrypted = objRecipient.get(2).GetByteString();
else throw new CoseException("Invalid Recipient structure");
if (objRecipient.size() == 4) {
if (objRecipient.get(3).getType() == CBORType.Array) {
recipientList = new ArrayList<>();
for (int i=0; i<objRecipient.get(3).size(); i++) {
Recipient recipX = new Recipient();
recipX.DecodeFromCBORObject(objRecipient.get(3).get(i));
recipientList.add(recipX);
}
}
else throw new CoseException("Invalid Recipient structure");
}
}
@Override
protected CBORObject EncodeCBORObject() throws CoseException {
CBORObject obj = CBORObject.NewArray();
if (objProtected.size() > 0) obj.Add(objProtected.EncodeToBytes());
else obj.Add(CBORObject.FromObject(new byte[0]));
obj.Add(objUnprotected);
obj.Add(rgbEncrypted);
if (recipientList != null) {
CBORObject objR = CBORObject.NewArray();
for (Recipient r : recipientList) {
objR.Add(r.EncodeCBORObject());
}
obj.Add(objR);
}
return obj;
}
public byte[] decrypt(AlgorithmID algCEK, Recipient recip) throws CoseException {
AlgorithmID alg = AlgorithmID.FromCBOR(findAttribute(HeaderKeys.Algorithm));
byte[] rgbKey = null;
if (recip != this) {
for (Recipient r : recipientList) {
if (recip == r) {
rgbKey = r.decrypt(alg, recip);
if (rgbKey == null) throw new CoseException("Internal error");
break;
}
else if (!r.recipientList.isEmpty()) {
rgbKey = r.decrypt(alg, recip);
if (rgbKey != null) break;
}
}
}
switch (alg) {
case Direct: // Direct
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_Octet) throw new CoseException("Mismatch of algorithm and key");
return privateKey.get(KeyKeys.Octet_K.AsCBOR()).GetByteString();
case HKDF_HMAC_SHA_256:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_Octet) throw new CoseException("Needs to be an octet key");
return HKDF(privateKey.get(KeyKeys.Octet_K.AsCBOR()).GetByteString(), algCEK.getKeySize(), algCEK, "SHA256");
case HKDF_HMAC_SHA_512:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_Octet) throw new CoseException("Needs to be an octet key");
return HKDF(privateKey.get(KeyKeys.Octet_K.AsCBOR()).GetByteString(), algCEK.getKeySize(), algCEK, "SHA512");
case AES_KW_128:
case AES_KW_192:
case AES_KW_256:
if (rgbKey == null) {
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_Octet) throw new CoseException("Key and algorithm do not agree");
rgbKey = privateKey.get(KeyKeys.Octet_K.AsCBOR()).GetByteString();
}
else if (privateKey != null) throw new CoseException("Key and algorithm do not agree");
return AES_KeyWrap_Decrypt(alg, rgbKey);
case ECDH_ES_HKDF_256:
case ECDH_SS_HKDF_256:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
rgbKey = ECDH_GenSecret(privateKey);
return HKDF(rgbKey, algCEK.getKeySize(), algCEK, "SHA256");
case ECDH_ES_HKDF_512:
case ECDH_SS_HKDF_512:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
rgbKey = ECDH_GenSecret(privateKey);
return HKDF(rgbKey, algCEK.getKeySize(), algCEK, "SHA512");
case ECDH_ES_HKDF_256_AES_KW_128:
case ECDH_SS_HKDF_256_AES_KW_128:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
rgbKey = ECDH_GenSecret(privateKey);
rgbKey = HKDF(rgbKey, 128, AlgorithmID.AES_KW_128, "SHA256");
return AES_KeyWrap_Decrypt(AlgorithmID.AES_KW_128, rgbKey);
case ECDH_ES_HKDF_256_AES_KW_192:
case ECDH_SS_HKDF_256_AES_KW_192:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
rgbKey = ECDH_GenSecret(privateKey);
rgbKey = HKDF(rgbKey, 192, AlgorithmID.AES_KW_192, "SHA256");
return AES_KeyWrap_Decrypt(AlgorithmID.AES_KW_192, rgbKey);
case ECDH_ES_HKDF_256_AES_KW_256:
case ECDH_SS_HKDF_256_AES_KW_256:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
rgbKey = ECDH_GenSecret(privateKey);
rgbKey = HKDF(rgbKey, 256, AlgorithmID.AES_KW_256, "SHA256");
return AES_KeyWrap_Decrypt(AlgorithmID.AES_KW_256, rgbKey);
default:
throw new CoseException("Unsupported Recipent Algorithm");
}
}
public void encrypt() throws CoseException {
AlgorithmID alg = AlgorithmID.FromCBOR(findAttribute(HeaderKeys.Algorithm));
byte[] rgbKey = null;
SecureRandom random;
int recipientTypes = 0;
if (recipientList != null && !recipientList.isEmpty()) {
if (privateKey != null) throw new CoseException("Cannot have dependent recipients if key is specified");
for (Recipient r : recipientList) {
switch (r.getRecipientType()) {
case 1:
if ((recipientTypes & 1) != 0) throw new CoseException("Cannot have two direct recipients");
recipientTypes |= 1;
rgbKey = r.getKey(alg);
break;
default:
recipientTypes |= 2;
break;
}
}
}
if (recipientTypes == 3) throw new CoseException("Do not mix direct and indirect recipients");
if (recipientTypes == 2) {
rgbKey = new byte[alg.getKeySize()/8];
random = new SecureRandom();
random.nextBytes(rgbKey);
}
switch (alg) {
case Direct:
case HKDF_HMAC_SHA_256:
case HKDF_HMAC_SHA_512:
rgbEncrypted = new byte[0];
break;
case AES_KW_128:
case AES_KW_192:
case AES_KW_256:
if (rgbKey == null) {
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_Octet) throw new CoseException("Key and algorithm do not agree");
rgbKey = privateKey.get(KeyKeys.Octet_K.AsCBOR()).GetByteString();
}
rgbEncrypted = AES_KeyWrap_Encrypt(alg, rgbKey);
break;
case ECDH_ES_HKDF_256:
case ECDH_ES_HKDF_512:
case ECDH_SS_HKDF_256:
case ECDH_SS_HKDF_512:
rgbEncrypted = new byte[0];
break;
case ECDH_ES_HKDF_256_AES_KW_128:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
ECDH_GenEphemeral();
rgbKey = ECDH_GenSecret(privateKey);
rgbKey = HKDF(rgbKey, 128, AlgorithmID.AES_KW_128, "SHA256");
rgbEncrypted = AES_KeyWrap_Encrypt(AlgorithmID.AES_KW_128, rgbKey);
break;
case ECDH_SS_HKDF_256_AES_KW_128:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
if (findAttribute(HeaderKeys.HKDF_Context_PartyU_nonce.AsCBOR()) == null) {
byte[] rgbAPU = new byte[256/8];
random = new SecureRandom();
random.nextBytes(rgbAPU);
addAttribute(HeaderKeys.HKDF_Context_PartyU_nonce.AsCBOR(), CBORObject.FromObject(rgbAPU), Attribute.UNPROTECTED);
}
rgbKey = ECDH_GenSecret(privateKey);
rgbKey = HKDF(rgbKey, 128, AlgorithmID.AES_KW_128, "SHA256");
rgbEncrypted = AES_KeyWrap_Encrypt(AlgorithmID.AES_KW_128, rgbKey);
break;
case ECDH_ES_HKDF_256_AES_KW_192:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
ECDH_GenEphemeral();
rgbKey = ECDH_GenSecret(privateKey);
rgbKey = HKDF(rgbKey, 192, AlgorithmID.AES_KW_192, "SHA256");
rgbEncrypted = AES_KeyWrap_Encrypt(AlgorithmID.AES_KW_192, rgbKey);
break;
case ECDH_SS_HKDF_256_AES_KW_192:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
if (findAttribute(HeaderKeys.HKDF_Context_PartyU_nonce.AsCBOR()) == null) {
byte[] rgbAPU = new byte[256/8];
random = new SecureRandom();
random.nextBytes(rgbAPU);
addAttribute(HeaderKeys.HKDF_Context_PartyU_nonce.AsCBOR(), CBORObject.FromObject(rgbAPU), Attribute.UNPROTECTED);
}
rgbKey = ECDH_GenSecret(privateKey);
rgbKey = HKDF(rgbKey, 192, AlgorithmID.AES_KW_192, "SHA256");
rgbEncrypted = AES_KeyWrap_Encrypt(AlgorithmID.AES_KW_192, rgbKey);
break;
case ECDH_ES_HKDF_256_AES_KW_256:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
ECDH_GenEphemeral();
rgbKey = ECDH_GenSecret(privateKey);
rgbKey = HKDF(rgbKey, 256, AlgorithmID.AES_KW_256, "SHA256");
rgbEncrypted = AES_KeyWrap_Encrypt(AlgorithmID.AES_KW_256, rgbKey);
break;
case ECDH_SS_HKDF_256_AES_KW_256:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
if (findAttribute(HeaderKeys.HKDF_Context_PartyU_nonce.AsCBOR()) == null) {
byte[] rgbAPU = new byte[256/8];
random = new SecureRandom();
random.nextBytes(rgbAPU);
addAttribute(HeaderKeys.HKDF_Context_PartyU_nonce.AsCBOR(), CBORObject.FromObject(rgbAPU), Attribute.UNPROTECTED);
}
rgbKey = ECDH_GenSecret(privateKey);
rgbKey = HKDF(rgbKey, 256, AlgorithmID.AES_KW_256, "SHA256");
rgbEncrypted = AES_KeyWrap_Encrypt(AlgorithmID.AES_KW_256, rgbKey);
break;
default:
throw new CoseException("Unsupported Recipient Algorithm");
}
if (recipientList != null) {
for (Recipient r : recipientList) {
r.SetContent(rgbKey);
r.encrypt();
}
}
}
public void addRecipient(Recipient recipient) {
if (recipientList == null) recipientList = new ArrayList<Recipient>();
recipientList.add(recipient);
}
public List<Recipient> getRecipientList() {
return recipientList;
}
public Recipient getRecipient(int iRecipient) {
return recipientList.get(iRecipient);
}
public int getRecipientCount() {
return recipientList.size();
}
public int getRecipientType() throws CoseException {
AlgorithmID alg = AlgorithmID.FromCBOR(findAttribute(HeaderKeys.Algorithm));
switch (alg) {
case Direct:
case HKDF_HMAC_SHA_256:
case HKDF_HMAC_SHA_512:
case ECDH_ES_HKDF_256:
case ECDH_ES_HKDF_512:
case ECDH_SS_HKDF_256:
case ECDH_SS_HKDF_512:
return 1;
default:
return 9;
}
}
public byte[] getKey(AlgorithmID algCEK) throws CoseException {
byte[] rgbSecret;
SecureRandom random;
if (privateKey == null) throw new CoseException("Private key not set for recipient");
AlgorithmID alg = AlgorithmID.FromCBOR(findAttribute(HeaderKeys.Algorithm));
switch (alg) {
case Direct:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_Octet) throw new CoseException("Key and algorithm do not agree");
return privateKey.get(KeyKeys.Octet_K.AsCBOR()).GetByteString();
case AES_KW_128:
case AES_KW_192:
case AES_KW_256:
if (!privateKey.HasKeyType(KeyKeys.KeyType_Octet)) throw new CoseException("Key and algorithm do not agree");
return privateKey.get(KeyKeys.Octet_K).GetByteString();
case ECDH_ES_HKDF_256:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
ECDH_GenEphemeral();
rgbSecret = ECDH_GenSecret(privateKey);
return HKDF(rgbSecret, algCEK.getKeySize(), algCEK, "SHA256");
case ECDH_ES_HKDF_512:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
ECDH_GenEphemeral();
rgbSecret = ECDH_GenSecret(privateKey);
return HKDF(rgbSecret, algCEK.getKeySize(), algCEK, "SHA512");
case ECDH_SS_HKDF_256:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
if (findAttribute(HeaderKeys.HKDF_Context_PartyU_nonce.AsCBOR()) == null) {
byte[] rgbAPU = new byte[256/8];
random = new SecureRandom();
random.nextBytes(rgbAPU);
addAttribute(HeaderKeys.HKDF_Context_PartyU_nonce.AsCBOR(), CBORObject.FromObject(rgbAPU), Attribute.UNPROTECTED);
}
rgbSecret = ECDH_GenSecret(privateKey);
return HKDF(rgbSecret, algCEK.getKeySize(), algCEK, "SHA256");
case ECDH_SS_HKDF_512:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
if (findAttribute(HeaderKeys.HKDF_Context_PartyU_nonce.AsCBOR()) == null) {
byte[] rgbAPU = new byte[512/8];
random = new SecureRandom();
random.nextBytes(rgbAPU);
addAttribute(HeaderKeys.HKDF_Context_PartyU_nonce.AsCBOR(), CBORObject.FromObject(rgbAPU), Attribute.UNPROTECTED);
}
rgbSecret = ECDH_GenSecret(privateKey);
return HKDF(rgbSecret, algCEK.getKeySize(), algCEK, "SHA512");
case HKDF_HMAC_SHA_256:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_Octet) throw new CoseException("Needs to be an octet key");
return HKDF(privateKey.get(KeyKeys.Octet_K.AsCBOR()).GetByteString(), algCEK.getKeySize(), algCEK, "SHA256");
case HKDF_HMAC_SHA_512:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_Octet) throw new CoseException("Needs to be an octet key");
return HKDF(privateKey.get(KeyKeys.Octet_K.AsCBOR()).GetByteString(), algCEK.getKeySize(), algCEK, "SHA512");
default:
throw new CoseException("Recipient Algorithm not supported");
}
}
/**
* Set the key for encrypting/decrypting the recipient key.
*
* @param key private key for encrypting or decrypting
* @exception CoseException Internal COSE package error.
* @deprecated In COSE 0.9.1, use SetKey(OneKey)
*/
@Deprecated
public void SetKey(CBORObject key) throws CoseException {
privateKey = new OneKey(key);
}
/**
* Set the key for encrypting/decrypting the recipient key.
*
* @param key private key for encrypting or decrypting
*/
public void SetKey(OneKey key) {
privateKey = key;
}
@Deprecated
public void SetSenderKey(CBORObject key) throws CoseException {
senderKey = new OneKey(key);
}
public void SetSenderKey(OneKey key) {
senderKey = key;
}
private byte[] AES_KeyWrap_Encrypt(AlgorithmID alg, byte[] rgbKey) throws CoseException
{
if (rgbKey.length != alg.getKeySize() / 8) throw new CoseException("Key is not the correct size");
try {
Cipher cipher = Cipher.getInstance("AESWrap");
cipher.init(Cipher.WRAP_MODE, new SecretKeySpec(rgbKey, "AESWrap"));
return cipher.wrap(new SecretKeySpec(rgbContent, "AES"));
} catch (NoSuchAlgorithmException ex) {
throw new CoseException("Algorithm not supported", ex);
} catch (Exception ex) {
throw new CoseException("Key Wrap failure", ex);
}
}
private byte[] AES_KeyWrap_Decrypt(AlgorithmID alg, byte[] rgbKey) throws CoseException
{
if (rgbKey.length != alg.getKeySize() / 8) throw new CoseException("Key is not the correct size");
try {
Cipher cipher = Cipher.getInstance("AESWrap");
cipher.init(Cipher.UNWRAP_MODE, new SecretKeySpec(rgbKey, "AESWrap"));
return ((SecretKeySpec)cipher.unwrap(rgbEncrypted, "AES", Cipher.SECRET_KEY)).getEncoded();
} catch (NoSuchAlgorithmException ex) {
throw new CoseException("Algorithm not supported", ex);
}
catch (InvalidKeyException ex) {
if (ex.getMessage() == "Illegal key size") {
throw new CoseException("Unsupported key size", ex);
}
throw new CoseException("Decryption failure", ex);
} catch (Exception ex) {
throw new CoseException("Key Unwrap failure", ex);
}
}
private void ECDH_GenEphemeral() throws CoseException {
OneKey secretKey = OneKey.generateKey(privateKey.get(KeyKeys.EC2_Curve));
// pack into EPK header
CBORObject epk = secretKey.PublicKey().AsCBOR();
addAttribute(HeaderKeys.ECDH_EPK, epk, Attribute.UNPROTECTED);
// apply as senderKey
senderKey = secretKey;
}
private byte[] ECDH_GenSecret(OneKey key) throws CoseException {
OneKey epk;
if (senderKey != null) {
epk = key;
key = senderKey;
} else {
CBORObject cn = findAttribute(HeaderKeys.ECDH_SPK);
if (cn == null) {
cn = findAttribute(HeaderKeys.ECDH_EPK);
}
if (cn == null) {
throw new CoseException("No second party EC key");
}
epk = new OneKey(cn);
}
if (key.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) {
throw new CoseException("Not an EC2 Key");
}
if (epk.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) {
throw new CoseException("Not an EC2 Key");
}
if (epk.get(KeyKeys.EC2_Curve.AsCBOR()) != key.get(KeyKeys.EC2_Curve.AsCBOR())) {
throw new CoseException("Curves are not the same");
}
try {
PublicKey pubKey = epk.AsPublicKey();
PrivateKey privKey = key.AsPrivateKey();
KeyAgreement ecdh = KeyAgreement.getInstance("ECDH");
ecdh.init(privKey);
ecdh.doPhase(pubKey, true);
return ecdh.generateSecret();
} catch (NoSuchAlgorithmException ex) {
throw new CoseException("Algorithm not supported", ex);
} catch (Exception ex) {
throw new CoseException("Key agreement failure", ex);
}
}
private byte[] HKDF(byte[] secret, int cbitKey, AlgorithmID alg, String digest) throws CoseException {
final String HMAC_ALG_NAME = "Hmac" + digest;
byte[] rgbContext = GetKDFInput(cbitKey, alg);
try {
Mac hmac = Mac.getInstance(HMAC_ALG_NAME);
int hashLen = hmac.getMacLength();
CBORObject cnSalt = findAttribute(HeaderKeys.HKDF_Salt.AsCBOR());
byte[] K;
if (cnSalt == null) {
K = new byte[hashLen];
} else {
K = cnSalt.GetByteString();
}
// Perform extract
hmac.init(new SecretKeySpec(K, HMAC_ALG_NAME));
byte[] rgbExtract = hmac.doFinal(secret);
// Perform expand
hmac.init(new SecretKeySpec(rgbExtract, HMAC_ALG_NAME));
int c = ((cbitKey + 7)/8 + hashLen-1)/hashLen;
byte[] rgbOut = new byte[cbitKey / 8];
byte[] T = new byte[hashLen * c];
byte[] last = new byte[0];
for (int i = 0; i < c; i++) {
hmac.reset();
hmac.update(last);
hmac.update(rgbContext);
hmac.update((byte)(i + 1));
last = hmac.doFinal();
System.arraycopy(last, 0, T, i * hashLen, hashLen);
}
System.arraycopy(T, 0, rgbOut, 0, cbitKey / 8);
return rgbOut;
} catch (NoSuchAlgorithmException ex) {
throw new CoseException("Algorithm not supported", ex);
} catch (Exception ex) {
throw new CoseException("Derivation failure", ex);
}
}
private byte[] GetKDFInput(int cbitKey, AlgorithmID algorithmID) {
CBORObject obj;
CBORObject contextArray = CBORObject.NewArray();
// First element is - algorithm ID
contextArray.Add(algorithmID.AsCBOR());
// Second item is - Party U info
CBORObject info = CBORObject.NewArray();
contextArray.Add(info);
obj = findAttribute(HeaderKeys.HKDF_Context_PartyU_ID.AsCBOR());
if (obj != null) info.Add(obj);
else info.Add(null);
obj = findAttribute(HeaderKeys.HKDF_Context_PartyU_nonce.AsCBOR());
if (obj != null) info.Add(obj);
else info.Add(null);
obj = findAttribute(HeaderKeys.HKDF_Context_PartyU_Other.AsCBOR());
if (obj != null) info.Add(obj);
else info.Add(null);
// third element is - Party V info
info = CBORObject.NewArray();
contextArray.Add(info);
obj = findAttribute(HeaderKeys.HKDF_Context_PartyV_ID.AsCBOR());
if (obj != null) info.Add(obj);
else info.Add(null);
obj = findAttribute(HeaderKeys.HKDF_Context_PartyV_nonce.AsCBOR());
if (obj != null) info.Add(obj);
else info.Add(null);
obj = findAttribute(HeaderKeys.HKDF_Context_PartyV_Other.AsCBOR());
if (obj != null) info.Add(obj);
else info.Add(null);
// fourth element is - Supplimental Public Info
info = CBORObject.NewArray();
contextArray.Add(info);
info.Add(CBORObject.FromObject(cbitKey));
if (objProtected.size()== 0) info.Add(new byte[0]);
else info.Add(objProtected.EncodeToBytes());
obj = findAttribute(HeaderKeys.HKDF_SuppPub_Other.AsCBOR());
if (obj != null) info.Add(obj);
// Fifth element is - Supplimental Private Info
obj = findAttribute(HeaderKeys.HKDF_SuppPriv_Other.AsCBOR());
if (obj != null) contextArray.Add(obj);
return contextArray.EncodeToBytes();
}
}