/************************************************************************
*
* Copyright (c) 2003-2004, C&C Research Laboratories, NEC Europe Ltd.
*
* Copyright in this library belongs to C&C Research Laboratories,
* Rathausallee 10, 53757 Sankt Augustin, Germany.
*
* This software may not be used, sold, licensed, transferred, copied
* or reproduced in whole or in part in any manner or form or in or
* on any media by any person other than in accordance with the terms
* of the Licence Agreement supplied with the software, or otherwise
* without the prior written consent of the copyright owners.
*
* This software is distributed WITHOUT ANY WARRANTY, without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE, except where stated in the Licence Agreement supplied with
* the software.
*
*  Created By :           Federico Crazzolara
*  Created for Project :  GEMSS (IST-2001-37153)
*
************************************************************************/


package de.nece.ccrl.tokenservice.sessions;

import org.xmlsoap.schemas.ws._2002._12.secext.*;
import javax.xml.namespace.*;
import de.nece.ccrl.sessionmanager.*;
import de.nece.ccrl.schemas.secmsg.*;
import de.nece.ccrl.schemas.sessid.SessionIdType;
import de.nece.ccrl.schemas.sessid.holders.SessionIdTypeHolder;
import de.nece.ccrl.securitycontext.NECSecurityContext;
import de.nece.ccrl.tokenservice.registry.*;
import de.nece.ccrl.securitycontext.token.types.key.KeyDescriptor;
import eu.gemss.components.security.token.types.key.symmetric.SecretKeyToken;
import de.nece.ccrl.securitycontext.token.types.contexttoken.SecurityContextTokenImpl;
import de.nece.ccrl.tokenmanager.*;
import de.nece.ccrl.tokenmanager.codec.*;
import eu.gemss.GridException;
import java.util.*;
import java.util.logging.*;

public class NSLSession extends Session{

    private static Logger logger = Logger.getLogger("logging.advancedSecurity");
    static final QName nonceencoding = new QName("int");

    //services must register with this tokenservice and fill in a registration file
    //with keystore passoword and location etc...

    public void run() {

	try{
	SessionPort requests = null, responses = null, servicename = null;
	RequestSecurityTokenType req = null;
	MessageType inmessage, outmessage;
	RequestSecurityTokenResponseType resp;
	QName tokentype;
	TokenTypeEnum tokentypeenum;
	String initiator = null, responder = null;
	EncodedString nonce1 = new EncodedString();
	EncodedString nonce2 = new EncodedString();
	
	nonce1.setEncodingType(nonceencoding);
	nonce2.setEncodingType(nonceencoding);

	SessionContext cxt = this.getContext();
	try {
	    requests = cxt.getSessionPort("in");
	    responses = cxt.getSessionPort("out");

	    SessionIdTypeHolder sidh = (SessionIdTypeHolder) cxt.getSessionId(); 
	    SessionIdType sid = sidh.getSessionId();
	    initiator = sid.getInitiator();
            responder = sid.getResponder(); //the service on behalf of which I act	
	    logger.info("Acting on Behalf of: " + responder);
	} catch (SessionContextException e) {}


	//gets first request
	try {
	    req = (RequestSecurityTokenType) requests.getValue();
	} catch (SessionContextException e) {}		
	inmessage = req.getMessage();

	
	if (check1(inmessage,initiator,nonce1)) {
		  
	    resp = new RequestSecurityTokenResponseType();

	    	    
	    outmessage = prepare(responder,nonce1,nonce2);
	    resp.setMessage(outmessage);

	    tokentype = new QName("wsse:SecurityContextToken");
	    tokentypeenum = TokenTypeEnum.fromValue(tokentype);
	    resp.setTokenType(tokentypeenum);
	
	    RequestedSecurityTokenType[] art = new RequestedSecurityTokenType[1];
	    RequestedSecurityTokenType rt =  new RequestedSecurityTokenType();
	    rt.setSecurityContextToken(prepareSharedKey(initiator, responder));
	    art[0] = rt;
	    resp.setRequestedSecurityToken(art);

            // produces first answer 
	    try{
		responses.setValue(resp);
	    } catch (SessionContextException e) {}


	    // gets second request 
	    try {
		req = (RequestSecurityTokenType) requests.getValue();
	    } catch (SessionContextException e) {}		
	    inmessage = req.getMessage();
	    
	    if (check2(inmessage,nonce2)) {
		// update keystore with security tokens and authentication token
	        // use the apropriate security context to generate the token and add it to the keystore
	    
		//dummy response to make the client work with rpc
		resp = new RequestSecurityTokenResponseType();
		try{
		    responses.setValue(resp);
		} catch (SessionContextException e) {}
		
	    } 
	}
	    
	try{
	    cxt.terminateContext(0);
	} catch (SessionContextException e) {}	
	
	}catch(Exception ex) {
	    logger.info("Unexpected exception occured in NSL session: " + ex.getMessage());
	    ex.printStackTrace();
	}
    } 	


