package keys

/*
Keys: holding private and public keys
*/

import (
    "fmt"
    "tools/common"
    "tools/rsa/integer"
)

type private struct {
    p, q, b, d *integer.RSAInt
}

type public struct {
    N, E *integer.RSAInt
}

type Keys struct {
    *private
    *public
    base_init float64
    base,
    exp int64
}

func New(base_init float64, base, exp int64) (*Keys) {
    k := new(Keys)
    k.private = new(private)
    k.public = new(public)
    k.base_init, k.base, k.exp = base_init, base, exp

    return k
}

func (k *Keys) Private() *private {
    return k.private
}

func (k *Keys) Public() *public {
    return k.public
}

func (k *Keys) GenPrimes() {
    for k.Private().init(k); !k.Public().init(k); k.Private().init(k) {}
    k.Private().d.ModMulInv(k.Public().E, k.Private().b)
}

func (k *Keys) Crack() {
    _, k.Private().p, k.Private().q = k.Public().N.PollardPm1(2)
    fmt.Printf("p:%s\tq:%s\n", k.Private().p, k.Private().q)
    k.Private().b = k.Private().p.Copy().Mul(k.Private().q).EulerTot(k.Private().p, k.Private().q)
    k.Private().d.ModMulInv(k.Public().E, k.Public().N)
}

func (p *private) init(k *Keys) {
    p.p = integer.New(
        int64(k.base_init * float64(k.base),
    )).Mul(
        integer.New(k.base).Pow64(
            (k.exp / 2) - int64(common.IntLen(k.base)),
        ),
    )
    p.q = p.p.Copy()
    p.genPQ()
    p.b = p.p.Copy().Mul(p.q).EulerTot(p.p, p.q)
    p.d = integer.New(0)
}

func (p *private) genPQ() {
    p.p.NextRandPrime()
    p.q.NextRandPrime()
    if p.p.Eq(p.q) {
        p.genPQ()
    }
}

func (p *public) init(k *Keys) bool {
    p.N = k.Private().p.Copy().Mul(k.Private().q)
    p.E = k.Private().b.Copy().RelativePrime()
    return p.testN(k) && p.E != nil
}

func (p *public) testN(k *Keys) bool {
    s := fmt.Sprintf("%d", int64(k.base_init * float64(k.base)))
    for i := int(0); i < int(k.exp) - common.IntLen(k.base); i++ {
        s += "0"
    }

    if t, e := integer.New(0).SetString(s); e {
        return p.N.Gt(t)
    }

    return false
}

func (k *Keys) SetKeys(d, e, n string) bool {
    k.Private().d = integer.New(0)
    k.Public().E = integer.New(0)
    k.Public().N = integer.New(0)
    _, d_success := k.Private().d.SetString(d)
    _, e_success := k.Public().E.SetString(e)
    _, n_success := k.Public().N.SetString(n)
    return d_success && e_success && n_success
}

func (p *private) D() *integer.RSAInt {
    return p.d
}

func (k *Keys) StrKeys() string {
    return fmt.Sprintf(
        "PRIVATE:\n\tp:%s\n\tq:%s\n\tb:%s\n\td:%s\nPUBLIC:\n\tn:%s\n\te:%s",
        k.Private().p,
        k.Private().q,
        k.Private().b,
        k.Private().d,
        k.Public().N,
        k.Public().E,
    )
}
