/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
/* jshint maxlen: 666  */
/* global forge */
/* global CL */

module.exports = (function() {

    'use strict';

    // validate server challenge 
    // returns: client challenge if ok

    var validateServerChallenge = function(responseData, data, credentials) {

        // verify response

        var serverChallengeMessage = responseData.serverChallengeMessage;
        if (!serverChallengeMessage.serverChallenge || !serverChallengeMessage.timestamp || !serverChallengeMessage.signature) {
            throw new Error('Bad server challenge');
        }

        // verify server challenge signature
        var dataToVerify = forge.util.encode64(data.challengeValue + data.timestamp + data.electionEventId + data.credentialId);
        var certificates = responseData.certificates;
        var publicKey = CL.certificates.service.load(certificates['authenticationTokenSignerCert']).getPublicKey();
        var verified = false;
        try {
            if (CL.asymmetric.service.verifySignature(serverChallengeMessage.signature, publicKey, [dataToVerify])) {
                verified = true;
            }
        } catch (e) {
            // ignore
        }
        if (!verified) {
            throw new Error('Challenge verification has failed');
        }

        // generate client challenge

        var prng = new CL.primitives.securerandom.factory.SecureRandomFactory().getCryptoRandomInteger();
        var clientChallenge = forge.util.encode64(prng.nextRandom(16).toString());

        // sign client challenge

        var dataToSign = forge.util.encode64(serverChallengeMessage.signature + clientChallenge);
        var privateKey = credentials.authPrivateKey;
        var clientChallengeSignature = null;


        // on ms edge this sig fails randomly for reasons yet
        // unknown. as a workaround we verify it and retry a couple of times if
        // it fails

        var MAX_RETRIES = 3;
        var retries = 0;
        var clientVerified = false;
        var session = require('../client/session.js');
        var clientPubKey = CL.certificates.service.load(session('credentials').certificateAuth).getPublicKey();
        do {
            try {
                clientChallengeSignature = CL.asymmetric.service.sign(privateKey, [dataToSign]);
                clientVerified = CL.asymmetric.service.verifySignature(clientChallengeSignature, clientPubKey, [dataToSign]);
            } catch (e) {
                // ignore
            }
        } while (!clientVerified && ++retries < MAX_RETRIES);

        return {
            clientChallenge: clientChallenge,
            signature: clientChallengeSignature
        };

    };

    return validateServerChallenge;

})();
