You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
2.8 KiB
124 lines
2.8 KiB
3 years ago
|
package crypto
|
||
|
|
||
|
import (
|
||
|
"crypto/rand"
|
||
|
"io"
|
||
|
"math/big"
|
||
|
)
|
||
|
|
||
|
// This piece of software can be found in multiple projects, referenced as "splitPQ":
|
||
|
// * https://github.com/sdidyk/mtproto/blob/cf2cb57ade6932a7e0854f2e3246492a2028d369/math.go#L44
|
||
|
// * https://github.com/cjongseok/mtproto/blob/dab4e1a6538c990d8a4450d700559197c344a63b/crypto.go#L48
|
||
|
// * https://gist.github.com/asaelko/6f27c8ccd2edfab4d1efadf80a5221a4
|
||
|
// * https://github.com/xelaj/mtproto/blob/13ca2c1b5bfaa249ec925921760be0dc05735915/math.go#L39
|
||
|
//
|
||
|
// Also there is StackOverflow question about this part of telegram auth:
|
||
|
// * https://stackoverflow.com/questions/31953836/decompose-a-number-into-2-prime-co-factors
|
||
|
//
|
||
|
// General information can be found in telegram auth docs:
|
||
|
// * https://core.telegram.org/mtproto/auth_key#dh-exchange-initiation
|
||
|
// * https://core.telegram.org/mtproto/samples-auth_key#4-encrypted-data-generation
|
||
|
//
|
||
|
// Looks like this is some kind of integer factorization algorithm implementation
|
||
|
// which is used as Proof of Work by Telegram Server for some reason.
|
||
|
//
|
||
|
// TODO(ernado): Try https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm
|
||
|
|
||
|
// DecomposePQ decomposes pq into prime factors such that p < q.
|
||
|
func DecomposePQ(pq *big.Int, randSource io.Reader) (p, q *big.Int, err error) { // nolint:gocognit
|
||
|
var (
|
||
|
value0 = big.NewInt(0)
|
||
|
value1 = big.NewInt(1)
|
||
|
value15 = big.NewInt(15)
|
||
|
value17 = big.NewInt(17)
|
||
|
rndMax = big.NewInt(0).SetBit(big.NewInt(0), 64, 1)
|
||
|
|
||
|
y = big.NewInt(0)
|
||
|
whatNext = big.NewInt(0)
|
||
|
|
||
|
a = big.NewInt(0)
|
||
|
b = big.NewInt(0)
|
||
|
c = big.NewInt(0)
|
||
|
|
||
|
b2 = big.NewInt(0)
|
||
|
|
||
|
z = big.NewInt(0)
|
||
|
)
|
||
|
|
||
|
what := big.NewInt(0).Set(pq)
|
||
|
g := big.NewInt(0)
|
||
|
i := 0
|
||
|
for !(g.Cmp(value1) == 1 && g.Cmp(what) == -1) {
|
||
|
v, err := rand.Int(randSource, rndMax)
|
||
|
if err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
v = v.And(v, value15)
|
||
|
v = v.Add(v, value17)
|
||
|
v = v.Mod(v, what)
|
||
|
|
||
|
x, err := rand.Int(randSource, rndMax)
|
||
|
if err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
whatNext.Sub(what, value1)
|
||
|
x = x.Mod(x, whatNext)
|
||
|
x = x.Add(x, value1)
|
||
|
|
||
|
y.Set(x)
|
||
|
lim := 1 << (uint(i) + 18)
|
||
|
j := 1
|
||
|
flag := true
|
||
|
|
||
|
for j < lim && flag {
|
||
|
a.Set(x)
|
||
|
b.Set(x)
|
||
|
c.Set(v)
|
||
|
|
||
|
for b.Cmp(value0) == 1 {
|
||
|
b2.SetInt64(0)
|
||
|
if b2.And(b, value1).Cmp(value0) == 1 {
|
||
|
c.Add(c, a)
|
||
|
if c.Cmp(what) >= 0 {
|
||
|
c.Sub(c, what)
|
||
|
}
|
||
|
}
|
||
|
a.Add(a, a)
|
||
|
if a.Cmp(what) >= 0 {
|
||
|
a.Sub(a, what)
|
||
|
}
|
||
|
b.Rsh(b, 1)
|
||
|
}
|
||
|
x.Set(c)
|
||
|
|
||
|
z.SetInt64(0)
|
||
|
if x.Cmp(y) == -1 {
|
||
|
z.Add(what, x)
|
||
|
z.Sub(z, y)
|
||
|
} else {
|
||
|
z.Sub(x, y)
|
||
|
}
|
||
|
g.GCD(nil, nil, z, what)
|
||
|
|
||
|
if (j & (j - 1)) == 0 {
|
||
|
y.Set(x)
|
||
|
}
|
||
|
j++
|
||
|
|
||
|
if g.Cmp(value1) != 0 {
|
||
|
flag = false
|
||
|
}
|
||
|
}
|
||
|
i++
|
||
|
}
|
||
|
|
||
|
p = big.NewInt(0).Set(g)
|
||
|
q = big.NewInt(0).Div(what, g)
|
||
|
|
||
|
if p.Cmp(q) == 1 {
|
||
|
p, q = q, p
|
||
|
}
|
||
|
|
||
|
return p, q, nil
|
||
|
}
|