Compare commits
	
		
			No commits in common. "3f7513364a5bd7cdce0fa9766850f1ec9c0bc606" and "6a22bfecc40cdba566d2e0619110dc14a8aaf8a0" have entirely different histories.
		
	
	
		
			3f7513364a
			...
			6a22bfecc4
		
	
		
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @ -4,7 +4,7 @@ go 1.13 | ||||
| 
 | ||||
| require ( | ||||
| 	git.rootprojects.org/root/hashcash v1.0.1 | ||||
| 	git.rootprojects.org/root/keypairs v0.6.5 | ||||
| 	git.rootprojects.org/root/keypairs v0.5.2 | ||||
| 	github.com/google/uuid v1.1.1 | ||||
| 	github.com/joho/godotenv v1.3.0 | ||||
| 	github.com/mailgun/mailgun-go/v3 v3.6.4 | ||||
|  | ||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							| @ -1,7 +1,7 @@ | ||||
| git.rootprojects.org/root/hashcash v1.0.1 h1:PkzwZu4CR5q/hwAntJdvcmNhmP0ONhetMo7rYhIZhZ0= | ||||
| git.rootprojects.org/root/hashcash v1.0.1/go.mod h1:HdoULUe94o1NVMES5K6aP3p8QGQiIia73F1HNZ1+FkQ= | ||||
| git.rootprojects.org/root/keypairs v0.6.5 h1:sdRAQD/O/JBS8+ZxUewXnY+cjQVDNH3TmcS+KtANZqA= | ||||
| git.rootprojects.org/root/keypairs v0.6.5/go.mod h1:WGI8PadOp+4LjUuI+wNlSwcJwFtY8L9XuNjuO3213HA= | ||||
| git.rootprojects.org/root/keypairs v0.5.2 h1:jr+drUUm/REaCDJTl5gT3kF2PwlXygcLsBZlqoKTZZw= | ||||
| git.rootprojects.org/root/keypairs v0.5.2/go.mod h1:WGI8PadOp+4LjUuI+wNlSwcJwFtY8L9XuNjuO3213HA= | ||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= | ||||
|  | ||||
| @ -13,6 +13,7 @@ import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.coolaj86.com/coolaj86/go-mockid/mockid" | ||||
| 	"git.coolaj86.com/coolaj86/go-mockid/xkeypairs" | ||||
| 	"git.rootprojects.org/root/keypairs" | ||||
| 
 | ||||
| 	_ "github.com/joho/godotenv/autoload" | ||||
| @ -91,7 +92,7 @@ func main() { | ||||
| 	}() | ||||
| 
 | ||||
| 	// TODO privB := keypairs.MarshalJWKPrivateKey(privkey) | ||||
| 	privB := keypairs.MarshalJWKPrivateKey(privkey) | ||||
| 	privB := xkeypairs.MarshalJWKPrivateKey(privkey) | ||||
| 	fmt.Printf("Private Key:\n\t%s\n", string(privB)) | ||||
| 	pubB := keypairs.MarshalJWKPublicKey(keypairs.NewPublicKey(privkey.Public())) | ||||
| 	fmt.Printf("Public Key:\n\t%s\n", string(pubB)) | ||||
|  | ||||
| @ -13,7 +13,6 @@ import ( | ||||
| 	"net/http" | ||||
| 
 | ||||
| 	"git.coolaj86.com/coolaj86/go-mockid/xkeypairs" | ||||
| 	"git.rootprojects.org/root/keypairs" | ||||
| ) | ||||
| 
 | ||||
| /* | ||||
| @ -48,8 +47,8 @@ func getOpts(r *http.Request) (*xkeypairs.KeyOptions, error) { | ||||
| 		Key:  key, | ||||
| 	} | ||||
| 
 | ||||
| 	opts.Claims, _ = tok["claims"].(keypairs.Object) | ||||
| 	opts.Header, _ = tok["header"].(keypairs.Object) | ||||
| 	opts.Claims, _ = tok["claims"].(xkeypairs.Object) | ||||
| 	opts.Header, _ = tok["header"].(xkeypairs.Object) | ||||
| 
 | ||||
| 	var n int | ||||
| 	if 0 != seed { | ||||
|  | ||||
| @ -45,7 +45,7 @@ func GeneratePrivateJWK(w http.ResponseWriter, r *http.Request) { | ||||
| 
 | ||||
| 	privkey := xkeypairs.GenPrivKey(opts) | ||||
| 
 | ||||
| 	jwk := keypairs.MarshalJWKPrivateKey(privkey) | ||||
| 	jwk := xkeypairs.MarshalJWKPrivateKey(privkey) | ||||
| 	w.Write(append(jwk, '\n')) | ||||
| } | ||||
| 
 | ||||
| @ -68,7 +68,7 @@ func GeneratePublicDER(w http.ResponseWriter, r *http.Request) { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	b, _ := keypairs.MarshalDERPublicKey(privkey.Public()) | ||||
| 	b, _ := xkeypairs.MarshalDERPublicKey(privkey.Public()) | ||||
| 
 | ||||
| 	w.Write(b) | ||||
| } | ||||
| @ -88,7 +88,7 @@ func GeneratePrivateDER(w http.ResponseWriter, r *http.Request) { | ||||
| 
 | ||||
| 	privkey := xkeypairs.GenPrivKey(opts) | ||||
| 
 | ||||
| 	der, _ := keypairs.MarshalDERPrivateKey(privkey) | ||||
| 	der, _ := xkeypairs.MarshalDERPrivateKey(privkey) | ||||
| 	w.Write(der) | ||||
| } | ||||
| 
 | ||||
| @ -111,7 +111,7 @@ func GeneratePublicPEM(w http.ResponseWriter, r *http.Request) { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	b, _ := keypairs.MarshalPEMPublicKey(privkey.Public()) | ||||
| 	b, _ := xkeypairs.MarshalPEMPublicKey(privkey.Public()) | ||||
| 
 | ||||
| 	w.Write(b) | ||||
| } | ||||
| @ -131,7 +131,7 @@ func GeneratePrivatePEM(w http.ResponseWriter, r *http.Request) { | ||||
| 
 | ||||
| 	privkey := xkeypairs.GenPrivKey(opts) | ||||
| 
 | ||||
| 	privpem, _ := keypairs.MarshalPEMPrivateKey(privkey) | ||||
| 	privpem, _ := xkeypairs.MarshalPEMPrivateKey(privkey) | ||||
| 	w.Write(privpem) | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -4,7 +4,7 @@ import ( | ||||
| 	"encoding/json" | ||||
| 	"net/http" | ||||
| 
 | ||||
| 	"git.rootprojects.org/root/keypairs" | ||||
| 	"git.coolaj86.com/coolaj86/go-mockid/xkeypairs" | ||||
| ) | ||||
| 
 | ||||
| // SignJWS will create an uncompressed JWT with the given payload | ||||
| @ -40,7 +40,7 @@ func sign(w http.ResponseWriter, r *http.Request, jwt bool) { | ||||
| 		header["_seed"] = opts.Seed | ||||
| 	} | ||||
| 
 | ||||
| 	jws, err := keypairs.SignClaims(privkey, header, opts.Claims) | ||||
| 	jws, err := xkeypairs.SignClaims(privkey, header, opts.Claims) | ||||
| 	if nil != err { | ||||
| 		http.Error(w, err.Error(), http.StatusBadRequest) | ||||
| 		return | ||||
| @ -48,7 +48,7 @@ func sign(w http.ResponseWriter, r *http.Request, jwt bool) { | ||||
| 
 | ||||
| 	var b []byte | ||||
| 	if jwt { | ||||
| 		s := keypairs.JWSToJWT(jws) | ||||
| 		s := xkeypairs.JWSToJWT(jws) | ||||
| 		w.Write(append([]byte(s), '\n')) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| @ -9,7 +9,7 @@ import ( | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.rootprojects.org/root/keypairs" | ||||
| 	"git.coolaj86.com/coolaj86/go-mockid/xkeypairs" | ||||
| ) | ||||
| 
 | ||||
| // Verify will verify both JWT and uncompressed JWS | ||||
| @ -19,7 +19,7 @@ func Verify(w http.ResponseWriter, r *http.Request) { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	jws := &keypairs.JWS{} | ||||
| 	jws := &xkeypairs.JWS{} | ||||
| 
 | ||||
| 	authzParts := strings.Split(r.Header.Get("Authorization"), " ") | ||||
| 	lenAuthz := len(authzParts) | ||||
| @ -75,12 +75,16 @@ func Verify(w http.ResponseWriter, r *http.Request) { | ||||
| 		jws.Claims["exp"] = float64(time.Now().Add(5 * time.Minute).Unix()) | ||||
| 	} | ||||
| 
 | ||||
| 	errs := keypairs.VerifyClaims(nil, jws) | ||||
| 	if 0 == len(errs) { | ||||
| 		log.Printf("jws verify error: %s", errs) | ||||
| 	ok, err := xkeypairs.VerifyClaims(nil, jws) | ||||
| 	if nil != err { | ||||
| 		log.Printf("jws verify error: %s", err) | ||||
| 		http.Error(w, "Bad Request: could not verify JWS claims", http.StatusBadRequest) | ||||
| 		return | ||||
| 	} | ||||
| 	if !ok { | ||||
| 		http.Error(w, "Bad Request: invalid JWS signature", http.StatusBadRequest) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	b := []byte(`{"success":true}`) | ||||
| 	w.Write(append(b, '\n')) | ||||
|  | ||||
| @ -66,13 +66,11 @@ type OTPResponse struct { | ||||
| 	HTTPResponse | ||||
| } | ||||
| 
 | ||||
| // Contact represents a map between an identifier and some users | ||||
| type Contact struct { | ||||
| 	Email    string   `json:"email"` | ||||
| 	Subjects []string `json:"subjects"` | ||||
| } | ||||
| 
 | ||||
| // Subject represents a map between a user and some identifiers | ||||
| type Subject struct { | ||||
| 	Subject string               `json:"subject"` | ||||
| 	Emails  map[string]time.Time `json:"emails"` | ||||
| @ -111,7 +109,7 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler { | ||||
| 	if nil != err { | ||||
| 		signingKey = xkeypairs.GenPrivKey(&xkeypairs.KeyOptions{}) | ||||
| 		_ = os.MkdirAll(jwksPrefix+"/private", 0750) | ||||
| 		b := keypairs.MarshalJWKPrivateKey(signingKey) | ||||
| 		b := xkeypairs.MarshalJWKPrivateKey(signingKey) | ||||
| 		if err := ioutil.WriteFile(privKeyJWKPath, b, 0600); nil != err { | ||||
| 			panic(err) | ||||
| 		} | ||||
| @ -322,10 +320,10 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler { | ||||
| 		uuid, _ := uuid.NewRandom() | ||||
| 		nonce, _ := uuid.MarshalBinary() | ||||
| 		baseURL := getBaseURL(r) | ||||
| 		tok, err := keypairs.SignClaims( | ||||
| 		tok, err := xkeypairs.SignClaims( | ||||
| 			signingKey, | ||||
| 			keypairs.Object{}, | ||||
| 			keypairs.Object{ | ||||
| 			xkeypairs.Object{}, | ||||
| 			xkeypairs.Object{ | ||||
| 				"sub": sub, | ||||
| 				"iss": baseURL + "/", | ||||
| 				"jti": base64.RawURLEncoding.EncodeToString(nonce), | ||||
| @ -338,7 +336,7 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler { | ||||
| 			fmt.Fprintf(w, "%s", err) | ||||
| 			return | ||||
| 		} | ||||
| 		otp.AccessToken = keypairs.JWSToJWT(tok) | ||||
| 		otp.AccessToken = xkeypairs.JWSToJWT(tok) | ||||
| 		b, _ := json.Marshal(&OTPResponse{ | ||||
| 			HTTPResponse: HTTPResponse{Success: true}, | ||||
| 			OTP:          *otp, | ||||
| @ -463,7 +461,7 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler { | ||||
| 	http.HandleFunc("/api/new-account", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		myURL := getBaseURL(r) + r.URL.Path | ||||
| 
 | ||||
| 		jws := &keypairs.JWS{} | ||||
| 		jws := &xkeypairs.JWS{} | ||||
| 
 | ||||
| 		decoder := json.NewDecoder(r.Body) | ||||
| 		if err := decoder.Decode(jws); nil != err { | ||||
| @ -506,8 +504,11 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler { | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		errs := keypairs.VerifyClaims(nil, jws) | ||||
| 		if 0 != len(errs) { | ||||
| 		ok, err := xkeypairs.VerifyClaims(nil, jws) | ||||
| 		if nil != err || !ok { | ||||
| 			if nil != err { | ||||
| 				log.Printf("jws verify error: %s", err) | ||||
| 			} | ||||
| 			http.Error(w, "Bad Request", http.StatusBadRequest) | ||||
| 			fmt.Fprintf(w, `{"error":"could not verify JWS claims"}`+"\n") | ||||
| 			return | ||||
| @ -529,7 +530,7 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler { | ||||
| 		) | ||||
| 		if nil != err { | ||||
| 			http.Error(w, "Bad Request", http.StatusBadRequest) | ||||
| 			msg, _ := json.Marshal(err) | ||||
| 			msg, _ := json.Marshal(err.Error()) | ||||
| 			fmt.Fprintf(w, `{"error":%s}`+"\n", msg) | ||||
| 			return | ||||
| 		} | ||||
| @ -683,7 +684,7 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler { | ||||
| 
 | ||||
| 	http.HandleFunc("/key.jwk.json", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		log.Printf("%s %s", r.Method, r.URL.Path) | ||||
| 		jwk := string(keypairs.MarshalJWKPrivateKey(privkey)) | ||||
| 		jwk := string(xkeypairs.MarshalJWKPrivateKey(privkey)) | ||||
| 		jwk = strings.Replace(jwk, `{"`, `{ "`, 1) | ||||
| 		jwk = strings.Replace(jwk, `",`, `", `, -1) | ||||
| 		jwk = jwk[0 : len(jwk)-1] | ||||
| @ -815,7 +816,6 @@ func verifyToken(token string) (*InspectableToken, error) { | ||||
| 	return inspected, nil | ||||
| } | ||||
| 
 | ||||
| // OTP is the one-time password for auth | ||||
| type OTP struct { | ||||
| 	//Attempts    int       `json:"attempts"` | ||||
| 	CreatedAt   time.Time `json:"created_at"` | ||||
|  | ||||
							
								
								
									
										5
									
								
								vendor/git.rootprojects.org/root/keypairs/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								vendor/git.rootprojects.org/root/keypairs/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,5 +0,0 @@ | ||||
| /keypairs | ||||
| /dist/ | ||||
| 
 | ||||
| .DS_Store | ||||
| .*.sw* | ||||
							
								
								
									
										41
									
								
								vendor/git.rootprojects.org/root/keypairs/.goreleaser.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										41
									
								
								vendor/git.rootprojects.org/root/keypairs/.goreleaser.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,41 +0,0 @@ | ||||
| # This is an example goreleaser.yaml file with some sane defaults. | ||||
| # Make sure to check the documentation at http://goreleaser.com | ||||
| before: | ||||
|   hooks: | ||||
|     - go generate ./... | ||||
| builds: | ||||
|   - id: keypairs | ||||
|     main: ./cmd/keypairs/keypairs.go | ||||
|     env: | ||||
|       - CGO_ENABLED=0 | ||||
|     flags: | ||||
|       - -mod=vendor | ||||
|     goos: | ||||
|       - linux | ||||
|       - windows | ||||
|       - darwin | ||||
|       - freebsd | ||||
|     goarch: | ||||
|       - amd64 | ||||
|       - arm | ||||
|       - arm64 | ||||
| archives: | ||||
|   - replacements: | ||||
|       386: i386 | ||||
|       amd64: x86-64 | ||||
|       arm64: aarch64 | ||||
|     format_overrides: | ||||
|       - goos: windows | ||||
|         format: zip | ||||
| env_files: | ||||
|   github_token: ~/.config/goreleaser/github_token.txt | ||||
| checksum: | ||||
|   name_template: 'checksums.txt' | ||||
| snapshot: | ||||
|   name_template: "{{ .Tag }}-next" | ||||
| changelog: | ||||
|   sort: asc | ||||
|   filters: | ||||
|     exclude: | ||||
|       - '^docs:' | ||||
|       - '^test:' | ||||
							
								
								
									
										1
									
								
								vendor/git.rootprojects.org/root/keypairs/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/git.rootprojects.org/root/keypairs/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | ||||
| AJ ONeal <aj@therootcompany.com> (https://therootcompany.com) | ||||
							
								
								
									
										10
									
								
								vendor/git.rootprojects.org/root/keypairs/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/git.rootprojects.org/root/keypairs/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,4 +1,4 @@ | ||||
| # [keypairs](https://git.rootprojects.org/root/keypairs) | ||||
| # go-keypairs | ||||
| 
 | ||||
| JSON Web Key (JWK) support and type safety lightly placed over top of Go's `crypto/ecdsa` and `crypto/rsa` | ||||
| 
 | ||||
| @ -14,9 +14,9 @@ jwk, err := keypairs.MarshalJWKPublicKey(pub, time.Now().Add(2 * time.Day)) | ||||
| kid, err := keypairs.ThumbprintPublicKey(pub) | ||||
| ``` | ||||
| 
 | ||||
| # GoDoc API Documentation | ||||
| # API Documentation | ||||
| 
 | ||||
| See <https://pkg.go.dev/git.rootprojects.org/root/keypairs> | ||||
| See <https://godoc.org/github.com/big-squid/go-keypairs> | ||||
| 
 | ||||
| # Philosophy | ||||
| 
 | ||||
| @ -56,8 +56,8 @@ between the ASN.1, x509, PEM, and JWK formats. | ||||
| 
 | ||||
| # LICENSE | ||||
| 
 | ||||
| Copyright (c) 2020-present AJ ONeal \ | ||||
| Copyright (c) 2020-present AJ ONeal | ||||
| Copyright (c) 2018-2019 Big Squid, Inc. | ||||
| 
 | ||||
| This work is licensed under the terms of the MIT license. \ | ||||
| This work is licensed under the terms of the MIT license. | ||||
| For a copy, see <https://opensource.org/licenses/MIT>. | ||||
|  | ||||
							
								
								
									
										19
									
								
								vendor/git.rootprojects.org/root/keypairs/cli_test.sh
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								vendor/git.rootprojects.org/root/keypairs/cli_test.sh
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,19 +0,0 @@ | ||||
| #!/bin/bash | ||||
| set -u | ||||
| 
 | ||||
| go build -mod=vendor cmd/keypairs/*.go | ||||
| ./keypairs gen > testkey.jwk.json 2> testpub.jwk.json | ||||
| 
 | ||||
| ./keypairs sign --exp 1h ./testkey.jwk.json '{"foo":"bar"}' > testjwt.txt 2> testjws.json | ||||
| 
 | ||||
| echo "" | ||||
| echo "Should pass:" | ||||
| ./keypairs verify ./testpub.jwk.json testjwt.txt > /dev/null | ||||
| ./keypairs verify ./testpub.jwk.json "$(cat testjwt.txt)" > /dev/null | ||||
| ./keypairs verify ./testpub.jwk.json testjws.json > /dev/null | ||||
| ./keypairs verify ./testpub.jwk.json "$(cat testjws.json)" > /dev/null | ||||
| 
 | ||||
| echo "" | ||||
| echo "Should fail:" | ||||
| ./keypairs sign --exp -1m ./testkey.jwk.json '{"bar":"foo"}' > errjwt.txt 2> errjws.json | ||||
| ./keypairs verify ./testpub.jwk.json errjwt.txt > /dev/null | ||||
							
								
								
									
										69
									
								
								vendor/git.rootprojects.org/root/keypairs/generate.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										69
									
								
								vendor/git.rootprojects.org/root/keypairs/generate.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,69 +0,0 @@ | ||||
| package keypairs | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/elliptic" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/rsa" | ||||
| 	"io" | ||||
| 	mathrand "math/rand" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| var randReader io.Reader = rand.Reader | ||||
| var allowMocking = false | ||||
| 
 | ||||
| // 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"` | ||||
| 	mockSeed int64  //`json:"-"` | ||||
| 	//SeedStr string `json:"seed"` | ||||
| 	//Claims  Object `json:"claims"` | ||||
| 	//Header  Object `json:"header"` | ||||
| } | ||||
| 
 | ||||
| func (o *keyOptions) nextReader() io.Reader { | ||||
| 	if allowMocking { | ||||
| 		return o.maybeMockReader() | ||||
| 	} | ||||
| 	return randReader | ||||
| } | ||||
| 
 | ||||
| // NewDefaultPrivateKey generates a key with reasonable strength. | ||||
| // Today that means a 256-bit equivalent - either RSA 2048 or EC P-256. | ||||
| func NewDefaultPrivateKey() PrivateKey { | ||||
| 	// insecure random is okay here, | ||||
| 	// it's just used for a coin toss | ||||
| 	mathrand.Seed(time.Now().UnixNano()) | ||||
| 	coin := mathrand.Int() | ||||
| 
 | ||||
| 	// the idea here is that we want to make | ||||
| 	// it dead simple to support RSA and EC | ||||
| 	// so it shouldn't matter which is used | ||||
| 	if 0 == coin%2 { | ||||
| 		return newPrivateKey(&keyOptions{ | ||||
| 			KeyType: "RSA", | ||||
| 		}) | ||||
| 	} | ||||
| 	return newPrivateKey(&keyOptions{ | ||||
| 		KeyType: "EC", | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // newPrivateKey generates a 256-bit entropy RSA or ECDSA private key | ||||
| func newPrivateKey(opts *keyOptions) PrivateKey { | ||||
| 	var privkey PrivateKey | ||||
| 
 | ||||
| 	if "RSA" == opts.KeyType { | ||||
| 		keylen := 2048 | ||||
| 		privkey, _ = rsa.GenerateKey(opts.nextReader(), keylen) | ||||
| 		if allowMocking { | ||||
| 			privkey = maybeDerandomizeMockKey(privkey, keylen, opts) | ||||
| 		} | ||||
| 	} else { | ||||
| 		// TODO: EC keys may also suffer the same random problems in the future | ||||
| 		privkey, _ = ecdsa.GenerateKey(elliptic.P256(), opts.nextReader()) | ||||
| 	} | ||||
| 	return privkey | ||||
| } | ||||
							
								
								
									
										69
									
								
								vendor/git.rootprojects.org/root/keypairs/jwk.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										69
									
								
								vendor/git.rootprojects.org/root/keypairs/jwk.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,69 +0,0 @@ | ||||
| package keypairs | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| ) | ||||
| 
 | ||||
| // JWK abstracts EC and RSA keys | ||||
| type JWK interface { | ||||
| 	marshalJWK() ([]byte, error) | ||||
| } | ||||
| 
 | ||||
| // ECJWK is the EC variant | ||||
| type ECJWK struct { | ||||
| 	KeyID string   `json:"kid,omitempty"` | ||||
| 	Curve string   `json:"crv"` | ||||
| 	X     string   `json:"x"` | ||||
| 	Y     string   `json:"y"` | ||||
| 	Use   []string `json:"use,omitempty"` | ||||
| 	Seed  string   `json:"_seed,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (k *ECJWK) marshalJWK() ([]byte, error) { | ||||
| 	return []byte(fmt.Sprintf(`{"crv":%q,"kty":"EC","x":%q,"y":%q}`, k.Curve, k.X, k.Y)), nil | ||||
| } | ||||
| 
 | ||||
| // RSAJWK is the RSA variant | ||||
| type RSAJWK struct { | ||||
| 	KeyID string   `json:"kid,omitempty"` | ||||
| 	Exp   string   `json:"e"` | ||||
| 	N     string   `json:"n"` | ||||
| 	Use   []string `json:"use,omitempty"` | ||||
| 	Seed  string   `json:"_seed,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (k *RSAJWK) marshalJWK() ([]byte, error) { | ||||
| 	return []byte(fmt.Sprintf(`{"e":%q,"kty":"RSA","n":%q}`, k.Exp, k.N)), nil | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| // ToPublicJWK exposes only the public parts | ||||
| func ToPublicJWK(pubkey PublicKey) JWK { | ||||
| 	switch k := pubkey.Key().(type) { | ||||
| 	case *ecdsa.PublicKey: | ||||
| 		return ECToPublicJWK(k) | ||||
| 	case *rsa.PublicKey: | ||||
| 		return RSAToPublicJWK(k) | ||||
| 	default: | ||||
| 		panic(errors.New("impossible key type")) | ||||
| 		//return nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // ECToPublicJWK will output the most minimal version of an EC JWK (no key id, no "use" flag, nada) | ||||
| func ECToPublicJWK(k *ecdsa.PublicKey) *ECJWK { | ||||
| 	return &ECJWK{ | ||||
| 		Curve: k.Curve.Params().Name, | ||||
| 		X:     base64.RawURLEncoding.EncodeToString(k.X.Bytes()), | ||||
| 		Y:     base64.RawURLEncoding.EncodeToString(k.Y.Bytes()), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // RSAToPublicJWK will output the most minimal version of an RSA JWK (no key id, no "use" flag, nada) | ||||
| func RSAToPublicJWK(p *rsa.PublicKey) *RSAJWK { | ||||
| 	return &RSAJWK{ | ||||
| 		Exp: base64.RawURLEncoding.EncodeToString(big.NewInt(int64(p.E)).Bytes()), | ||||
| 		N:   base64.RawURLEncoding.EncodeToString(p.N.Bytes()), | ||||
| 	} | ||||
| } | ||||
| */ | ||||
							
								
								
									
										63
									
								
								vendor/git.rootprojects.org/root/keypairs/jws.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										63
									
								
								vendor/git.rootprojects.org/root/keypairs/jws.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,63 +0,0 @@ | ||||
| package keypairs | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // JWS is a parsed JWT, representation as signable/verifiable and human-readable parts | ||||
| type JWS struct { | ||||
| 	Header    Object `json:"header"`    // JSON | ||||
| 	Claims    Object `json:"claims"`    // JSON | ||||
| 	Protected string `json:"protected"` // base64 | ||||
| 	Payload   string `json:"payload"`   // base64 | ||||
| 	Signature string `json:"signature"` // base64 | ||||
| } | ||||
| 
 | ||||
| // JWSToJWT joins JWS parts into a JWT as {ProtectedHeader}.{SerializedPayload}.{Signature}. | ||||
| func JWSToJWT(jwt *JWS) string { | ||||
| 	return fmt.Sprintf( | ||||
| 		"%s.%s.%s", | ||||
| 		jwt.Protected, | ||||
| 		jwt.Payload, | ||||
| 		jwt.Signature, | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| // JWTToJWS splits the JWT into its JWS segments | ||||
| func JWTToJWS(jwt string) (jws *JWS) { | ||||
| 	jwt = strings.TrimSpace(jwt) | ||||
| 	parts := strings.Split(jwt, ".") | ||||
| 	if 3 != len(parts) { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return &JWS{ | ||||
| 		Protected: parts[0], | ||||
| 		Payload:   parts[1], | ||||
| 		Signature: parts[2], | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // DecodeComponents decodes JWS Header and Claims | ||||
| func (jws *JWS) DecodeComponents() error { | ||||
| 	protected, err := base64.RawURLEncoding.DecodeString(jws.Protected) | ||||
| 	if nil != err { | ||||
| 		return errors.New("invalid JWS header base64Url encoding") | ||||
| 	} | ||||
| 	if err := json.Unmarshal([]byte(protected), &jws.Header); nil != err { | ||||
| 		return errors.New("invalid JWS header") | ||||
| 	} | ||||
| 
 | ||||
| 	payload, err := base64.RawURLEncoding.DecodeString(jws.Payload) | ||||
| 	if nil != err { | ||||
| 		return errors.New("invalid JWS payload base64Url encoding") | ||||
| 	} | ||||
| 	if err := json.Unmarshal([]byte(payload), &jws.Claims); nil != err { | ||||
| 		return errors.New("invalid JWS claims") | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										171
									
								
								vendor/git.rootprojects.org/root/keypairs/marshal.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										171
									
								
								vendor/git.rootprojects.org/root/keypairs/marshal.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,171 +0,0 @@ | ||||
| package keypairs | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/rsa" | ||||
| 	"crypto/x509" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/pem" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"math/big" | ||||
| 	mathrand "math/rand" | ||||
| ) | ||||
| 
 | ||||
| // MarshalPEMPublicKey outputs the given public key as JWK | ||||
| func MarshalPEMPublicKey(pubkey crypto.PublicKey) ([]byte, error) { | ||||
| 	block, err := marshalDERPublicKey(pubkey) | ||||
| 	if nil != err { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return pem.EncodeToMemory(block), nil | ||||
| } | ||||
| 
 | ||||
| // MarshalDERPublicKey outputs the given public key as JWK | ||||
| func MarshalDERPublicKey(pubkey crypto.PublicKey) ([]byte, error) { | ||||
| 	block, err := marshalDERPublicKey(pubkey) | ||||
| 	if nil != err { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return block.Bytes, nil | ||||
| } | ||||
| 
 | ||||
| // marshalDERPublicKey outputs the given public key as JWK | ||||
| func marshalDERPublicKey(pubkey crypto.PublicKey) (*pem.Block, error) { | ||||
| 
 | ||||
| 	var der []byte | ||||
| 	var typ string | ||||
| 	var err error | ||||
| 	switch k := pubkey.(type) { | ||||
| 	case *rsa.PublicKey: | ||||
| 		der = x509.MarshalPKCS1PublicKey(k) | ||||
| 		typ = "RSA PUBLIC KEY" | ||||
| 	case *ecdsa.PublicKey: | ||||
| 		typ = "PUBLIC KEY" | ||||
| 		der, err = x509.MarshalPKIXPublicKey(k) | ||||
| 		if nil != err { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	default: | ||||
| 		panic("Developer Error: impossible key type") | ||||
| 	} | ||||
| 
 | ||||
| 	return &pem.Block{ | ||||
| 		Bytes: der, | ||||
| 		Type:  typ, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // MarshalJWKPrivateKey outputs the given private key as JWK | ||||
| func MarshalJWKPrivateKey(privkey PrivateKey) []byte { | ||||
| 	// thumbprint keys are alphabetically sorted and only include the necessary public parts | ||||
| 	switch k := privkey.(type) { | ||||
| 	case *rsa.PrivateKey: | ||||
| 		return MarshalRSAPrivateKey(k) | ||||
| 	case *ecdsa.PrivateKey: | ||||
| 		return MarshalECPrivateKey(k) | ||||
| 	default: | ||||
| 		// this is unreachable because we know the types that we pass in | ||||
| 		log.Printf("keytype: %t, %+v\n", privkey, privkey) | ||||
| 		panic(ErrInvalidPublicKey) | ||||
| 		//return nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // MarshalDERPrivateKey outputs the given private key as ASN.1 DER | ||||
| func MarshalDERPrivateKey(privkey PrivateKey) ([]byte, error) { | ||||
| 	// thumbprint keys are alphabetically sorted and only include the necessary public parts | ||||
| 	switch k := privkey.(type) { | ||||
| 	case *rsa.PrivateKey: | ||||
| 		return x509.MarshalPKCS1PrivateKey(k), nil | ||||
| 	case *ecdsa.PrivateKey: | ||||
| 		return x509.MarshalECPrivateKey(k) | ||||
| 	default: | ||||
| 		// this is unreachable because we know the types that we pass in | ||||
| 		log.Printf("keytype: %t, %+v\n", privkey, privkey) | ||||
| 		panic(ErrInvalidPublicKey) | ||||
| 		//return nil, nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func marshalDERPrivateKey(privkey PrivateKey) (*pem.Block, error) { | ||||
| 	var typ string | ||||
| 	var bytes []byte | ||||
| 	var err error | ||||
| 
 | ||||
| 	switch k := privkey.(type) { | ||||
| 	case *rsa.PrivateKey: | ||||
| 		if 0 == mathrand.Intn(2) { | ||||
| 			typ = "PRIVATE KEY" | ||||
| 			bytes, err = x509.MarshalPKCS8PrivateKey(k) | ||||
| 			if nil != err { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} else { | ||||
| 			typ = "RSA PRIVATE KEY" | ||||
| 			bytes = x509.MarshalPKCS1PrivateKey(k) | ||||
| 		} | ||||
| 		return &pem.Block{ | ||||
| 			Type:  typ, | ||||
| 			Bytes: bytes, | ||||
| 		}, nil | ||||
| 	case *ecdsa.PrivateKey: | ||||
| 		if 0 == mathrand.Intn(2) { | ||||
| 			typ = "PRIVATE KEY" | ||||
| 			bytes, err = x509.MarshalPKCS8PrivateKey(k) | ||||
| 		} else { | ||||
| 			typ = "EC PRIVATE KEY" | ||||
| 			bytes, err = x509.MarshalECPrivateKey(k) | ||||
| 		} | ||||
| 		if nil != err { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return &pem.Block{ | ||||
| 			Type:  typ, | ||||
| 			Bytes: bytes, | ||||
| 		}, nil | ||||
| 	default: | ||||
| 		// this is unreachable because we know the types that we pass in | ||||
| 		log.Printf("keytype: %t, %+v\n", privkey, privkey) | ||||
| 		panic(ErrInvalidPublicKey) | ||||
| 		//return nil, nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // MarshalPEMPrivateKey outputs the given private key as ASN.1 PEM | ||||
| func MarshalPEMPrivateKey(privkey PrivateKey) ([]byte, error) { | ||||
| 	block, err := marshalDERPrivateKey(privkey) | ||||
| 	if nil != err { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return pem.EncodeToMemory(block), nil | ||||
| } | ||||
| 
 | ||||
| // MarshalECPrivateKey will output the given private key as JWK | ||||
| func MarshalECPrivateKey(k *ecdsa.PrivateKey) []byte { | ||||
| 	crv := k.Curve.Params().Name | ||||
| 	d := base64.RawURLEncoding.EncodeToString(k.D.Bytes()) | ||||
| 	x := base64.RawURLEncoding.EncodeToString(k.X.Bytes()) | ||||
| 	y := base64.RawURLEncoding.EncodeToString(k.Y.Bytes()) | ||||
| 	return []byte(fmt.Sprintf( | ||||
| 		`{"crv":%q,"d":%q,"kty":"EC","x":%q,"y":%q}`, | ||||
| 		crv, d, x, y, | ||||
| 	)) | ||||
| } | ||||
| 
 | ||||
| // MarshalRSAPrivateKey will output the given private key as JWK | ||||
| func MarshalRSAPrivateKey(pk *rsa.PrivateKey) []byte { | ||||
| 	e := base64.RawURLEncoding.EncodeToString(big.NewInt(int64(pk.E)).Bytes()) | ||||
| 	n := base64.RawURLEncoding.EncodeToString(pk.N.Bytes()) | ||||
| 	d := base64.RawURLEncoding.EncodeToString(pk.D.Bytes()) | ||||
| 	p := base64.RawURLEncoding.EncodeToString(pk.Primes[0].Bytes()) | ||||
| 	q := base64.RawURLEncoding.EncodeToString(pk.Primes[1].Bytes()) | ||||
| 	dp := base64.RawURLEncoding.EncodeToString(pk.Precomputed.Dp.Bytes()) | ||||
| 	dq := base64.RawURLEncoding.EncodeToString(pk.Precomputed.Dq.Bytes()) | ||||
| 	qi := base64.RawURLEncoding.EncodeToString(pk.Precomputed.Qinv.Bytes()) | ||||
| 	return []byte(fmt.Sprintf( | ||||
| 		`{"d":%q,"dp":%q,"dq":%q,"e":%q,"kty":"RSA","n":%q,"p":%q,"q":%q,"qi":%q}`, | ||||
| 		d, dp, dq, e, n, p, q, qi, | ||||
| 	)) | ||||
| } | ||||
							
								
								
									
										46
									
								
								vendor/git.rootprojects.org/root/keypairs/mock.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								vendor/git.rootprojects.org/root/keypairs/mock.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,46 +0,0 @@ | ||||
| package keypairs | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/rsa" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	mathrand "math/rand" | ||||
| ) | ||||
| 
 | ||||
| // this shananigans is only for testing and debug API stuff | ||||
| func (o *keyOptions) maybeMockReader() io.Reader { | ||||
| 	if !allowMocking { | ||||
| 		panic("mock method called when mocking is not allowed") | ||||
| 	} | ||||
| 
 | ||||
| 	if 0 == o.mockSeed { | ||||
| 		return randReader | ||||
| 	} | ||||
| 
 | ||||
| 	log.Println("WARNING: MOCK: using insecure reader") | ||||
| 	return mathrand.New(mathrand.NewSource(o.mockSeed)) | ||||
| } | ||||
| 
 | ||||
| const maxRetry = 16 | ||||
| 
 | ||||
| func maybeDerandomizeMockKey(privkey PrivateKey, keylen int, opts *keyOptions) PrivateKey { | ||||
| 	if 0 != opts.mockSeed { | ||||
| 		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) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return privkey | ||||
| } | ||||
							
								
								
									
										165
									
								
								vendor/git.rootprojects.org/root/keypairs/sign.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										165
									
								
								vendor/git.rootprojects.org/root/keypairs/sign.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,165 +0,0 @@ | ||||
| package keypairs | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/rsa" | ||||
| 	"crypto/sha256" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	mathrand "math/rand" // to be used for good, not evil | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Object is a type alias representing generic JSON data | ||||
| 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 PrivateKey, header Object, claims Object) (*JWS, error) { | ||||
| 	var randsrc io.Reader = randReader | ||||
| 	seed, _ := header["_seed"].(int64) | ||||
| 	if 0 != seed { | ||||
| 		randsrc = mathrand.New(mathrand.NewSource(seed)) | ||||
| 		//delete(header, "_seed") | ||||
| 	} | ||||
| 
 | ||||
| 	protected, header, err := headerToProtected(NewPublicKey(privkey.Public()), header) | ||||
| 	if nil != err { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	protected64 := base64.RawURLEncoding.EncodeToString(protected) | ||||
| 
 | ||||
| 	payload, err := claimsToPayload(claims) | ||||
| 	if nil != err { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	payload64 := base64.RawURLEncoding.EncodeToString(payload) | ||||
| 
 | ||||
| 	signable := fmt.Sprintf(`%s.%s`, protected64, payload64) | ||||
| 	hash := sha256.Sum256([]byte(signable)) | ||||
| 
 | ||||
| 	sig := Sign(privkey, hash[:], randsrc) | ||||
| 	sig64 := base64.RawURLEncoding.EncodeToString(sig) | ||||
| 	//log.Printf("\n(Sign)\nSignable: %s", signable) | ||||
| 	//log.Printf("Hash: %s", hash) | ||||
| 	//log.Printf("Sig: %s", sig64) | ||||
| 
 | ||||
| 	return &JWS{ | ||||
| 		Header:    header, | ||||
| 		Claims:    claims, | ||||
| 		Protected: protected64, | ||||
| 		Payload:   payload64, | ||||
| 		Signature: sig64, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func headerToProtected(pub PublicKey, header Object) ([]byte, Object, error) { | ||||
| 	if nil == header { | ||||
| 		header = Object{} | ||||
| 	} | ||||
| 
 | ||||
| 	// Only supporting 2048-bit and P256 keys right now | ||||
| 	// because that's all that's practical and well-supported. | ||||
| 	// No security theatre here. | ||||
| 	alg := "ES256" | ||||
| 	switch pub.Key().(type) { | ||||
| 	case *rsa.PublicKey: | ||||
| 		alg = "RS256" | ||||
| 	} | ||||
| 
 | ||||
| 	if selfSign, _ := header["_jwk"].(bool); selfSign { | ||||
| 		delete(header, "_jwk") | ||||
| 		any := Object{} | ||||
| 		_ = json.Unmarshal(MarshalJWKPublicKey(pub), &any) | ||||
| 		header["jwk"] = any | ||||
| 	} | ||||
| 
 | ||||
| 	// TODO what are the acceptable values? JWT. JWS? others? | ||||
| 	header["typ"] = "JWT" | ||||
| 	if _, ok := header["jwk"]; !ok { | ||||
| 		thumbprint := ThumbprintPublicKey(pub) | ||||
| 		kid, _ := header["kid"].(string) | ||||
| 		if "" != kid && thumbprint != kid { | ||||
| 			return nil, nil, errors.New("'kid' should be the key's thumbprint") | ||||
| 		} | ||||
| 		header["kid"] = thumbprint | ||||
| 	} | ||||
| 	header["alg"] = alg | ||||
| 
 | ||||
| 	protected, err := json.Marshal(header) | ||||
| 	if nil != err { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
| 	return protected, header, nil | ||||
| } | ||||
| 
 | ||||
| func claimsToPayload(claims Object) ([]byte, error) { | ||||
| 	if nil == claims { | ||||
| 		claims = Object{} | ||||
| 	} | ||||
| 
 | ||||
| 	var dur time.Duration | ||||
| 	jti, _ := claims["jti"].(string) | ||||
| 	insecure, _ := claims["insecure"].(bool) | ||||
| 
 | ||||
| 	switch exp := claims["exp"].(type) { | ||||
| 	case time.Duration: | ||||
| 		// TODO: MUST this go first? | ||||
| 		// int64(time.Duration) vs time.Duration(int64) | ||||
| 		dur = exp | ||||
| 	case string: | ||||
| 		var err error | ||||
| 		dur, err = time.ParseDuration(exp) | ||||
| 		// TODO s, err := time.ParseDuration(dur) | ||||
| 		if nil != err { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	case int: | ||||
| 		dur = time.Second * time.Duration(exp) | ||||
| 	case int64: | ||||
| 		dur = time.Second * time.Duration(exp) | ||||
| 	case float64: | ||||
| 		dur = time.Second * time.Duration(exp) | ||||
| 	default: | ||||
| 		dur = 0 | ||||
| 	} | ||||
| 
 | ||||
| 	if "" == jti && 0 == dur && !insecure { | ||||
| 		return nil, errors.New("token must have jti or exp as to be expirable / cancellable") | ||||
| 	} | ||||
| 	claims["exp"] = time.Now().Add(dur).Unix() | ||||
| 
 | ||||
| 	return json.Marshal(claims) | ||||
| } | ||||
| 
 | ||||
| // Sign signs both RSA and ECDSA. Use `nil` or `crypto/rand.Reader` except for debugging. | ||||
| func Sign(privkey PrivateKey, hash []byte, rand io.Reader) []byte { | ||||
| 	if nil == rand { | ||||
| 		rand = randReader | ||||
| 	} | ||||
| 	var sig []byte | ||||
| 
 | ||||
| 	if len(hash) != 32 { | ||||
| 		panic("only 256-bit hashes for 2048-bit and 256-bit keys are supported") | ||||
| 	} | ||||
| 
 | ||||
| 	switch k := privkey.(type) { | ||||
| 	case *rsa.PrivateKey: | ||||
| 		sig, _ = rsa.SignPKCS1v15(rand, k, crypto.SHA256, hash) | ||||
| 	case *ecdsa.PrivateKey: | ||||
| 		r, s, _ := ecdsa.Sign(rand, k, hash[:]) | ||||
| 		rb := r.Bytes() | ||||
| 		for len(rb) < 32 { | ||||
| 			rb = append([]byte{0}, rb...) | ||||
| 		} | ||||
| 		sb := s.Bytes() | ||||
| 		for len(rb) < 32 { | ||||
| 			sb = append([]byte{0}, sb...) | ||||
| 		} | ||||
| 		sig = append(rb, sb...) | ||||
| 	} | ||||
| 	return sig | ||||
| } | ||||
							
								
								
									
										174
									
								
								vendor/git.rootprojects.org/root/keypairs/verify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										174
									
								
								vendor/git.rootprojects.org/root/keypairs/verify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,174 +0,0 @@ | ||||
| package keypairs | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/rsa" | ||||
| 	"crypto/sha256" | ||||
| 	"crypto/subtle" | ||||
| 	"encoding/base64" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"math/big" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // VerifyClaims will check the signature of a parsed JWT | ||||
| func VerifyClaims(pubkey PublicKey, jws *JWS) (errs []error) { | ||||
| 	kid, _ := jws.Header["kid"].(string) | ||||
| 	jwkmap, hasJWK := jws.Header["jwk"].(Object) | ||||
| 	//var jwk JWK = nil | ||||
| 
 | ||||
| 	seed, _ := jws.Header["_seed"].(int64) | ||||
| 	seedf64, _ := jws.Header["_seed"].(float64) | ||||
| 	kty, _ := jws.Header["_kty"].(string) | ||||
| 	if 0 == seed { | ||||
| 		seed = int64(seedf64) | ||||
| 	} | ||||
| 
 | ||||
| 	var pub PublicKey = nil | ||||
| 	if hasJWK { | ||||
| 		pub, errs = selfsignCheck(jwkmap, errs) | ||||
| 	} else { | ||||
| 		opts := &keyOptions{mockSeed: seed, KeyType: kty} | ||||
| 		pub, errs = pubkeyCheck(pubkey, kid, opts, errs) | ||||
| 	} | ||||
| 
 | ||||
| 	jti, _ := jws.Claims["jti"].(string) | ||||
| 	expf64, _ := jws.Claims["exp"].(float64) | ||||
| 	exp := int64(expf64) | ||||
| 	if 0 == exp { | ||||
| 		if "" == jti { | ||||
| 			err := errors.New("one of 'jti' or 'exp' must exist for token expiry") | ||||
| 			errs = append(errs, err) | ||||
| 		} | ||||
| 	} else { | ||||
| 		if time.Now().Unix() > exp { | ||||
| 			err := fmt.Errorf("token expired at %d (%s)", exp, time.Unix(exp, 0)) | ||||
| 			errs = append(errs, err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	signable := fmt.Sprintf("%s.%s", jws.Protected, jws.Payload) | ||||
| 	hash := sha256.Sum256([]byte(signable)) | ||||
| 	sig, err := base64.RawURLEncoding.DecodeString(jws.Signature) | ||||
| 	if nil != err { | ||||
| 		err := fmt.Errorf("could not decode signature: %w", err) | ||||
| 		errs = append(errs, err) | ||||
| 		return errs | ||||
| 	} | ||||
| 
 | ||||
| 	//log.Printf("\n(Verify)\nSignable: %s", signable) | ||||
| 	//log.Printf("Hash: %s", hash) | ||||
| 	//log.Printf("Sig: %s", jws.Signature) | ||||
| 	if nil == pub { | ||||
| 		err := fmt.Errorf("token signature could not be verified") | ||||
| 		errs = append(errs, err) | ||||
| 	} else if !Verify(pub, hash[:], sig) { | ||||
| 		err := fmt.Errorf("token signature is not valid") | ||||
| 		errs = append(errs, err) | ||||
| 	} | ||||
| 	return errs | ||||
| } | ||||
| 
 | ||||
| func selfsignCheck(jwkmap Object, errs []error) (PublicKey, []error) { | ||||
| 	var pub PublicKey = nil | ||||
| 	log.Println("Security TODO: did not check jws.Claims[\"sub\"] against 'jwk'") | ||||
| 	log.Println("Security TODO: did not check jws.Claims[\"iss\"]") | ||||
| 	kty := jwkmap["kty"] | ||||
| 	var err error | ||||
| 	if "RSA" == kty { | ||||
| 		e, _ := jwkmap["e"].(string) | ||||
| 		n, _ := jwkmap["n"].(string) | ||||
| 		k, _ := (&RSAJWK{ | ||||
| 			Exp: e, | ||||
| 			N:   n, | ||||
| 		}).marshalJWK() | ||||
| 		pub, err = ParseJWKPublicKey(k) | ||||
| 		if nil != err { | ||||
| 			return nil, append(errs, err) | ||||
| 		} | ||||
| 	} else { | ||||
| 		crv, _ := jwkmap["crv"].(string) | ||||
| 		x, _ := jwkmap["x"].(string) | ||||
| 		y, _ := jwkmap["y"].(string) | ||||
| 		k, _ := (&ECJWK{ | ||||
| 			Curve: crv, | ||||
| 			X:     x, | ||||
| 			Y:     y, | ||||
| 		}).marshalJWK() | ||||
| 		pub, err = ParseJWKPublicKey(k) | ||||
| 		if nil != err { | ||||
| 			return nil, append(errs, err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return pub, errs | ||||
| } | ||||
| 
 | ||||
| func pubkeyCheck(pubkey PublicKey, kid string, opts *keyOptions, errs []error) (PublicKey, []error) { | ||||
| 	var pub PublicKey = nil | ||||
| 
 | ||||
| 	if "" == kid { | ||||
| 		err := errors.New("token should have 'kid' or 'jwk' in header to identify the public key") | ||||
| 		errs = append(errs, err) | ||||
| 	} | ||||
| 
 | ||||
| 	if nil == pubkey { | ||||
| 		if allowMocking { | ||||
| 			if 0 == opts.mockSeed { | ||||
| 				err := errors.New("the debug API requires '_seed' to accompany 'kid'") | ||||
| 				errs = append(errs, err) | ||||
| 			} | ||||
| 			if "" == opts.KeyType { | ||||
| 				err := errors.New("the debug API requires '_kty' to accompany '_seed'") | ||||
| 				errs = append(errs, err) | ||||
| 			} | ||||
| 
 | ||||
| 			if 0 == opts.mockSeed || "" == opts.KeyType { | ||||
| 				return nil, errs | ||||
| 			} | ||||
| 			privkey := newPrivateKey(opts) | ||||
| 			pub = NewPublicKey(privkey.Public()) | ||||
| 			return pub, errs | ||||
| 		} | ||||
| 		err := errors.New("no matching public key") | ||||
| 		errs = append(errs, err) | ||||
| 	} else { | ||||
| 		pub = pubkey | ||||
| 	} | ||||
| 
 | ||||
| 	if nil != pub && "" != kid { | ||||
| 		if 1 != subtle.ConstantTimeCompare([]byte(kid), []byte(pub.Thumbprint())) { | ||||
| 			err := errors.New("'kid' does not match the public key thumbprint") | ||||
| 			errs = append(errs, err) | ||||
| 		} | ||||
| 	} | ||||
| 	return pub, errs | ||||
| } | ||||
| 
 | ||||
| // Verify will check the signature of a hash | ||||
| func Verify(pubkey PublicKey, hash []byte, sig []byte) bool { | ||||
| 
 | ||||
| 	switch pub := pubkey.Key().(type) { | ||||
| 	case *rsa.PublicKey: | ||||
| 		//log.Printf("RSA VERIFY") | ||||
| 		// TODO Size(key) to detect key size ? | ||||
| 		//alg := "SHA256" | ||||
| 		// TODO: this hasn't been tested yet | ||||
| 		if err := rsa.VerifyPKCS1v15(pub, crypto.SHA256, hash, sig); nil != err { | ||||
| 			return false | ||||
| 		} | ||||
| 		return true | ||||
| 	case *ecdsa.PublicKey: | ||||
| 		r := &big.Int{} | ||||
| 		r.SetBytes(sig[0:32]) | ||||
| 		s := &big.Int{} | ||||
| 		s.SetBytes(sig[32:]) | ||||
| 		return ecdsa.Verify(pub, hash, r, s) | ||||
| 	default: | ||||
| 		panic("impossible condition: non-rsa/non-ecdsa key") | ||||
| 		//return false | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										2
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| # git.rootprojects.org/root/hashcash v1.0.1 | ||||
| git.rootprojects.org/root/hashcash | ||||
| # git.rootprojects.org/root/keypairs v0.6.5 | ||||
| # git.rootprojects.org/root/keypairs v0.5.2 | ||||
| git.rootprojects.org/root/keypairs | ||||
| git.rootprojects.org/root/keypairs/keyfetch | ||||
| git.rootprojects.org/root/keypairs/keyfetch/uncached | ||||
|  | ||||
| @ -3,30 +3,22 @@ package xkeypairs | ||||
| import ( | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/elliptic" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/rsa" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	mathrand "math/rand" | ||||
| 	"math/rand" | ||||
| 
 | ||||
| 	"git.rootprojects.org/root/keypairs" | ||||
| ) | ||||
| 
 | ||||
| const maxRetry = 16 | ||||
| 
 | ||||
| // RandomReader may be overwritten for testing | ||||
| var RandomReader io.Reader = rand.Reader | ||||
| 
 | ||||
| //var RandomReader = rand.Reader | ||||
| 
 | ||||
| // 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  keypairs.Object `json:"claims"` | ||||
| 	Header  keypairs.Object `json:"header"` | ||||
| 	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 | ||||
| @ -34,7 +26,7 @@ func (o *KeyOptions) MyFooNextReader() io.Reader { | ||||
| 	if 0 == o.Seed { | ||||
| 		return RandomReader | ||||
| 	} | ||||
| 	return mathrand.New(mathrand.NewSource(o.Seed)) | ||||
| 	return rand.New(rand.NewSource(o.Seed)) | ||||
| } | ||||
| 
 | ||||
| // GenPrivKey generates a 256-bit entropy RSA or ECDSA private key | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user