    boolean check1(MessageType inmessage, String initiator, EncodedString nonce1){

	MessageList l = inmessage.getList();
	if (l==null) return false;
	else {
	    MessageType[] a = l.getMessage();
	    if (a.length != 2) return false;
	    else {
		nonce1.setValue((a[0].getNonce()).getValue()); 
		if (!(initiator.equals(a[1].getDN()))) return false; 
		// need to check that the initiator id is the same as the initiator in the message. Initiator id is
		// used for encryption of the outgoing message
		else if ((nonce1 == null) || (initiator == null)) return false;
		else if (!(nonceencoding.equals(nonce1.getEncodingType()))) return false;
	    }
	} 
	return true;
    }

    boolean check2(MessageType inmessage, EncodedString nonce2){
	EncodedString resp_nonce = inmessage.getNonce();
	return nonce2.equals(resp_nonce);
    }

    MessageType prepare(String responder, EncodedString nonce1, EncodedString nonce2){
	MessageType[] tuple = new MessageType[3];
	MessageType init_nonce = new MessageType();
	MessageType resp_nonce = new MessageType();
	MessageType respDN = new MessageType();
	
	init_nonce.setNonce(nonce1);
	tuple[0] = init_nonce;

        respDN.setDN(responder);
	tuple[2] = respDN;
	
	Random generator = new Random();
	nonce2.setValue(Integer.toString(generator.nextInt()));
	resp_nonce.setNonce(nonce2);
	tuple[1] = resp_nonce;
	
	MessageList l = new MessageList();
	l.setMessage(tuple);
	MessageType msg = new MessageType();
	msg.setList(l);
	return msg;
    }

    SecurityContextTokenType prepareSharedKey(String initiator, String responder) {
	
	// instantiate the security context of the service in question
		
	NECSecurityContext seccxt = null;
	
	RegistryImpl reg = new RegistryImpl();
	KeystoreAccessor a = reg.getAccessor(responder);
	try{
	    seccxt = new NECSecurityContext(".", a.getConfigfile());
	} catch (Exception e) {}

	// generate key 

	SecretKeyToken keytoken = null;
	KeyDescriptor desc = new KeyDescriptor(KeyDescriptor.SYM_KEY,KeyDescriptor.DES);
	try{
	    keytoken = (SecretKeyToken) seccxt.generateSecurityToken(desc);
	}catch(GridException e) {}

	

	// store key in keystore
	ArrayList owners = new ArrayList();
	// key is identified both by initiator and responder because
	// there can be a client (initiator) generating keys for more than one service
	// and if the keystore is in common there is conflict.
	owners.add(initiator); 
	owners.add(responder);
	keytoken.setOwners(owners);
	
	try{
	    seccxt.addSecurityTokenToContext(keytoken);
	}catch(GridException e) {}

	
	
 

	// prepare NSL context token containing key,initiaor and expiration date information
	// initiaor inforamtion is used to retrieve the token in the enc/decr processor NOT service
	// in this case!!! (maybe change token impl to include initiator explicitly?) 
	
	SecurityContextTokenImpl token = new SecurityContextTokenImpl(keytoken,"NSL",initiator,null,10000000);
	
	// store context token
	
	try{
	    seccxt.addSecurityTokenToContext(token);
	}catch(GridException e) {
	    
	}
	
	// produce the security context token to be returned 
	
	GEMSSTokenManagerCoDec codec = new GEMSSTokenManagerCoDec();

	SecurityContextTokenType t = null;
	try {
	    t = (SecurityContextTokenType) codec.encode(token);
	} catch (TokenManagerException e) {}

	return t;
    }

}
