ReferenceToken.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;

import java.security.SecureRandom;
import java.util.Base64;
import java.util.Map;

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

import se.sics.ace.rs.IntrospectionException;
import se.sics.ace.rs.IntrospectionHandler;

/**
 * This class implements a reference token.
 * 
 * @author Ludwig Seitz
 *
 */
public class ReferenceToken implements AccessToken {

	/**
	 * The reference 
	 */
	private byte[] ref;
	
	/**
	 * The reference as Base64 encoded string
	 */
	private String refS;
	
	/**
	 * A handler for introspecting this token.
	 */
	private IntrospectionHandler introspect;
	
	/**
	 * Constructor.
	 * 
	 * @param length  the length in bits of the reference.
	 */
	public ReferenceToken(int length) {		
		this.ref = new byte[length];
		new SecureRandom().nextBytes(this.ref);
		this.refS = Base64.getEncoder().encodeToString(this.ref);
	}
	
	/**
	 * Constructor. Uses the default
	 * length of 128 bits for the reference.
	 */
	public ReferenceToken() {
	    this.ref = new byte[128/8];
	    new SecureRandom().nextBytes(this.ref);
	    this.refS = Base64.getEncoder().encodeToString(this.ref);
	}
	
	/**
	 * Constructor. Uses a given cti as reference.
	 * 
	 * @param ref  the reference
	 */
	public ReferenceToken(byte[] ref) {
	    this.ref = ref;
        this.refS = Base64.getEncoder().encodeToString(this.ref); 
	}
	
	
	/**
	 * Add an introspection handler to this ReferenceToken in order to do 
	 * introspection.
	 * 
	 * @param intropsect
	 */
	public void addIntrospectionHandler(IntrospectionHandler intropsect) {
		this.introspect = intropsect;
	}
	
	@Override
	public boolean expired(long now) throws AceException {
		if (this.introspect == null) {
			throw new AceException("Need IntrospectionHandler");
		}
		Map<Short, CBORObject> params = null;
        try {
            params = this.introspect.getParams(this.ref);
        } catch (IntrospectionException e) {
            throw new AceException("Introspection failed: "
                    + e.getMessage());
        }
		if (params == null) {
		    throw new AceException("Token reference not found: " + this.refS);
		}
		CBORObject expO = params.get(Constants.EXP);
		if (expO != null && expO.AsNumber().ToInt64Checked() < now) {
			//Token has expired
			return true;
		}
		return false;		
	}

	@Override
	public boolean isValid(long now) throws AceException {
		if (this.introspect == null) {
			throw new AceException("Need IntrospectionHandler");
		}
		Map<Short, CBORObject> params = null;
        try {
            params = this.introspect.getParams(this.ref);
        } catch (IntrospectionException e) {
            throw new AceException("Introspection failed: "
                    + e.getMessage());
        }
		if (params == null) {
		    throw new AceException("Token reference not found: " + this.refS);
		}
		//Check nbf and exp for the found match
		CBORObject nbfO = params.get(Constants.NBF);
		if (nbfO != null &&  nbfO.AsNumber().ToInt64Checked()	> now) {
		    return false;
		}	
		CBORObject expO = params.get(Constants.EXP);
		if (expO != null && expO.AsNumber().ToInt64Checked() < now) {
		    //Token has expired
		    return false;
		}
		return false;
	}

	@Override
	public CBORObject encode() {
		return CBORObject.FromObject(this.ref);
	}

	/**
	 * Parse a reference token from a CBOR object (must be a ByteString).
	 * @param ob
	 * @return  the reference token or null of the object didn't contain
	 *          a valid String
	 */
	public static ReferenceToken parse(CBORObject ob) {
	   if (ob.getType().equals(CBORType.ByteString)) {
	       return new ReferenceToken(ob.GetByteString());
	   } 
	   return null;
	}

    @Override
    public String getCti() throws AceException {
        return this.refS;
    }
}