EndpointUtils.java
/*******************************************************************************
* Copyright (c) 2019, 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.as;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.upokecenter.cbor.CBORObject;
import org.eclipse.californium.cose.AlgorithmID;
import org.eclipse.californium.cose.Attribute;
import org.eclipse.californium.cose.CoseException;
import org.eclipse.californium.cose.HeaderKeys;
import org.eclipse.californium.cose.KeyKeys;
import org.eclipse.californium.cose.MessageTag;
import org.eclipse.californium.cose.OneKey;
import org.eclipse.californium.cose.Recipient;
import se.sics.ace.AceException;
import se.sics.ace.COSEparams;
import se.sics.ace.cwt.CwtCryptoCtx;
/**
* Utility methods for the token and introspect endpoints.
*
* @author Ludwig Seitz
*
*/
public class EndpointUtils {
/**
* Create a common CWT crypto context for the given audience.
*
* @param aud the audiences
* @param db the database connector
* @param asymKey the asymmetric key of the AS if Sign1 is
* to be used, null otherwise
* @param verify true if the context is needed for verifying, false if
* it is for signing.
*
* @return a common crypto context or null if there isn't any
*
* @throws CoseException
* @throws AceException
*/
public static CwtCryptoCtx makeCommonCtx(Set<String> aud, DBConnector db,
OneKey asymKey, boolean verify) throws AceException, CoseException {
COSEparams cose = db.getSupportedCoseParams(aud);
if (cose == null) {
return null;
}
MessageTag tag = cose.getTag();
switch (tag) {
case Encrypt:
AlgorithmID ealg = cose.getAlg();
return CwtCryptoCtx.encrypt(makeRecipients(aud, cose, db),
ealg.AsCBOR());
case Encrypt0:
byte[] ekey = getCommonSecretKey(aud, db);
if (ekey == null) {
return null;
}
return CwtCryptoCtx.encrypt0(ekey, cose.getAlg().AsCBOR());
case MAC:
return CwtCryptoCtx.mac(makeRecipients(aud, cose, db),
cose.getAlg().AsCBOR());
case MAC0:
byte[] mkey = getCommonSecretKey(aud, db);
if (mkey == null) {
return null;
}
return CwtCryptoCtx.mac0(mkey, cose.getAlg().AsCBOR());
case Sign:
// Access tokens with multiple signers not supported
return null;
case Sign1:
if (verify) {
return CwtCryptoCtx.sign1Verify(asymKey.PublicKey(),
cose.getAlg().AsCBOR());
}
return CwtCryptoCtx.sign1Create(
asymKey, cose.getAlg().AsCBOR());
default:
throw new IllegalArgumentException("Unknown COSE message type");
}
}
/**
* Tries to find a common PSK for the given audience.
*
* @param aud the audience
* @param db the database connector
*
* @return a common PSK or null if there isn't any
* @throws AceException
*/
private static byte[] getCommonSecretKey(Set<String> aud, DBConnector db)
throws AceException {
Set<String> rss = new HashSet<>();
for (String audE : aud) {
rss.addAll(db.getRSS(audE));
}
byte[] key = null;
for (String rs : rss) {
OneKey cose = db.getRsTokenPSK(rs);
if (cose == null) {
return null;
}
byte[] secKey = cose.get(KeyKeys.Octet_K).GetByteString();
if (key == null) {
key = Arrays.copyOf(secKey, secKey.length);
} else {
if (!Arrays.equals(key, secKey)) {
return null;
}
}
}
return key;
}
/**
* Create a recipient list for an audience.
*
* @param aud the audience
* @param cose the COSE parameters
* @param db the database connector
*
* @return the recipient list
* @throws AceException
* @throws CoseException
*/
private static List<Recipient> makeRecipients(Set<String> aud, COSEparams cose,
DBConnector db) throws AceException, CoseException {
List<Recipient> rl = new ArrayList<>();
for (String audE : aud) {
for (String rs : db.getRSS(audE)) {
Recipient r = new Recipient();
r.addAttribute(HeaderKeys.Algorithm,
cose.getKeyWrap().AsCBOR(),
Attribute.UNPROTECTED);
CBORObject key = CBORObject.NewMap();
key.Add(KeyKeys.KeyType.AsCBOR(), KeyKeys.KeyType_Octet);
key.Add(KeyKeys.Octet_K.AsCBOR(), CBORObject.FromObject(
db.getRsTokenPSK(rs)));
OneKey coseKey = new OneKey(key);
r.SetKey(coseKey);
rl.add(r);
}
}
return rl;
}
}