/*
   Copyright (C) 2000-2007 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING. If not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   MA  02110-1301  USA.
*/


#include "runtime.hpp"
#include "dsa.hpp"
#include "sha.hpp"
#include "asn.hpp"
#include "modarith.hpp"


namespace TaoCrypt {


void DSA_PublicKey::Swap(DSA_PublicKey& other)
{
    p_.Swap(other.p_);
    q_.Swap(other.q_);
    g_.Swap(other.g_);
    y_.Swap(other.y_);
}


DSA_PublicKey::DSA_PublicKey(const DSA_PublicKey& other)
    : p_(other.p_), q_(other.q_), g_(other.g_), y_(other.y_)
{}


DSA_PublicKey& DSA_PublicKey::operator=(const DSA_PublicKey& that)
{
    DSA_PublicKey tmp(that);
    Swap(tmp);
    return *this;
}


DSA_PublicKey::DSA_PublicKey(Source& source)
{
    Initialize(source);
}


void DSA_PublicKey::Initialize(Source& source)
{
    DSA_Public_Decoder decoder(source);
    decoder.Decode(*this);
}


void DSA_PublicKey::Initialize(const Integer& p, const Integer& q,
                               const Integer& g, const Integer& y)
{
    p_ = p;
    q_ = q;
    g_ = g;
    y_ = y;
}
   

const Integer& DSA_PublicKey::GetModulus() const
{
    return p_;
}

const Integer& DSA_PublicKey::GetSubGroupOrder() const
{
    return q_;
}


const Integer& DSA_PublicKey::GetSubGroupGenerator() const
{
    return g_;
}


const Integer& DSA_PublicKey::GetPublicPart() const
{
    return y_;
}


void DSA_PublicKey::SetModulus(const Integer& p)
{
    p_ = p;
}


void DSA_PublicKey::SetSubGroupOrder(const Integer& q)
{
    q_ = q;
}


void DSA_PublicKey::SetSubGroupGenerator(const Integer& g)
{
    g_ = g;
}


void DSA_PublicKey::SetPublicPart(const Integer& y)
{
    y_ = y;
}


word32 DSA_PublicKey::SignatureLength() const
{
    return GetSubGroupOrder().ByteCount() * 2;  // r and s
}



DSA_PrivateKey::DSA_PrivateKey(Source& source)
{
    Initialize(source);
}


void DSA_PrivateKey::Initialize(Source& source)
{
    DSA_Private_Decoder decoder(source);
    decoder.Decode(*this);
}


void DSA_PrivateKey::Initialize(const Integer& p, const Integer& q,
                                const Integer& g, const Integer& y,
                                const Integer& x)
{
    DSA_PublicKey::Initialize(p, q, g, y);
    x_ = x;
}


const Integer& DSA_PrivateKey::GetPrivatePart() const
{
    return x_;
}


void DSA_PrivateKey::SetPrivatePart(const Integer& x)
{
    x_ = x;
}


DSA_Signer::DSA_Signer(const DSA_PrivateKey& key)
    : key_(key)
{}


word32 DSA_Signer::Sign(const byte* sha_digest, byte* sig,
                        RandomNumberGenerator& rng)
{
    const Integer& p = key_.GetModulus();
    const Integer& q = key_.GetSubGroupOrder();
    const Integer& g = key_.GetSubGroupGenerator();
    const Integer& x = key_.GetPrivatePart();

    Integer k(rng, 1, q - 1);

    r_ =  a_exp_b_mod_c(g, k, p);
    r_ %= q;

    Integer H(sha_digest, SHA::DIGEST_SIZE);  // sha Hash(m)

    Integer kInv = k.InverseMod(q);
    s_ = (kInv * (H + x*r_)) % q;

    assert(!!r_ && !!s_);

    int rSz = r_.ByteCount();

    if (rSz == 19) {
        sig[0] = 0;
        sig++;
    }
    
    r_.Encode(sig,  rSz);

    int sSz = s_.ByteCount();

    if (sSz == 19) {
        sig[rSz] = 0;
        sig++;
    }

    s_.Encode(sig + rSz, sSz);

    return 40;
}


DSA_Verifier::DSA_Verifier(const DSA_PublicKey& key)
    : key_(key)
{}


bool DSA_Verifier::Verify(const byte* sha_digest, const byte* sig)
{
    const Integer& p = key_.GetModulus();
    const Integer& q = key_.GetSubGroupOrder();
    const Integer& g = key_.GetSubGroupGenerator();
    const Integer& y = key_.GetPublicPart();

    int sz = q.ByteCount();

    r_.Decode(sig, sz);
    s_.Decode(sig + sz, sz);

    if (r_ >= q || r_ < 1 || s_ >= q || s_ < 1)
        return false;

    Integer H(sha_digest, SHA::DIGEST_SIZE);  // sha Hash(m)

    Integer w = s_.InverseMod(q);
    Integer u1 = (H  * w) % q;
    Integer u2 = (r_ * w) % q;

    // verify r == ((g^u1 * y^u2) mod p) mod q
    ModularArithmetic ma(p);
    Integer v = ma.CascadeExponentiate(g, u1, y, u2);
    v %= q;

    return r_ == v;
}




const Integer& DSA_Signer::GetR() const
{
    return r_;
}


const Integer& DSA_Signer::GetS() const
{
    return s_;
}


const Integer& DSA_Verifier::GetR() const
{
    return r_;
}


const Integer& DSA_Verifier::GetS() const
{
    return s_;
}


} // namespace
