truly deterministic RSA keys
This commit is contained in:
		
							parent
							
								
									bc7e9740d8
								
							
						
					
					
						commit
						d80c8226b5
					
				| @ -9,6 +9,7 @@ import ( | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"math/rand" | ||||
| 	mathrand "math/rand" | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| @ -17,11 +18,25 @@ type options struct { | ||||
| 	KeyType string `json:"kty"` | ||||
| 	Seed    int64  `json:"-"` | ||||
| 	SeedStr string `json:"seed"` | ||||
| 	rndReader io.Reader `json:"-"` | ||||
| } | ||||
| 
 | ||||
| // this shananigans is only for testing and debug API stuff | ||||
| func (o *options) nextRand() *mathrand.Rand { | ||||
| 	if 0 == o.Seed { | ||||
| 		panic(errors.New("called nextRand impossibly")) | ||||
| 		return nil | ||||
| 	} | ||||
| 	return rand.New(rand.NewSource(o.Seed)) | ||||
| } | ||||
| 
 | ||||
| func (o *options) nextReader() io.Reader { | ||||
| 	if 0 == o.Seed { | ||||
| 		return RandomReader | ||||
| 	} | ||||
| 	return rand.New(rand.NewSource(o.Seed)) | ||||
| } | ||||
| 
 | ||||
| func getOpts(r *http.Request) (*options, error) { | ||||
| 	rndReader := RandomReader | ||||
| 	tok := make(map[string]interface{}) | ||||
| 	decoder := json.NewDecoder(r.Body) | ||||
| 	err := decoder.Decode(&tok) | ||||
| @ -41,27 +56,25 @@ func getOpts(r *http.Request) (*options, error) { | ||||
| 		seed, _ = binary.ReadVarint(bytes.NewReader(b[0:8])) | ||||
| 	} | ||||
| 
 | ||||
| 	opts := &options{ | ||||
| 		Seed: seed, | ||||
| 	} | ||||
| 
 | ||||
| 	var n int | ||||
| 	if 0 != seed { | ||||
| 		rnd := rand.New(rand.NewSource(seed)) | ||||
| 		rndReader = rnd | ||||
| 		n = rnd.Intn(2) | ||||
| 		n = opts.nextRand().Intn(2) | ||||
| 	} else { | ||||
| 		n = rand.Intn(2) | ||||
| 	} | ||||
| 
 | ||||
| 	kty, _ := tok["kty"].(string) | ||||
| 	if "" == kty { | ||||
| 	opts.KeyType, _ = tok["kty"].(string) | ||||
| 	if "" == opts.KeyType { | ||||
| 		if 0 == n { | ||||
| 			kty = "RSA" | ||||
| 			opts.KeyType = "RSA" | ||||
| 		} else { | ||||
| 			kty = "EC" | ||||
| 			opts.KeyType = "EC" | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return &options{ | ||||
| 		KeyType:   kty, | ||||
| 		Seed:      seed, | ||||
| 		rndReader: rndReader, | ||||
| 	}, nil | ||||
| 	return opts, nil | ||||
| } | ||||
|  | ||||
| @ -38,7 +38,7 @@ func GeneratePrivateJWK(w http.ResponseWriter, r *http.Request) { | ||||
| 
 | ||||
| // GeneratePrivateDER will create a new private key in a valid DER encoding | ||||
| func GeneratePrivateDER(w http.ResponseWriter, r *http.Request) { | ||||
| 	log.Printf("%s %s\n", r.Method, r.URL.Path) | ||||
| 	log.Printf("%s %s", r.Method, r.URL.Path) | ||||
| 	if "POST" != r.Method { | ||||
| 		http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) | ||||
| 		return | ||||
| @ -58,7 +58,7 @@ func GeneratePrivateDER(w http.ResponseWriter, r *http.Request) { | ||||
| 
 | ||||
| // GeneratePrivatePEM will create a new private key in a valid PEM encoding | ||||
| func GeneratePrivatePEM(w http.ResponseWriter, r *http.Request) { | ||||
| 	log.Printf("%s %s\n", r.Method, r.URL.Path) | ||||
| 	log.Printf("%s %s", r.Method, r.URL.Path) | ||||
| 	if "POST" != r.Method { | ||||
| 		http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) | ||||
| 		return | ||||
| @ -80,9 +80,24 @@ func genPrivKey(opts *options) keypairs.PrivateKey { | ||||
| 	var privkey keypairs.PrivateKey | ||||
| 	if "RSA" == opts.KeyType { | ||||
| 		keylen := 2048 | ||||
| 		privkey, _ = rsa.GenerateKey(opts.rndReader, keylen) | ||||
| 		rndReader := opts.nextReader() | ||||
| 		privkey, _ = rsa.GenerateKey(rndReader, keylen) | ||||
| 		if rndReader != RandomReader { | ||||
| 			for i := 0; i < 16; i++ { | ||||
| 				otherkey, _ := rsa.GenerateKey(opts.nextRand(), 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 | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		privkey, _ = ecdsa.GenerateKey(elliptic.P256(), opts.rndReader) | ||||
| 		privkey, _ = ecdsa.GenerateKey(elliptic.P256(), opts.nextReader()) | ||||
| 	} | ||||
| 	return privkey | ||||
| } | ||||
|  | ||||
| @ -121,7 +121,7 @@ func TestGenWithSeed(t *testing.T) { | ||||
| 	res, err := client.Do(&http.Request{ | ||||
| 		Method: "POST", | ||||
| 		URL:    urlstr, | ||||
| 		Body:   ioutil.NopCloser(bytes.NewReader([]byte(`{"seed":"c"}`))), | ||||
| 		Body:   ioutil.NopCloser(bytes.NewReader([]byte(`{"seed":"test"}`))), | ||||
| 	}) | ||||
| 	if nil != err { | ||||
| 		//t.Fatal(err) | ||||
| @ -135,13 +135,15 @@ func TestGenWithSeed(t *testing.T) { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// See https://github.com/square/go-jose/issues/189 | ||||
| 	for i := 0; i < 8; i++ { | ||||
| 		// Key B | ||||
| 		client = srv.Client() | ||||
| 		urlstr, _ = url.Parse(srv.URL + "/private.jwk.json") | ||||
| 		res, err = client.Do(&http.Request{ | ||||
| 			Method: "POST", | ||||
| 			URL:    urlstr, | ||||
| 		Body:   ioutil.NopCloser(bytes.NewReader([]byte(`{"seed":"c"}`))), | ||||
| 			Body:   ioutil.NopCloser(bytes.NewReader([]byte(`{"seed":"test"}`))), | ||||
| 		}) | ||||
| 		if nil != err { | ||||
| 			//t.Fatal(err) | ||||
| @ -156,10 +158,13 @@ func TestGenWithSeed(t *testing.T) { | ||||
| 		} | ||||
| 
 | ||||
| 		if '{' != dataA[0] || len(dataA) < 100 || string(dataA) != string(dataB) { | ||||
| 			log.Println(string(dataA)) | ||||
| 			log.Println(string(dataB)) | ||||
| 			t.Error(errors.New("keys with identical seeds should be identical")) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestGenWithRand(t *testing.T) { | ||||
| 	// Key A | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user