cleanup some xkeypairs functions
This commit is contained in:
parent
a8f1a99667
commit
9de2f796db
|
@ -11,35 +11,17 @@ import (
|
|||
"math/rand"
|
||||
mathrand "math/rand"
|
||||
"net/http"
|
||||
|
||||
"git.coolaj86.com/coolaj86/go-mockid/xkeypairs"
|
||||
)
|
||||
|
||||
type Object = map[string]interface{}
|
||||
|
||||
// options are the things that we may need to know about a request to fulfill it properly
|
||||
type options struct {
|
||||
Key string `json:"key"`
|
||||
KeyType string `json:"kty"`
|
||||
Seed int64 `json:"-"`
|
||||
SeedStr string `json:"seed"`
|
||||
Claims Object `json:"claims"`
|
||||
Header Object `json:"header"`
|
||||
}
|
||||
|
||||
// this shananigans is only for testing and debug API stuff
|
||||
func (o *options) nextReader() io.Reader {
|
||||
if 0 == o.Seed {
|
||||
return RandomReader
|
||||
}
|
||||
return rand.New(rand.NewSource(o.Seed))
|
||||
}
|
||||
|
||||
/*
|
||||
func getJWS(r *http.Request) (*options, error) {
|
||||
func getJWS(r *http.Request) (*xkeypairs.KeyOptions, error) {
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
func getOpts(r *http.Request) (*options, error) {
|
||||
func getOpts(r *http.Request) (*xkeypairs.KeyOptions, error) {
|
||||
tok := make(map[string]interface{})
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&tok)
|
||||
|
@ -60,17 +42,17 @@ func getOpts(r *http.Request) (*options, error) {
|
|||
}
|
||||
|
||||
key, _ := tok["key"].(string)
|
||||
opts := &options{
|
||||
opts := &xkeypairs.KeyOptions{
|
||||
Seed: seed,
|
||||
Key: key,
|
||||
}
|
||||
|
||||
opts.Claims, _ = tok["claims"].(Object)
|
||||
opts.Header, _ = tok["header"].(Object)
|
||||
opts.Claims, _ = tok["claims"].(xkeypairs.Object)
|
||||
opts.Header, _ = tok["header"].(xkeypairs.Object)
|
||||
|
||||
var n int
|
||||
if 0 != seed {
|
||||
n = opts.nextReader().(*mathrand.Rand).Intn(2)
|
||||
n = opts.MyFooNextReader().(*mathrand.Rand).Intn(2)
|
||||
} else {
|
||||
n = rand.Intn(2)
|
||||
}
|
||||
|
|
|
@ -1,21 +1,12 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"git.coolaj86.com/coolaj86/go-mockid/xkeypairs"
|
||||
"git.rootprojects.org/root/keypairs"
|
||||
)
|
||||
|
||||
// RandomReader may be overwritten for testing
|
||||
var RandomReader io.Reader = rand.Reader
|
||||
|
||||
// GeneratePublicJWK will create a new private key in JWK format
|
||||
func GeneratePublicJWK(w http.ResponseWriter, r *http.Request) {
|
||||
if "POST" != r.Method {
|
||||
|
@ -52,7 +43,7 @@ func GeneratePrivateJWK(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
privkey := genPrivKey(opts)
|
||||
privkey := xkeypairs.GenPrivKey(opts)
|
||||
|
||||
jwk := xkeypairs.MarshalJWKPrivateKey(privkey)
|
||||
w.Write(append(jwk, '\n'))
|
||||
|
@ -95,7 +86,7 @@ func GeneratePrivateDER(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
privkey := genPrivKey(opts)
|
||||
privkey := xkeypairs.GenPrivKey(opts)
|
||||
|
||||
der, _ := xkeypairs.MarshalDERPrivateKey(privkey)
|
||||
w.Write(der)
|
||||
|
@ -138,7 +129,7 @@ func GeneratePrivatePEM(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
privkey := genPrivKey(opts)
|
||||
privkey := xkeypairs.GenPrivKey(opts)
|
||||
|
||||
privpem, _ := xkeypairs.MarshalPEMPrivateKey(privkey)
|
||||
w.Write(privpem)
|
||||
|
@ -146,39 +137,9 @@ func GeneratePrivatePEM(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
const maxRetry = 16
|
||||
|
||||
func getPrivKey(opts *options) (keypairs.PrivateKey, error) {
|
||||
func getPrivKey(opts *xkeypairs.KeyOptions) (keypairs.PrivateKey, error) {
|
||||
if "" != opts.Key {
|
||||
return keypairs.ParsePrivateKey([]byte(opts.Key))
|
||||
}
|
||||
return genPrivKey(opts), nil
|
||||
}
|
||||
|
||||
func genPrivKey(opts *options) keypairs.PrivateKey {
|
||||
var privkey keypairs.PrivateKey
|
||||
|
||||
if "RSA" == opts.KeyType {
|
||||
keylen := 2048
|
||||
privkey, _ = rsa.GenerateKey(opts.nextReader(), keylen)
|
||||
if 0 != opts.Seed {
|
||||
for i := 0; i < maxRetry; i++ {
|
||||
otherkey, _ := rsa.GenerateKey(opts.nextReader(), keylen)
|
||||
otherCmp := otherkey.D.Cmp(privkey.(*rsa.PrivateKey).D)
|
||||
if 0 != otherCmp {
|
||||
// There are two possible keys, choose the lesser D value
|
||||
// See https://github.com/square/go-jose/issues/189
|
||||
if otherCmp < 0 {
|
||||
privkey = otherkey
|
||||
}
|
||||
break
|
||||
}
|
||||
if maxRetry == i-1 {
|
||||
log.Printf("error: coinflip landed on heads %d times", maxRetry)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: EC keys may also suffer the same random problems in the future
|
||||
privkey, _ = ecdsa.GenerateKey(elliptic.P256(), opts.nextReader())
|
||||
}
|
||||
return privkey
|
||||
return xkeypairs.GenPrivKey(opts), nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package xkeypairs
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
|
||||
"git.rootprojects.org/root/keypairs"
|
||||
)
|
||||
|
||||
// KeyOptions are the things that we may need to know about a request to fulfill it properly
|
||||
type KeyOptions struct {
|
||||
Key string `json:"key"`
|
||||
KeyType string `json:"kty"`
|
||||
Seed int64 `json:"-"`
|
||||
SeedStr string `json:"seed"`
|
||||
Claims Object `json:"claims"`
|
||||
Header Object `json:"header"`
|
||||
}
|
||||
|
||||
// this shananigans is only for testing and debug API stuff
|
||||
func (o *KeyOptions) MyFooNextReader() io.Reader {
|
||||
if 0 == o.Seed {
|
||||
return RandomReader
|
||||
}
|
||||
return rand.New(rand.NewSource(o.Seed))
|
||||
}
|
||||
|
||||
// GenPrivKey generates a 256-bit entropy RSA or ECDSA private key
|
||||
func GenPrivKey(opts *KeyOptions) keypairs.PrivateKey {
|
||||
var privkey keypairs.PrivateKey
|
||||
|
||||
if "RSA" == opts.KeyType {
|
||||
keylen := 2048
|
||||
privkey, _ = rsa.GenerateKey(opts.MyFooNextReader(), keylen)
|
||||
if 0 != opts.Seed {
|
||||
for i := 0; i < maxRetry; i++ {
|
||||
otherkey, _ := rsa.GenerateKey(opts.MyFooNextReader(), keylen)
|
||||
otherCmp := otherkey.D.Cmp(privkey.(*rsa.PrivateKey).D)
|
||||
if 0 != otherCmp {
|
||||
// There are two possible keys, choose the lesser D value
|
||||
// See https://github.com/square/go-jose/issues/189
|
||||
if otherCmp < 0 {
|
||||
privkey = otherkey
|
||||
}
|
||||
break
|
||||
}
|
||||
if maxRetry == i-1 {
|
||||
log.Printf("error: coinflip landed on heads %d times", maxRetry)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: EC keys may also suffer the same random problems in the future
|
||||
privkey, _ = ecdsa.GenerateKey(elliptic.P256(), opts.MyFooNextReader())
|
||||
}
|
||||
return privkey
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package xkeypairs
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"io/ioutil"
|
||||
|
||||
"git.rootprojects.org/root/keypairs"
|
||||
)
|
||||
|
@ -12,37 +12,11 @@ func ParsePEMPrivateKey(block []byte) (keypairs.PrivateKey, error) {
|
|||
return keypairs.ParsePrivateKey(block)
|
||||
}
|
||||
|
||||
func ParseDuration(exp string) (int, error) {
|
||||
if "" == exp {
|
||||
exp = "15m"
|
||||
}
|
||||
|
||||
mult := 1
|
||||
switch exp[len(exp)-1] {
|
||||
case 'w':
|
||||
mult *= 7
|
||||
fallthrough
|
||||
case 'd':
|
||||
mult *= 24
|
||||
fallthrough
|
||||
case 'h':
|
||||
mult *= 60
|
||||
fallthrough
|
||||
case 'm':
|
||||
mult *= 60
|
||||
fallthrough
|
||||
case 's':
|
||||
// no fallthrough
|
||||
default:
|
||||
// could be 'k' or 'z', but we assume its empty
|
||||
exp += "s"
|
||||
}
|
||||
|
||||
// 15m => num=15, mult=1*60
|
||||
num, err := strconv.Atoi(exp[:len(exp)-1])
|
||||
// ParsePrivateKeyFile returns the private key from the given file path, if available
|
||||
func ParsePrivateKeyFile(pathname string) (keypairs.PrivateKey, error) {
|
||||
block, err := ioutil.ReadFile(pathname)
|
||||
if nil != err {
|
||||
return 0, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return num * mult, nil
|
||||
return keypairs.ParsePrivateKey(block)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,10 @@ import (
|
|||
"git.rootprojects.org/root/keypairs"
|
||||
)
|
||||
|
||||
var RandomReader = rand.Reader
|
||||
// RandomReader may be overwritten for testing
|
||||
var RandomReader io.Reader = rand.Reader
|
||||
|
||||
//var RandomReader = rand.Reader
|
||||
|
||||
type JWS struct {
|
||||
Header Object `json:"header"` // JSON
|
||||
|
@ -29,6 +32,7 @@ type JWS struct {
|
|||
|
||||
type Object = map[string]interface{}
|
||||
|
||||
// SignClaims adds `typ`, `kid` (or `jwk`), and `alg` in the header and expects claims for `jti`, `exp`, `iss`, and `iat`
|
||||
func SignClaims(privkey keypairs.PrivateKey, header Object, claims Object) (*JWS, error) {
|
||||
var randsrc io.Reader = RandomReader
|
||||
seed, _ := header["_seed"].(int64)
|
||||
|
@ -119,11 +123,12 @@ func claimsToPayload(claims Object) ([]byte, error) {
|
|||
|
||||
// parse if exp is actually a duration, such as "15m"
|
||||
if 0 == exp && "" != dur {
|
||||
s, err := ParseDuration(dur)
|
||||
s, err := time.ParseDuration(dur)
|
||||
// TODO s, err := time.ParseDuration(dur)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
exp = time.Now().Add(time.Duration(s) * time.Second).Unix()
|
||||
exp = time.Now().Add(s * time.Second).Unix()
|
||||
claims["exp"] = exp
|
||||
}
|
||||
if "" == jti && 0 == exp && !insecure {
|
||||
|
@ -155,8 +160,6 @@ func Sign(rand io.Reader, privkey keypairs.PrivateKey, hash []byte) []byte {
|
|||
case *ecdsa.PrivateKey:
|
||||
r, s, _ := ecdsa.Sign(rand, k, hash[:])
|
||||
rb := r.Bytes()
|
||||
fmt.Println("debug:")
|
||||
fmt.Println(r, s)
|
||||
for len(rb) < 32 {
|
||||
rb = append([]byte{0}, rb...)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue