mirror of
https://github.com/Luzifer/go-dhparam.git
synced 2024-11-08 15:20:03 +00:00
173 lines
3.9 KiB
Go
173 lines
3.9 KiB
Go
package dhparam
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"fmt"
|
|
"math/big"
|
|
)
|
|
|
|
const pemHeader = "DH PARAMETERS"
|
|
|
|
// GeneratorResult is a type of results sent to the GeneratorCallback function
|
|
type GeneratorResult uint
|
|
|
|
const (
|
|
// GeneratorFoundPossiblePrime signals a possible (non-verified) prime number was found (OpenSSL: ".")
|
|
GeneratorFoundPossiblePrime GeneratorResult = iota
|
|
// GeneratorFirstConfirmation signals the prime number itself was verified but is not yet considered "safe" (OpenSSL: "+")
|
|
GeneratorFirstConfirmation
|
|
// GeneratorSafePrimeFound signals the prime number now is considered "safe" (OpenSSL: "*")
|
|
GeneratorSafePrimeFound
|
|
)
|
|
|
|
// Generator is the generator number to use when determining the prime number
|
|
type Generator int
|
|
|
|
const (
|
|
// GeneratorTwo uses a generator 2
|
|
GeneratorTwo Generator = 2
|
|
// GeneratorFive uses a generator 5
|
|
GeneratorFive = 5
|
|
)
|
|
|
|
// GeneratorCallback is a type of function to receive GeneratorResults while the prime number is determined
|
|
type GeneratorCallback func(r GeneratorResult)
|
|
|
|
func nullCallback(_ GeneratorResult) {}
|
|
|
|
// Generate determines a prime number according to the generator having the specified number of bits
|
|
//
|
|
// In OpenSSL defined generators are 2 and 5. Others are supported but the verification is not supported in an extend as with generators 2 and 5.
|
|
// The bit size should be adjusted to be high enough for the current requirements. Also, you should keep
|
|
// in mind the higher the bitsize, the longer the generation might take.
|
|
func Generate(bits int, generator Generator, cb GeneratorCallback) (*DH, error) {
|
|
// Invoke GenerateWithContext with a background context
|
|
return GenerateWithContext(context.Background(), bits, generator, cb)
|
|
}
|
|
|
|
// GenerateWithContext is just like the Generate function, but it accepts a ctx parameter with a context, that can be used to interrupt the generation if needed
|
|
func GenerateWithContext(ctx context.Context, bits int, generator Generator, cb GeneratorCallback) (*DH, error) {
|
|
var (
|
|
err error
|
|
padd, rem int64
|
|
prime *big.Int
|
|
)
|
|
|
|
if cb == nil {
|
|
cb = nullCallback
|
|
}
|
|
|
|
switch generator {
|
|
case 2: //nolint:mnd
|
|
padd, rem = 24, 11
|
|
case 5: //nolint:mnd
|
|
padd, rem = 10, 3
|
|
default:
|
|
padd, rem = 2, 1
|
|
}
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err() //nolint:wrapcheck // Fine in this case
|
|
default:
|
|
if prime, err = genPrime(bits, big.NewInt(padd), big.NewInt(rem)); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if prime.BitLen() > bits {
|
|
continue
|
|
}
|
|
|
|
t := new(big.Int)
|
|
t.Rsh(prime, 1)
|
|
|
|
cb(GeneratorFoundPossiblePrime)
|
|
|
|
if prime.ProbablyPrime(0) {
|
|
cb(GeneratorFirstConfirmation)
|
|
} else {
|
|
continue
|
|
}
|
|
|
|
if t.ProbablyPrime(0) {
|
|
cb(GeneratorSafePrimeFound)
|
|
return &DH{
|
|
P: prime,
|
|
G: int(generator),
|
|
}, nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func genPrime(bits int, padd, rem *big.Int) (*big.Int, error) {
|
|
var (
|
|
err error
|
|
p = new(big.Int)
|
|
qadd = new(big.Int)
|
|
q *big.Int
|
|
t1 = new(big.Int)
|
|
)
|
|
|
|
bits--
|
|
|
|
qadd.Rsh(padd, 1)
|
|
|
|
if q, err = genRand(bits); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
t1.Mod(q, qadd)
|
|
q.Sub(q, t1)
|
|
|
|
t1.Rsh(rem, 1)
|
|
q.Add(q, t1)
|
|
|
|
p.Lsh(q, 1)
|
|
p.Add(p, big.NewInt(1))
|
|
|
|
for !mightBePrime(p) || !mightBePrime(q) {
|
|
p.Add(p, padd)
|
|
q.Add(q, qadd)
|
|
}
|
|
|
|
return p, nil
|
|
}
|
|
|
|
func mightBePrime(i *big.Int) bool {
|
|
m := new(big.Int)
|
|
for _, p := range quickTestPrimes {
|
|
if m.Mod(i, big.NewInt(p)).Int64() == 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
//nolint:mnd
|
|
func genRand(bits int) (*big.Int, error) {
|
|
bytes := (bits + 7) / 8
|
|
bit := (bits - 1) % 8
|
|
mask := 0xff << uint(bit+1) //#nosec:G115 // Should only ever run with positive ints
|
|
|
|
buf := make([]byte, bytes)
|
|
if _, err := rand.Read(buf); err != nil {
|
|
return nil, fmt.Errorf("unable to read random: %w", err)
|
|
}
|
|
|
|
if bit == 0 {
|
|
buf[0] = 1
|
|
buf[1] |= 0x80
|
|
} else {
|
|
buf[0] |= 3 << uint(bit-1) //#nosec:G115 // Should only ever run with positive ints
|
|
}
|
|
|
|
buf[0] &= byte(^mask)
|
|
|
|
buf[bytes-1] |= 1
|
|
|
|
r := new(big.Int)
|
|
return r.SetBytes(buf), nil
|
|
}
|