/************************************************************************
*
* 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.tokenmanager.manager;

import org.xmlsoap.schemas.ws._2002._12.secext.*;
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.tokenmanager.*;
import de.nece.ccrl.tokenmanager.policies.*;
import javax.xml.namespace.*;
import java.util.Random;

public class NSLTokenManager implements TokenManager {

    TokenManagerDriver driver;
    TokenManagerCoDec codec;
    static final PubEncrPolicy outpolicy = new PubEncrPolicy("X.509");
    static final PrivDecrPolicy inpolicy = new PrivDecrPolicy("RSA");
    static final QName nonceencoding = new QName("int");


    public void setDriver(TokenManagerDriver driver) {
	this.driver = driver;
    }

    public void setCoDec(TokenManagerCoDec codec) {
	this.codec = codec;
    }

    public void setSupportTokens(Object[] support) {
    }


    public Object execute(String initiator, String responder) throws TokenManagerException {

	if (driver == null) throw new TokenManagerException("No driver ist set");
	if (codec == null) throw new TokenManagerException("No codec is set");

	RequestSecurityTokenType request;
	RequestSecurityTokenResponseType response;
	RequestedSecurityTokenType[] contexttoken;
	MessageType outmessage;
	EncodedString resp_nonce;
	EncodedString init_nonce = new EncodedString();
	init_nonce.setEncodingType(nonceencoding);



	// sets the responder DN for the NSL policy (it is only the CN in reality!)
	outpolicy.setOwner(responder); 


	TokenManagerPolicy[] in_pol_list = new TokenManagerPolicy[1];
	in_pol_list[0] = inpolicy;
	TokenManagerPolicy[] out_pol_list = new TokenManagerPolicy[1];
	out_pol_list[0] = outpolicy;


	// setup the sessionID

	SessionIdType sid = new SessionIdType();
	sid.setInitiator(initiator);
	sid.setResponder(responder);
	SessionIdTypeHolder sidh = new SessionIdTypeHolder(sid);


	// prepare first output message

	request = new RequestSecurityTokenType();

	outmessage = prepare_1(initiator,init_nonce);
	request.setMessage(outmessage);

	QName requesttype = new QName("wsse:ReqIssue");
	RequestTypeEnum requesttypeen = RequestTypeEnum.fromValue(requesttype);
        request.setRequestType(requesttypeen);

	QName tokentype = new QName("wsse:SecurityContextToken");
	TokenTypeEnum tokentypeen = TokenTypeEnum.fromValue(tokentype);
        request.setTokenType(tokentypeen);


	// first rpc call. Throws an exception if invocation encounters problems
	// on the server side.
	
	response = driver.invoke(sidh, request, out_pol_list, in_pol_list);
       
	// first check. Throws an exception if the check does not succeed.

	resp_nonce = check(response,init_nonce,responder);
	
	// Extract Security context token to be returned
		
	contexttoken = response.getRequestedSecurityToken();

	// prepare second output message

	outmessage = prepare_2(resp_nonce);
	request.setMessage(outmessage);

	// second rpc call.

	response = driver.invoke(sidh, request, out_pol_list, in_pol_list);
	
	// no second check is needed according to NSL

  
	// returns the security context token if one was generated
	if (contexttoken != null) {	    
	    return codec.decode(contexttoken[0].getSecurityContextToken());
	}
	else return null;

    }

    
    MessageType prepare_1(String initiator, EncodedString nonce) {
	MessageType[] tuple = new MessageType[2];
	MessageType init_N = new MessageType();
	MessageType initDN = new MessageType();

	Random generator = new Random();
	nonce.setValue(Integer.toString(generator.nextInt()));
	init_N.setNonce(nonce);
	tuple[0] = init_N;

        initDN.setDN(initiator);     	
	tuple[1] = initDN;
	
	MessageList l = new MessageList();
	l.setMessage(tuple);
	MessageType msg = new MessageType();
	msg.setList(l);
	return msg;
    }

    EncodedString check(RequestSecurityTokenResponseType response, EncodedString nonce, String responder) throws TokenManagerException {
	MessageType inmessage = response.getMessage();
	MessageList l = inmessage.getList();
	EncodedString resp_nonce = new EncodedString();
	resp_nonce.setEncodingType(nonceencoding);

	if (l!=null) {
	    MessageType[] a = l.getMessage();
	    if (   (a.length == 3) 
		&& (nonce.equals(a[0].getNonce()))
		&& (responder.equals(a[2].getDN()))
		&& (a[1].getNonce() != null)
		&& (nonceencoding.equals((a[1].getNonce()).getEncodingType())) 
		   ) {
		resp_nonce.setValue((a[1].getNonce()).getValue());
		System.out.println("Token service response is correct");
		return resp_nonce;
	    }
	}	throw new TokenManagerException("wrong NSL response");

    }

    MessageType prepare_2(EncodedString nonce) {
	MessageType resp_N = new MessageType();
	resp_N.setNonce(nonce);	
      	return resp_N;
    }

}
