GroupcommParameters.java
/*******************************************************************************
* Copyright (c) 2025, RISE AB
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
package se.sics.ace;
import java.util.HashSet;
import java.util.Set;
import com.upokecenter.cbor.CBORObject;
import com.upokecenter.cbor.CBORType;
import org.eclipse.californium.cose.AlgorithmID;
import org.eclipse.californium.cose.KeyKeys;
/**
* Constants for use with ACE Groupcomm
*
* @author Marco Tiloca
*
*/
public class GroupcommParameters {
/**
* Group OSCORE abbreviations =================================
*/
/*
* The prefixes used at the AS to internally represent scopes as text strings
*/
// This is relevant only for an admin scope entry,
// where Toid is the wildcard group name pattern
public static final String GROUP_OSCORE_AS_SCOPE_WILDCARD_PREFIX = "oscgm0";
// This is relevant for an admin or user scope entry,
// where Toid is a literal group name pattern (i.e., a specific group name)
public static final String GROUP_OSCORE_AS_SCOPE_LITERAL_PREFIX = "oscgm1";
// This is relevant only for an admin scope entry,
// where Toid is a complex group name pattern (e.g., a regular expression)
public static final String GROUP_OSCORE_AS_SCOPE_COMPLEX_PREFIX = "oscgm2";
/**
* The OSCORE group uses only the group mode
*/
public static final short GROUP_OSCORE_GROUP_MODE_ONLY = 1;
/**
* The OSCORE group uses both the group mode and the pairwise mode
*/
public static final short GROUP_OSCORE_GROUP_PAIRWISE_MODE = 2;
/**
* The OSCORE group uses only the pairwise mode
*/
public static final short GROUP_OSCORE_PAIRWISE_MODE_ONLY = 3;
/**
* Requester role
*/
public static final short GROUP_OSCORE_REQUESTER = 1;
/**
* Responder role
*/
public static final short GROUP_OSCORE_RESPONDER = 2;
/**
* Monitor role
*/
public static final short GROUP_OSCORE_MONITOR = 3;
/**
* Verifier role
*/
public static final short GROUP_OSCORE_VERIFIER = 4;
/**
* Roles as strings
*/
public static final String[] GROUP_OSCORE_ROLES = {"reserved", "requester", "responder", "monitor", "verifier"};
/**
* Return a set of integers including the valid Group OSCORE role combinations
*
* @return the set of valid Group OSCORE combinations
*/
public static Set<Integer> getValidGroupOSCORERoleCombinations() {
Set<Integer> validRoleCombinations = new HashSet<Integer>();
// Set the valid combinations of roles in a Joining Request
// Combinations are expressed with the AIF specific data model AIF-OSCORE-GROUPCOMM
validRoleCombinations.add(1 << GROUP_OSCORE_REQUESTER); // Requester (2)
validRoleCombinations.add(1 << GROUP_OSCORE_RESPONDER); // Responder (4)
validRoleCombinations.add((1 << GROUP_OSCORE_REQUESTER) +
(1 << GROUP_OSCORE_RESPONDER)); // Requester+Responder (6)
validRoleCombinations.add(1 << GROUP_OSCORE_MONITOR); // Monitor (8)
return validRoleCombinations;
}
/**
* Admin permission "List"
*/
public static final short GROUP_OSCORE_ADMIN_LIST = 0;
/**
* Admin permission "Create"
*/
public static final short GROUP_OSCORE_ADMIN_CREATE = 1;
/**
* Admin permission "Read"
*/
public static final short GROUP_OSCORE_ADMIN_READ = 2;
/**
* Admin permission "Write"
*/
public static final short GROUP_OSCORE_ADMIN_WRITE = 3;
/**
* Admin permission "Delete"
*/
public static final short GROUP_OSCORE_ADMIN_DELETE = 4;
/**
* Admin permissions as strings
*/
public static final String[] GROUP_OSCORE_ADMIN_PERMISSIONS = {"list", "create", "read", "write", "delete"};
/**
* Value for the application profile "coap_group_oscore_app"
*/
public static final short COAP_GROUP_OSCORE_APP = 1; // provisional
/**
* Value for the group key type "Group_OSCORE_Input_Material object"
*/
public static final short GROUP_OSCORE_INPUT_MATERIAL_OBJECT = 1; // provisional
/**
* CBOR abbreviations for the CoAP Content-Format application/ace-groupcomm+cbor =================================
*/
public static final CBORObject GID = CBORObject.FromObject(0);
public static final CBORObject GNAME = CBORObject.FromObject(1);
public static final CBORObject GURI = CBORObject.FromObject(2);
public static final CBORObject SCOPE = CBORObject.FromObject(3);
public static final CBORObject GET_CREDS = CBORObject.FromObject(4);
public static final CBORObject CLIENT_CRED = CBORObject.FromObject(5);
public static final CBORObject CNONCE = CBORObject.FromObject(6);
public static final CBORObject GKTY = CBORObject.FromObject(7);
public static final CBORObject KEY = CBORObject.FromObject(8);
public static final CBORObject NUM = CBORObject.FromObject(9);
public static final CBORObject ACE_GROUPCOMM_PROFILE = CBORObject.FromObject(10);
public static final CBORObject EXP = CBORObject.FromObject(11);
public static final CBORObject EXI = CBORObject.FromObject(12);
public static final CBORObject CREDS = CBORObject.FromObject(13);
public static final CBORObject PEER_ROLES = CBORObject.FromObject(14);
public static final CBORObject PEER_IDENTIFIERS = CBORObject.FromObject(15);
public static final CBORObject GROUP_POLICIES = CBORObject.FromObject(16);
public static final CBORObject KDC_CRED = CBORObject.FromObject(17);
public static final CBORObject KDC_NONCE = CBORObject.FromObject(18);
public static final CBORObject KDC_CRED_VERIFY = CBORObject.FromObject(19);
public static final CBORObject REKEYING_SCHEME = CBORObject.FromObject(20);
public static final CBORObject CLIENT_CRED_VERIFY = CBORObject.FromObject(24);
public static final CBORObject CREDS_REPO = CBORObject.FromObject(25);
public static final CBORObject CONTROL_URI = CBORObject.FromObject(26);
public static final CBORObject MGT_KEY_MATERIAL = CBORObject.FromObject(27);
public static final CBORObject CONTROL_GROUP_URI = CBORObject.FromObject(28);
public static final CBORObject SIGN_INFO = CBORObject.FromObject(29);
public static final CBORObject KDCCHALLENGE = CBORObject.FromObject(30);
// Defined in draft-ietf-ace-key-groupcomm-oscore
public static final CBORObject GROUP_SENDER_ID = CBORObject.FromObject(21); // provisional
public static final CBORObject ECDH_INFO = CBORObject.FromObject(31); // provisional
public static final CBORObject KDC_DH_CREDS = CBORObject.FromObject(32); // provisional
public static final CBORObject SIGN_ENC_KEY = CBORObject.FromObject(33); // provisional
public static final CBORObject STALE_NODE_IDS = CBORObject.FromObject(34); // provisional
// Defined in draft-ietf-ace-oscore-gm-admin
public static final CBORObject HKDF = CBORObject.FromObject(-1); // provisional
public static final CBORObject CRED_FMT = CBORObject.FromObject(-2); // provisional
public static final CBORObject GROUP_MODE = CBORObject.FromObject(-3); // provisional
public static final CBORObject GP_ENC_ALG = CBORObject.FromObject(-4); // provisional
public static final CBORObject SIGN_ALG = CBORObject.FromObject(-5); // provisional
public static final CBORObject SIGN_PARAMS = CBORObject.FromObject(-6); // provisional
public static final CBORObject PAIRWISE_MODE = CBORObject.FromObject(-7); // provisional
public static final CBORObject ALG = CBORObject.FromObject(-8); // provisional
public static final CBORObject ECDH_ALG = CBORObject.FromObject(-9); // provisional
public static final CBORObject ECDH_PARAMS = CBORObject.FromObject(-10); // provisional
public static final CBORObject RT = CBORObject.FromObject(-11); // provisional
public static final CBORObject ACTIVE = CBORObject.FromObject(-12); // provisional
public static final CBORObject GROUP_NAME = CBORObject.FromObject(-13); // provisional
public static final CBORObject GROUP_DESCRIPTION = CBORObject.FromObject(-14); // provisional
public static final CBORObject MAX_STALE_SETS = CBORObject.FromObject(-15); // provisional
public static final CBORObject GID_REUSE = CBORObject.FromObject(-16); // provisional
public static final CBORObject APP_GROUPS = CBORObject.FromObject(-17); // provisional
public static final CBORObject JOINING_URI = CBORObject.FromObject(-18); // provisional
public static final CBORObject AS_URI = CBORObject.FromObject(-19); // provisional
public static final CBORObject DET_REQ = CBORObject.FromObject(-25); // provisional
public static final CBORObject DET_HASH_ALG = CBORObject.FromObject(-26); // provisional
public static final CBORObject CONF_FILTER = CBORObject.FromObject(-27); // provisional
public static final CBORObject APP_GROUPS_DIFF = CBORObject.FromObject(-28); // provisional
/**
* Check whether the specified parameter is meaningful, i.e., it is of the expected CBOR type
* and it has the right value if it is of CBOR type simple value.
*
* @param name the CBOR abbreviation of the parameter to be checked
* @param value the value of the parameter to be checked
* @return true if the parameter is meaningful, or false if not meaningful or not recognized
*/
public static boolean isAdminRequestParameterMeaningful(CBORObject name, CBORObject value) {
CBORType type = value.getType();
if (name.equals(HKDF)) {
CBORType expectedType1 = CBORType.TextString;
CBORType expectedType2 = CBORType.Integer;
if ((type == expectedType1) || (type == expectedType2)) return true;
else return false;
}
if (name.equals(CRED_FMT)) {
CBORType expectedType = CBORType.Integer;
if (type == expectedType) return true;
else return false;
}
if (name.equals(GROUP_MODE)) {
CBORObject expectedValue1 = CBORObject.True;
CBORObject expectedValue2 = CBORObject.False;
if (value.equals(expectedValue1) || value.equals(expectedValue2)) return true;
else return false;
}
if (name.equals(GP_ENC_ALG)) {
CBORType expectedType1 = CBORType.TextString;
CBORType expectedType2 = CBORType.Integer;
CBORObject expectedValue = CBORObject.Null;
if ((type == expectedType1) || (type == expectedType2) || value.equals(expectedValue)) return true;
else return false;
}
if (name.equals(SIGN_ALG)) {
CBORType expectedType1 = CBORType.TextString;
CBORType expectedType2 = CBORType.Integer;
CBORObject expectedValue = CBORObject.Null;
if ((type == expectedType1) || (type == expectedType2) || value.equals(expectedValue)) return true;
else return false;
}
if (name.equals(SIGN_PARAMS)) {
CBORType expectedType = CBORType.Array;
CBORObject expectedValue = CBORObject.Null;
if ((type == expectedType) || value.equals(expectedValue)) return true;
else return false;
}
if (name.equals(PAIRWISE_MODE)) {
CBORObject expectedValue1 = CBORObject.True;
CBORObject expectedValue2 = CBORObject.False;
if (value.equals(expectedValue1) || value.equals(expectedValue2)) return true;
else return false;
}
if (name.equals(ALG)) {
CBORType expectedType1 = CBORType.TextString;
CBORType expectedType2 = CBORType.Integer;
CBORObject expectedValue = CBORObject.Null;
if ((type == expectedType1) || (type == expectedType2) || value.equals(expectedValue)) return true;
else return false;
}
if (name.equals(ECDH_ALG)) {
CBORType expectedType1 = CBORType.TextString;
CBORType expectedType2 = CBORType.Integer;
CBORObject expectedValue = CBORObject.Null;
if ((type == expectedType1) || (type == expectedType2) || value.equals(expectedValue)) return true;
else return false;
}
if (name.equals(ECDH_PARAMS)) {
CBORType expectedType = CBORType.Array;
CBORObject expectedValue = CBORObject.Null;
if ((type == expectedType) || value.equals(expectedValue)) return true;
else return false;
}
if (name.equals(DET_REQ)) {
CBORObject expectedValue1 = CBORObject.True;
CBORObject expectedValue2 = CBORObject.False;
if (value.equals(expectedValue1) || value.equals(expectedValue2)) return true;
else return false;
}
if (name.equals(DET_HASH_ALG)) {
CBORType expectedType1 = CBORType.TextString;
CBORType expectedType2 = CBORType.Integer;
if ((type == expectedType1) || (type == expectedType2)) return true;
else return false;
}
if (name.equals(ACTIVE)) {
CBORObject expectedValue1 = CBORObject.True;
CBORObject expectedValue2 = CBORObject.False;
if (value.equals(expectedValue1) || value.equals(expectedValue2)) return true;
else return false;
}
if (name.equals(GROUP_NAME)) {
CBORType expectedType = CBORType.TextString;
if (type == expectedType) return true;
else return false;
}
if (name.equals(GROUP_DESCRIPTION)) {
CBORType expectedType = CBORType.TextString;
CBORObject expectedValue = CBORObject.Null;
if ((type == expectedType) || value.equals(expectedValue)) return true;
else return false;
}
if (name.equals(MAX_STALE_SETS)) {
CBORType expectedType = CBORType.Integer;
if ((type == expectedType) && (value.AsInt32() > 1)) return true;
else return false;
}
if (name.equals(EXP)) {
CBORType expectedType = CBORType.Integer;
if ((type == expectedType) && (value.AsInt32() > 0) && (value.AsInt64Value() > (System.currentTimeMillis() / 1000L))) return true;
else return false;
}
if (name.equals(GROUP_POLICIES)) {
CBORType expectedType = CBORType.Map;
if (type == expectedType) return true;
else return false;
}
if (name.equals(GID_REUSE)) {
CBORObject expectedValue1 = CBORObject.True;
CBORObject expectedValue2 = CBORObject.False;
if (value.equals(expectedValue1) || value.equals(expectedValue2)) return true;
else return false;
}
if (name.equals(APP_GROUPS)) {
CBORType expectedType = CBORType.Array;
if (type == expectedType) return true;
else return false;
}
if (name.equals(AS_URI)) {
CBORType expectedType = CBORType.TextString;
if (type == expectedType) return true;
else return false;
}
if (name.equals(CONF_FILTER)) {
CBORType expectedType = CBORType.Array;
if (type == expectedType) return true;
else return false;
}
if (name.equals(APP_GROUPS_DIFF)) {
CBORType expectedType = CBORType.Array;
if (type == expectedType) return true;
else return false;
}
// Unrecognized parameter
return false;
}
/**
* Check whether the specified parameter value is meaningful.
*
* This takes into account admitted values altogether (e.g,. per the relevant COSE registries),
* as well as the local support for algorithms and other functionalities at the Group Manager.
*
* Note that the parameters have been checked already for being of the correct CBOR type.
* The parameters of type CBOR simple value has also already been checked as to their value.
*
* @param name the CBOR abbreviation of the parameter to be checked
* @param name the value of the parameter to be checked
* @return true if the parameter value is meaningful, or false if not meaningful or not recognized
*/
public static boolean isAdminParameterValueMeaningful(CBORObject name, CBORObject value) {
if (name.equals(HKDF)) {
if (value.getType().equals(CBORType.Integer)) {
if (value.equals(AlgorithmID.HMAC_SHA_256.AsCBOR()) ||
value.equals(AlgorithmID.HMAC_SHA_512.AsCBOR())) {
return true;
}
}
return false;
}
if (name.equals(CRED_FMT)) {
if (value.equals(CBORObject.FromObject(Constants.COSE_HEADER_PARAM_X5BAG)) ||
value.equals(CBORObject.FromObject(Constants.COSE_HEADER_PARAM_X5CHAIN)) ||
value.equals(CBORObject.FromObject(Constants.COSE_HEADER_PARAM_KCWT)) ||
value.equals(CBORObject.FromObject(Constants.COSE_HEADER_PARAM_KCCS)) ||
value.equals(CBORObject.FromObject(Constants.COSE_HEADER_PARAM_C5B)) ||
value.equals(CBORObject.FromObject(Constants.COSE_HEADER_PARAM_C5C))) {
return true;
}
return false;
}
if (name.equals(GP_ENC_ALG)) {
if (value.getType().equals(CBORType.Integer)) {
if (value.equals(AlgorithmID.AES_CCM_16_64_128.AsCBOR()) ||
value.equals(AlgorithmID.AES_CCM_16_64_256.AsCBOR()) ||
value.equals(AlgorithmID.AES_CCM_64_64_128.AsCBOR()) ||
value.equals(AlgorithmID.AES_CCM_64_64_256.AsCBOR()) ||
value.equals(AlgorithmID.AES_CCM_16_128_128.AsCBOR()) ||
value.equals(AlgorithmID.AES_CCM_16_128_256.AsCBOR()) ||
value.equals(AlgorithmID.AES_CCM_64_128_128.AsCBOR()) ||
value.equals(AlgorithmID.AES_CCM_64_128_256.AsCBOR())) {
return true;
}
}
return false;
}
if (name.equals(SIGN_ALG)) {
if (value.getType().equals(CBORType.Integer)) {
if (value.equals(AlgorithmID.EDDSA.AsCBOR()) ||
value.equals(AlgorithmID.ECDSA_256.AsCBOR()) ||
value.equals(AlgorithmID.ECDSA_384.AsCBOR()) ||
value.equals(AlgorithmID.ECDSA_512.AsCBOR())) {
return true;
}
// This Group Manager does not support RSA as signature algorithm
}
return false;
}
if (name.equals(SIGN_PARAMS)) {
if (value.size() != 2)
return false;
if ((value.get(0).getType() != CBORType.Array) || (value.get(1).getType() != CBORType.Array))
return false;
if ((value.get(0).size() != 1) || (value.get(1).size() != 2))
return false;
CBORObject keyType = value.get(0).get(0);
if (value.get(1).get(0).equals(keyType) == false)
return false;
CBORObject curve = value.get(1).get(1);
if (keyType.equals(KeyKeys.KeyType_OKP)) {
if (curve.equals(KeyKeys.OKP_Ed25519) || curve.equals(KeyKeys.OKP_Ed448)) {
return true;
}
}
if (keyType.equals(KeyKeys.KeyType_EC2)) {
if (curve.equals(KeyKeys.EC2_P256) || curve.equals(KeyKeys.EC2_P384) || curve.equals(KeyKeys.EC2_P521)) {
return true;
}
}
// This Group Manager does not support RSA as signature algorithm
return false;
}
if (name.equals(ALG)) {
if (value.getType().equals(CBORType.Integer)) {
if (value.equals(AlgorithmID.AES_CCM_16_64_128.AsCBOR()) ||
value.equals(AlgorithmID.AES_CCM_16_64_256.AsCBOR()) ||
value.equals(AlgorithmID.AES_CCM_64_64_128.AsCBOR()) ||
value.equals(AlgorithmID.AES_CCM_64_64_256.AsCBOR()) ||
value.equals(AlgorithmID.AES_CCM_16_128_128.AsCBOR()) ||
value.equals(AlgorithmID.AES_CCM_16_128_256.AsCBOR()) ||
value.equals(AlgorithmID.AES_CCM_64_128_128.AsCBOR()) ||
value.equals(AlgorithmID.AES_CCM_64_128_256.AsCBOR())) {
return true;
}
}
return false;
}
if (name.equals(ECDH_ALG)) {
if (value.getType().equals(CBORType.Integer)) {
if (value.equals(AlgorithmID.ECDH_SS_HKDF_256.AsCBOR()) ||
value.equals(AlgorithmID.ECDH_SS_HKDF_512.AsCBOR())) {
return true;
}
}
return false;
}
if (name.equals(ECDH_PARAMS)) {
if (value.size() != 2)
return false;
if ((value.get(0).getType() != CBORType.Array) || (value.get(1).getType() != CBORType.Array))
return false;
if ((value.get(0).size() != 1) || (value.get(1).size() != 2))
return false;
CBORObject keyType = value.get(0).get(0);
if (value.get(1).get(0).equals(keyType) == false)
return false;
CBORObject curve = value.get(1).get(1);
if (keyType.equals(KeyKeys.KeyType_OKP)) {
if (curve.equals(KeyKeys.OKP_X25519) || curve.equals(KeyKeys.OKP_X448)) {
return true;
}
}
if (keyType.equals(KeyKeys.KeyType_EC2)) {
if (curve.equals(KeyKeys.EC2_P256) || curve.equals(KeyKeys.EC2_P384) || curve.equals(KeyKeys.EC2_P521)) {
return true;
}
}
// This Group Manager does not support RSA as signature algorithm
return false;
}
if (name.equals(DET_HASH_ALG)) {
if (value.getType().equals(CBORType.Integer)) {
int hashAlg = value.AsInt32();
if (hashAlg == -16 || hashAlg == -43 || hashAlg == -44) { // SHA-256, SHA-384, SHA-512
return true;
}
}
return false;
}
if (name.equals(GROUP_POLICIES)) {
Set<Integer> admittedPolicies = new HashSet<>();
admittedPolicies.add(GroupcommPolicies.KEY_CHECK_INTERVAL.AsInt32());
admittedPolicies.add(GroupcommPolicies.EXP_DELTA.AsInt32());
for (CBORObject policyName : value.getKeys()) {
if (!admittedPolicies.contains(policyName.AsInt32()))
return false;
if (policyName.equals(GroupcommPolicies.KEY_CHECK_INTERVAL)) {
if (value.get(policyName).getType() != CBORType.Integer ||
value.get(policyName).AsInt32() < 0)
return false;
}
if (policyName.equals(GroupcommPolicies.EXP_DELTA)) {
if (value.get(policyName).getType() != CBORType.Integer ||
value.get(policyName).AsInt32() < 0)
return false;
}
}
return true;
}
if (name.equals(APP_GROUPS)) {
for (int i = 0; i < value.size(); i++) {
if (value.get(i).getType() != CBORType.TextString)
return false;
}
return true;
}
return true;
}
}