2018-10-07 23:07:43 +00:00
package dhparam
import (
2020-05-04 15:59:29 +00:00
"context"
2018-10-07 23:07:43 +00:00
"crypto/rand"
"math/big"
"github.com/pkg/errors"
)
const pemHeader = "DH PARAMETERS"
// GeneratorResult is a type of results sent to the GeneratorCallback function
type GeneratorResult uint
const (
2018-10-07 23:13:08 +00:00
// GeneratorFoundPossiblePrime signals a possible (non-verified) prime number was found (OpenSSL: ".")
2018-10-07 23:07:43 +00:00
GeneratorFoundPossiblePrime GeneratorResult = iota
2018-10-07 23:13:08 +00:00
// GeneratorFirstConfirmation signals the prime number itself was verified but is not yet considered "safe" (OpenSSL: "+")
2018-10-07 23:07:43 +00:00
GeneratorFirstConfirmation
2018-10-07 23:13:08 +00:00
// GeneratorSafePrimeFound signals the prime number now is considered "safe" (OpenSSL: "*")
2018-10-07 23:07:43 +00:00
GeneratorSafePrimeFound
)
2018-10-08 09:33:53 +00:00
// 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
)
2018-10-07 23:07:43 +00:00
// GeneratorCallback is a type of function to receive GeneratorResults while the prime number is determined
type GeneratorCallback func ( r GeneratorResult )
func nullCallback ( r GeneratorResult ) { }
2018-10-07 23:13:08 +00:00
// Generate determines a prime number according to the generator having the specified number of bits
2018-10-07 23:16:44 +00:00
//
2018-10-08 09:33:53 +00:00
// 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.
2018-10-07 23:16:44 +00:00
// 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.
2018-10-08 09:33:53 +00:00
func Generate ( bits int , generator Generator , cb GeneratorCallback ) ( * DH , error ) {
2020-05-04 15:59:29 +00:00
// 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 ) {
2018-10-07 23:07:43 +00:00
var (
err error
padd , rem int64
prime * big . Int
)
if cb == nil {
cb = nullCallback
}
switch generator {
case 2 :
padd , rem = 24 , 11
case 5 :
padd , rem = 10 , 3
default :
padd , rem = 2 , 1
}
for {
2020-05-04 15:59:29 +00:00
select {
case <- ctx . Done ( ) :
return nil , ctx . Err ( )
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
}
2018-10-07 23:07:43 +00:00
}
}
}
func genPrime ( bits int , padd , rem * big . Int ) ( * big . Int , error ) {
var (
err error
p = new ( big . Int )
qadd = new ( big . Int )
q = new ( 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
}
func genRand ( bits int ) ( * big . Int , error ) {
bytes := ( bits + 7 ) / 8
bit := ( bits - 1 ) % 8
mask := 0xff << uint ( bit + 1 )
buf := make ( [ ] byte , bytes )
if _ , err := rand . Read ( buf ) ; err != nil {
2018-10-07 23:13:08 +00:00
return nil , errors . Wrap ( err , "Unable to read random" )
2018-10-07 23:07:43 +00:00
}
if bit == 0 {
buf [ 0 ] = 1
buf [ 1 ] |= 0x80
} else {
buf [ 0 ] |= ( 3 << uint ( bit - 1 ) )
}
buf [ 0 ] &= byte ( ^ mask )
buf [ bytes - 1 ] |= 1
r := new ( big . Int )
return r . SetBytes ( buf ) , nil
}