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.
123 lines
2.8 KiB
123 lines
2.8 KiB
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 |
|
}
|
|
|