TUN-7586: Upgrade go-jose/go-jose/v3 and core-os/go-oidc/v3
Removes usages of gopkg.in/square/go-jose.v2 and gopkg.in/coreos/go-oidc.v2 packages.
This commit is contained in:
		
							parent
							
								
									60a44fb030
								
							
						
					
					
						commit
						b2654318b1
					
				
							
								
								
									
										7
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										7
									
								
								go.mod
								
								
								
								
							|  | @ -6,7 +6,7 @@ require ( | |||
| 	github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93 | ||||
| 	github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc | ||||
| 	github.com/coredns/coredns v1.10.0 | ||||
| 	github.com/coreos/go-oidc/v3 v3.4.0 | ||||
| 	github.com/coreos/go-oidc/v3 v3.6.0 | ||||
| 	github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf | ||||
| 	github.com/facebookgo/grace v0.0.0-20180706040059-75cf19382434 | ||||
| 	github.com/fsnotify/fsnotify v1.4.9 | ||||
|  | @ -43,9 +43,7 @@ require ( | |||
| 	golang.org/x/sys v0.7.0 | ||||
| 	golang.org/x/term v0.7.0 | ||||
| 	google.golang.org/protobuf v1.28.1 | ||||
| 	gopkg.in/coreos/go-oidc.v2 v2.2.1 | ||||
| 	gopkg.in/natefinch/lumberjack.v2 v2.0.0 | ||||
| 	gopkg.in/square/go-jose.v2 v2.6.0 | ||||
| 	gopkg.in/yaml.v3 v3.0.1 | ||||
| 	nhooyr.io/websocket v1.8.7 | ||||
| 	zombiezen.com/go/capnproto2 v2.18.0+incompatible | ||||
|  | @ -86,7 +84,6 @@ require ( | |||
| 	github.com/onsi/gomega v1.23.0 // indirect | ||||
| 	github.com/opentracing/opentracing-go v1.2.0 // indirect | ||||
| 	github.com/pmezard/go-difflib v1.0.0 // indirect | ||||
| 	github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect | ||||
| 	github.com/prometheus/common v0.37.0 // indirect | ||||
| 	github.com/prometheus/procfs v0.8.0 // indirect | ||||
| 	github.com/quic-go/qtls-go1-19 v0.3.2 // indirect | ||||
|  | @ -94,7 +91,7 @@ require ( | |||
| 	github.com/russross/blackfriday/v2 v2.1.0 // indirect | ||||
| 	golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect | ||||
| 	golang.org/x/mod v0.8.0 // indirect | ||||
| 	golang.org/x/oauth2 v0.4.0 // indirect | ||||
| 	golang.org/x/oauth2 v0.6.0 // indirect | ||||
| 	golang.org/x/text v0.9.0 // indirect | ||||
| 	golang.org/x/tools v0.6.0 // indirect | ||||
| 	google.golang.org/appengine v1.6.7 // indirect | ||||
|  |  | |||
							
								
								
									
										226
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										226
									
								
								go.sum
								
								
								
								
							|  | @ -13,36 +13,14 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV | |||
| cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= | ||||
| cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= | ||||
| cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= | ||||
| cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= | ||||
| cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= | ||||
| cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= | ||||
| cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= | ||||
| cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= | ||||
| cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= | ||||
| cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= | ||||
| cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= | ||||
| cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= | ||||
| cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= | ||||
| cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= | ||||
| cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= | ||||
| cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= | ||||
| cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= | ||||
| cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= | ||||
| cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= | ||||
| cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= | ||||
| cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= | ||||
| cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= | ||||
| cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= | ||||
| cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= | ||||
| cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= | ||||
| cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= | ||||
| cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= | ||||
| cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= | ||||
| cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= | ||||
| cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= | ||||
| cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= | ||||
| cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= | ||||
| cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= | ||||
| cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= | ||||
| cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= | ||||
| cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= | ||||
|  | @ -52,7 +30,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo | |||
| cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= | ||||
| cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= | ||||
| cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= | ||||
| cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= | ||||
| dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= | ||||
|  | @ -93,20 +70,18 @@ github.com/cloudflare/qtls-pq v0.0.0-20230320122459-4ed280d0d633/go.mod h1:j/igS | |||
| github.com/cloudflare/qtls-pq v0.0.0-20230320123031-3faac1a945b2 h1:0/KuLjh9lBMiXlooAdwoo+FbLVD5DABtquB0ImEFOK0= | ||||
| github.com/cloudflare/qtls-pq v0.0.0-20230320123031-3faac1a945b2/go.mod h1:XzuZIjv4mF5cM205RHHW1d60PQtWGwMR6jx38YKuYHs= | ||||
| github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= | ||||
| github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= | ||||
| github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= | ||||
| github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= | ||||
| github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= | ||||
| github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= | ||||
| github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= | ||||
| github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= | ||||
| github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= | ||||
| github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0= | ||||
| github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= | ||||
| github.com/coredns/coredns v1.10.0 h1:jCfuWsBjTs0dapkkhISfPCzn5LqvSRtrFtaf/Tjj4DI= | ||||
| github.com/coredns/coredns v1.10.0/go.mod h1:CIfRU5TgpuoIiJBJ4XrofQzfFQpPFh32ERpUevrSlaw= | ||||
| github.com/coreos/go-oidc/v3 v3.4.0 h1:xz7elHb/LDwm/ERpwHd+5nb7wFHL32rsr6bBOgaeu6g= | ||||
| github.com/coreos/go-oidc/v3 v3.4.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw= | ||||
| github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o= | ||||
| github.com/coreos/go-oidc/v3 v3.6.0/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc= | ||||
| github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= | ||||
| github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= | ||||
| github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= | ||||
|  | @ -122,12 +97,9 @@ github.com/devincarr/quic-go v0.0.0-20230502200822-d1f4edacbee7/go.mod h1:+4CVgV | |||
| github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | ||||
| github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | ||||
| github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= | ||||
| github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= | ||||
| github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= | ||||
| github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= | ||||
| github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= | ||||
| github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= | ||||
| github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= | ||||
| github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= | ||||
| github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= | ||||
| github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= | ||||
|  | @ -209,7 +181,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt | |||
| github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= | ||||
| github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= | ||||
| github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= | ||||
| github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= | ||||
| github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= | ||||
| github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= | ||||
| github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
|  | @ -227,10 +198,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD | |||
| github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||||
| github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||||
| github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | ||||
| github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= | ||||
| github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= | ||||
| github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | ||||
| github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | ||||
| github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | ||||
| github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | ||||
| github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | ||||
|  | @ -240,21 +209,16 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ | |||
| github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= | ||||
| github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||
| github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= | ||||
| github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= | ||||
| github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= | ||||
| github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= | ||||
| github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= | ||||
| github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= | ||||
| github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | ||||
| github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | ||||
| github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | ||||
|  | @ -262,12 +226,6 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf | |||
| github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | ||||
| github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | ||||
| github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | ||||
| github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | ||||
| github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | ||||
| github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | ||||
| github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | ||||
| github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | ||||
| github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | ||||
| github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= | ||||
| github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | ||||
| github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= | ||||
|  | @ -275,15 +233,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ | |||
| github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= | ||||
| github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= | ||||
| github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= | ||||
| github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= | ||||
| github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= | ||||
| github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= | ||||
| github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= | ||||
| github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= | ||||
| github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= | ||||
| github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= | ||||
| github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||
| github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= | ||||
| github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||
|  | @ -362,8 +313,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | |||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= | ||||
| github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= | ||||
| github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= | ||||
| github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= | ||||
| github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= | ||||
|  | @ -424,15 +373,12 @@ github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0 | |||
| github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= | ||||
| go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= | ||||
| go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= | ||||
| go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||
| go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||
| go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||
| go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= | ||||
| go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= | ||||
| go.opentelemetry.io/contrib/propagators v0.22.0 h1:KGdv58M2//veiYLIhb31mofaI2LgkIPXXAZVeYVyfd8= | ||||
| go.opentelemetry.io/contrib/propagators v0.22.0/go.mod h1:xGOuXr6lLIF9BXipA4pm6UuOSI0M98U6tsI3khbOiwU= | ||||
| go.opentelemetry.io/otel v1.0.0-RC2/go.mod h1:w1thVQ7qbAy8MHb0IFj8a5Q2QU0l2ksf8u/CN8m3NOM= | ||||
|  | @ -485,8 +431,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl | |||
| golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= | ||||
| golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= | ||||
| golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= | ||||
| golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= | ||||
| golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= | ||||
| golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= | ||||
| golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= | ||||
| golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= | ||||
|  | @ -495,8 +439,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB | |||
| golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= | ||||
| golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= | ||||
| golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | ||||
|  | @ -528,26 +470,12 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ | |||
| golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||
| golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||
| golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||
| golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||
| golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||
| golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||
| golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= | ||||
| golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= | ||||
| golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | ||||
| golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | ||||
| golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | ||||
| golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | ||||
| golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= | ||||
| golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= | ||||
| golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= | ||||
| golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= | ||||
| golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= | ||||
| golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | ||||
| golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | ||||
| golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= | ||||
| golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= | ||||
| golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= | ||||
| golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||
|  | @ -555,24 +483,11 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr | |||
| golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= | ||||
| golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= | ||||
| golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= | ||||
| golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= | ||||
| golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= | ||||
| golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= | ||||
| golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= | ||||
| golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= | ||||
| golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= | ||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
|  | @ -581,10 +496,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ | |||
| golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= | ||||
| golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
|  | @ -620,44 +533,17 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w | |||
| golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= | ||||
|  | @ -671,7 +557,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |||
| golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | ||||
|  | @ -721,17 +606,7 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY | |||
| golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= | ||||
| golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= | ||||
| golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= | ||||
| golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= | ||||
| golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= | ||||
| golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= | ||||
| golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= | ||||
| golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= | ||||
| golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= | ||||
| golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= | ||||
| golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= | ||||
| golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= | ||||
| golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= | ||||
|  | @ -739,9 +614,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T | |||
| golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= | ||||
| golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= | ||||
| google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= | ||||
| google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= | ||||
| google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= | ||||
|  | @ -758,29 +630,6 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M | |||
| google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= | ||||
| google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= | ||||
| google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= | ||||
| google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= | ||||
| google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= | ||||
| google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= | ||||
| google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= | ||||
| google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= | ||||
| google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= | ||||
| google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= | ||||
| google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= | ||||
| google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= | ||||
| google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= | ||||
| google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= | ||||
| google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= | ||||
| google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= | ||||
| google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= | ||||
| google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= | ||||
| google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= | ||||
| google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= | ||||
| google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= | ||||
| google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= | ||||
| google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= | ||||
| google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= | ||||
| google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= | ||||
| google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= | ||||
| google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | ||||
| google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
| google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
|  | @ -819,54 +668,7 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc | |||
| google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= | ||||
| google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= | ||||
| google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= | ||||
| google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= | ||||
| google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= | ||||
| google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= | ||||
| google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= | ||||
| google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= | ||||
| google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= | ||||
| google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= | ||||
| google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= | ||||
| google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= | ||||
| google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= | ||||
| google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= | ||||
| google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= | ||||
| google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= | ||||
| google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= | ||||
| google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= | ||||
| google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= | ||||
| google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= | ||||
| google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= | ||||
| google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= | ||||
| google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= | ||||
| google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= | ||||
| google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= | ||||
| google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= | ||||
| google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= | ||||
| google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= | ||||
| google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= | ||||
| google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= | ||||
| google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= | ||||
| google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= | ||||
| google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= | ||||
| google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= | ||||
| google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= | ||||
| google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= | ||||
| google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= | ||||
| google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= | ||||
| google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= | ||||
| google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd h1:OjndDrsik+Gt+e6fs45z9AxiewiKyLKYpA45W5Kpkks= | ||||
| google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= | ||||
| google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | ||||
|  | @ -881,29 +683,13 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa | |||
| google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= | ||||
| google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= | ||||
| google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= | ||||
| google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= | ||||
| google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= | ||||
| google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= | ||||
| google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= | ||||
| google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= | ||||
| google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= | ||||
| google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= | ||||
| google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= | ||||
| google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= | ||||
| google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= | ||||
| google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= | ||||
| google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= | ||||
| google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= | ||||
| google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= | ||||
| google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= | ||||
| google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= | ||||
| google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= | ||||
| google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= | ||||
| google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= | ||||
| google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= | ||||
| google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= | ||||
| google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= | ||||
| google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= | ||||
| google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= | ||||
| google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= | ||||
| google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | ||||
|  | @ -926,13 +712,9 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 | |||
| gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= | ||||
| gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= | ||||
| gopkg.in/coreos/go-oidc.v2 v2.2.1 h1:MY5SZClJ7vhjKfr64a4nHAOV/c3WH2gB9BMrR64J1Mc= | ||||
| gopkg.in/coreos/go-oidc.v2 v2.2.1/go.mod h1:fYaTe2FS96wZZwR17YTDHwG+Mw6fmyqJNxN2eNCGPCI= | ||||
| gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= | ||||
| gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= | ||||
| gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= | ||||
| gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= | ||||
| gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= | ||||
| gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
|  |  | |||
|  | @ -7,8 +7,8 @@ import ( | |||
| 	"errors" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/go-jose/go-jose/v3" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 	"gopkg.in/square/go-jose.v2" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
|  |  | |||
|  | @ -15,10 +15,10 @@ import ( | |||
| 	"net/url" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/go-jose/go-jose/v3/jwt" | ||||
| 	homedir "github.com/mitchellh/go-homedir" | ||||
| 	"github.com/pkg/errors" | ||||
| 	gossh "golang.org/x/crypto/ssh" | ||||
| 	"gopkg.in/square/go-jose.v2/jwt" | ||||
| 
 | ||||
| 	"github.com/cloudflare/cloudflared/config" | ||||
| 	cfpath "github.com/cloudflare/cloudflared/token" | ||||
|  |  | |||
|  | @ -16,9 +16,9 @@ import ( | |||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/go-jose/go-jose/v3" | ||||
| 	"github.com/go-jose/go-jose/v3/jwt" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"gopkg.in/square/go-jose.v2" | ||||
| 	"gopkg.in/square/go-jose.v2/jwt" | ||||
| 
 | ||||
| 	"github.com/cloudflare/cloudflared/config" | ||||
| 	cfpath "github.com/cloudflare/cloudflared/token" | ||||
|  |  | |||
|  | @ -13,9 +13,9 @@ import ( | |||
| 	"syscall" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/go-jose/go-jose/v3" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"github.com/rs/zerolog" | ||||
| 	"gopkg.in/square/go-jose.v2" | ||||
| 
 | ||||
| 	"github.com/cloudflare/cloudflared/config" | ||||
| 	"github.com/cloudflare/cloudflared/retry" | ||||
|  |  | |||
|  | @ -9,9 +9,9 @@ import ( | |||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/coreos/go-oidc/v3/oidc" | ||||
| 	"github.com/pkg/errors" | ||||
| 	"golang.org/x/net/idna" | ||||
| 	"gopkg.in/coreos/go-oidc.v2" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
|  |  | |||
|  | @ -13,4 +13,5 @@ const ( | |||
| 	PS256 = "PS256" // RSASSA-PSS using SHA256 and MGF1-SHA256
 | ||||
| 	PS384 = "PS384" // RSASSA-PSS using SHA384 and MGF1-SHA384
 | ||||
| 	PS512 = "PS512" // RSASSA-PSS using SHA512 and MGF1-SHA512
 | ||||
| 	EdDSA = "EdDSA" // Ed25519 using SHA-512
 | ||||
| ) | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import ( | |||
| 	"context" | ||||
| 	"crypto" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/ed25519" | ||||
| 	"crypto/rsa" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
|  | @ -12,7 +13,7 @@ import ( | |||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	jose "gopkg.in/square/go-jose.v2" | ||||
| 	jose "github.com/go-jose/go-jose/v3" | ||||
| ) | ||||
| 
 | ||||
| // StaticKeySet is a verifier that validates JWT against a static set of public keys.
 | ||||
|  | @ -32,6 +33,7 @@ func (s *StaticKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, | |||
| 		switch pub.(type) { | ||||
| 		case *rsa.PublicKey: | ||||
| 		case *ecdsa.PublicKey: | ||||
| 		case ed25519.PublicKey: | ||||
| 		default: | ||||
| 			return nil, fmt.Errorf("invalid public key type provided: %T", pub) | ||||
| 		} | ||||
|  | @ -60,7 +62,7 @@ func newRemoteKeySet(ctx context.Context, jwksURL string, now func() time.Time) | |||
| 	if now == nil { | ||||
| 		now = time.Now | ||||
| 	} | ||||
| 	return &RemoteKeySet{jwksURL: jwksURL, ctx: cloneContext(ctx), now: now} | ||||
| 	return &RemoteKeySet{jwksURL: jwksURL, ctx: ctx, now: now} | ||||
| } | ||||
| 
 | ||||
| // RemoteKeySet is a KeySet implementation that validates JSON web tokens against
 | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ import ( | |||
| 	"mime" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/oauth2" | ||||
|  | @ -48,39 +49,34 @@ var issuerURLKey contextKey | |||
| // This method sets the same context key used by the golang.org/x/oauth2 package,
 | ||||
| // so the returned context works for that package too.
 | ||||
| //
 | ||||
| //    myClient := &http.Client{}
 | ||||
| //    ctx := oidc.ClientContext(parentContext, myClient)
 | ||||
| //
 | ||||
| //    // This will use the custom client
 | ||||
| //    provider, err := oidc.NewProvider(ctx, "https://accounts.example.com")
 | ||||
| //	myClient := &http.Client{}
 | ||||
| //	ctx := oidc.ClientContext(parentContext, myClient)
 | ||||
| //
 | ||||
| //	// This will use the custom client
 | ||||
| //	provider, err := oidc.NewProvider(ctx, "https://accounts.example.com")
 | ||||
| func ClientContext(ctx context.Context, client *http.Client) context.Context { | ||||
| 	return context.WithValue(ctx, oauth2.HTTPClient, client) | ||||
| } | ||||
| 
 | ||||
| // cloneContext copies a context's bag-of-values into a new context that isn't
 | ||||
| // associated with its cancellation. This is used to initialize remote keys sets
 | ||||
| // which run in the background and aren't associated with the initial context.
 | ||||
| func cloneContext(ctx context.Context) context.Context { | ||||
| 	cp := context.Background() | ||||
| func getClient(ctx context.Context) *http.Client { | ||||
| 	if c, ok := ctx.Value(oauth2.HTTPClient).(*http.Client); ok { | ||||
| 		cp = ClientContext(cp, c) | ||||
| 		return c | ||||
| 	} | ||||
| 	return cp | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // InsecureIssuerURLContext allows discovery to work when the issuer_url reported
 | ||||
| // by upstream is mismatched with the discovery URL. This is meant for integration
 | ||||
| // with off-spec providers such as Azure.
 | ||||
| //
 | ||||
| //    discoveryBaseURL := "https://login.microsoftonline.com/organizations/v2.0"
 | ||||
| //    issuerURL := "https://login.microsoftonline.com/my-tenantid/v2.0"
 | ||||
| //	discoveryBaseURL := "https://login.microsoftonline.com/organizations/v2.0"
 | ||||
| //	issuerURL := "https://login.microsoftonline.com/my-tenantid/v2.0"
 | ||||
| //
 | ||||
| //    ctx := oidc.InsecureIssuerURLContext(parentContext, issuerURL)
 | ||||
| //	ctx := oidc.InsecureIssuerURLContext(parentContext, issuerURL)
 | ||||
| //
 | ||||
| //    // Provider will be discovered with the discoveryBaseURL, but use issuerURL
 | ||||
| //    // for future issuer validation.
 | ||||
| //    provider, err := oidc.NewProvider(ctx, discoveryBaseURL)
 | ||||
| //	// Provider will be discovered with the discoveryBaseURL, but use issuerURL
 | ||||
| //	// for future issuer validation.
 | ||||
| //	provider, err := oidc.NewProvider(ctx, discoveryBaseURL)
 | ||||
| //
 | ||||
| // This is insecure because validating the correct issuer is critical for multi-tenant
 | ||||
| // proivders. Any overrides here MUST be carefully reviewed.
 | ||||
|  | @ -90,7 +86,7 @@ func InsecureIssuerURLContext(ctx context.Context, issuerURL string) context.Con | |||
| 
 | ||||
| func doRequest(ctx context.Context, req *http.Request) (*http.Response, error) { | ||||
| 	client := http.DefaultClient | ||||
| 	if c, ok := ctx.Value(oauth2.HTTPClient).(*http.Client); ok { | ||||
| 	if c := getClient(ctx); c != nil { | ||||
| 		client = c | ||||
| 	} | ||||
| 	return client.Do(req.WithContext(ctx)) | ||||
|  | @ -102,12 +98,33 @@ type Provider struct { | |||
| 	authURL     string | ||||
| 	tokenURL    string | ||||
| 	userInfoURL string | ||||
| 	jwksURL     string | ||||
| 	algorithms  []string | ||||
| 
 | ||||
| 	// Raw claims returned by the server.
 | ||||
| 	rawClaims []byte | ||||
| 
 | ||||
| 	remoteKeySet KeySet | ||||
| 	// Guards all of the following fields.
 | ||||
| 	mu sync.Mutex | ||||
| 	// HTTP client specified from the initial NewProvider request. This is used
 | ||||
| 	// when creating the common key set.
 | ||||
| 	client *http.Client | ||||
| 	// A key set that uses context.Background() and is shared between all code paths
 | ||||
| 	// that don't have a convinent way of supplying a unique context.
 | ||||
| 	commonRemoteKeySet KeySet | ||||
| } | ||||
| 
 | ||||
| func (p *Provider) remoteKeySet() KeySet { | ||||
| 	p.mu.Lock() | ||||
| 	defer p.mu.Unlock() | ||||
| 	if p.commonRemoteKeySet == nil { | ||||
| 		ctx := context.Background() | ||||
| 		if p.client != nil { | ||||
| 			ctx = ClientContext(ctx, p.client) | ||||
| 		} | ||||
| 		p.commonRemoteKeySet = NewRemoteKeySet(ctx, p.jwksURL) | ||||
| 	} | ||||
| 	return p.commonRemoteKeySet | ||||
| } | ||||
| 
 | ||||
| type providerJSON struct { | ||||
|  | @ -132,6 +149,7 @@ var supportedAlgorithms = map[string]bool{ | |||
| 	PS256: true, | ||||
| 	PS384: true, | ||||
| 	PS512: true, | ||||
| 	EdDSA: true, | ||||
| } | ||||
| 
 | ||||
| // ProviderConfig allows creating providers when discovery isn't supported. It's
 | ||||
|  | @ -167,12 +185,13 @@ type ProviderConfig struct { | |||
| // through discovery.
 | ||||
| func (p *ProviderConfig) NewProvider(ctx context.Context) *Provider { | ||||
| 	return &Provider{ | ||||
| 		issuer:       p.IssuerURL, | ||||
| 		authURL:      p.AuthURL, | ||||
| 		tokenURL:     p.TokenURL, | ||||
| 		userInfoURL:  p.UserInfoURL, | ||||
| 		algorithms:   p.Algorithms, | ||||
| 		remoteKeySet: NewRemoteKeySet(cloneContext(ctx), p.JWKSURL), | ||||
| 		issuer:      p.IssuerURL, | ||||
| 		authURL:     p.AuthURL, | ||||
| 		tokenURL:    p.TokenURL, | ||||
| 		userInfoURL: p.UserInfoURL, | ||||
| 		jwksURL:     p.JWKSURL, | ||||
| 		algorithms:  p.Algorithms, | ||||
| 		client:      getClient(ctx), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -221,26 +240,27 @@ func NewProvider(ctx context.Context, issuer string) (*Provider, error) { | |||
| 		} | ||||
| 	} | ||||
| 	return &Provider{ | ||||
| 		issuer:       issuerURL, | ||||
| 		authURL:      p.AuthURL, | ||||
| 		tokenURL:     p.TokenURL, | ||||
| 		userInfoURL:  p.UserInfoURL, | ||||
| 		algorithms:   algs, | ||||
| 		rawClaims:    body, | ||||
| 		remoteKeySet: NewRemoteKeySet(cloneContext(ctx), p.JWKSURL), | ||||
| 		issuer:      issuerURL, | ||||
| 		authURL:     p.AuthURL, | ||||
| 		tokenURL:    p.TokenURL, | ||||
| 		userInfoURL: p.UserInfoURL, | ||||
| 		jwksURL:     p.JWKSURL, | ||||
| 		algorithms:  algs, | ||||
| 		rawClaims:   body, | ||||
| 		client:      getClient(ctx), | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // Claims unmarshals raw fields returned by the server during discovery.
 | ||||
| //
 | ||||
| //    var claims struct {
 | ||||
| //        ScopesSupported []string `json:"scopes_supported"`
 | ||||
| //        ClaimsSupported []string `json:"claims_supported"`
 | ||||
| //    }
 | ||||
| //	var claims struct {
 | ||||
| //	    ScopesSupported []string `json:"scopes_supported"`
 | ||||
| //	    ClaimsSupported []string `json:"claims_supported"`
 | ||||
| //	}
 | ||||
| //
 | ||||
| //    if err := provider.Claims(&claims); err != nil {
 | ||||
| //        // handle unmarshaling error
 | ||||
| //    }
 | ||||
| //	if err := provider.Claims(&claims); err != nil {
 | ||||
| //	    // handle unmarshaling error
 | ||||
| //	}
 | ||||
| //
 | ||||
| // For a list of fields defined by the OpenID Connect spec see:
 | ||||
| // https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
 | ||||
|  | @ -256,6 +276,12 @@ func (p *Provider) Endpoint() oauth2.Endpoint { | |||
| 	return oauth2.Endpoint{AuthURL: p.authURL, TokenURL: p.tokenURL} | ||||
| } | ||||
| 
 | ||||
| // UserInfoEndpoint returns the OpenID Connect userinfo endpoint for the given
 | ||||
| // provider.
 | ||||
| func (p *Provider) UserInfoEndpoint() string { | ||||
| 	return p.userInfoURL | ||||
| } | ||||
| 
 | ||||
| // UserInfo represents the OpenID Connect userinfo claims.
 | ||||
| type UserInfo struct { | ||||
| 	Subject       string `json:"sub"` | ||||
|  | @ -317,7 +343,7 @@ func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource) | |||
| 	ct := resp.Header.Get("Content-Type") | ||||
| 	mediaType, _, parseErr := mime.ParseMediaType(ct) | ||||
| 	if parseErr == nil && mediaType == "application/jwt" { | ||||
| 		payload, err := p.remoteKeySet.VerifySignature(ctx, string(body)) | ||||
| 		payload, err := p.remoteKeySet().VerifySignature(ctx, string(body)) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("oidc: invalid userinfo jwt signature %v", err) | ||||
| 		} | ||||
|  | @ -391,18 +417,17 @@ type IDToken struct { | |||
| 
 | ||||
| // Claims unmarshals the raw JSON payload of the ID Token into a provided struct.
 | ||||
| //
 | ||||
| //		idToken, err := idTokenVerifier.Verify(rawIDToken)
 | ||||
| //		if err != nil {
 | ||||
| //			// handle error
 | ||||
| //		}
 | ||||
| //		var claims struct {
 | ||||
| //			Email         string `json:"email"`
 | ||||
| //			EmailVerified bool   `json:"email_verified"`
 | ||||
| //		}
 | ||||
| //		if err := idToken.Claims(&claims); err != nil {
 | ||||
| //			// handle error
 | ||||
| //		}
 | ||||
| //
 | ||||
| //	idToken, err := idTokenVerifier.Verify(rawIDToken)
 | ||||
| //	if err != nil {
 | ||||
| //		// handle error
 | ||||
| //	}
 | ||||
| //	var claims struct {
 | ||||
| //		Email         string `json:"email"`
 | ||||
| //		EmailVerified bool   `json:"email_verified"`
 | ||||
| //	}
 | ||||
| //	if err := idToken.Claims(&claims); err != nil {
 | ||||
| //		// handle error
 | ||||
| //	}
 | ||||
| func (i *IDToken) Claims(v interface{}) error { | ||||
| 	if i.claims == nil { | ||||
| 		return errors.New("oidc: claims not set") | ||||
|  | @ -424,7 +449,7 @@ func (i *IDToken) VerifyAccessToken(accessToken string) error { | |||
| 		h = sha256.New() | ||||
| 	case RS384, ES384, PS384: | ||||
| 		h = sha512.New384() | ||||
| 	case RS512, ES512, PS512: | ||||
| 	case RS512, ES512, PS512, EdDSA: | ||||
| 		h = sha512.New() | ||||
| 	default: | ||||
| 		return fmt.Errorf("oidc: unsupported signing algorithm %q", i.sigAlgorithm) | ||||
|  |  | |||
|  | @ -12,8 +12,8 @@ import ( | |||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	jose "github.com/go-jose/go-jose/v3" | ||||
| 	"golang.org/x/oauth2" | ||||
| 	jose "gopkg.in/square/go-jose.v2" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
|  | @ -64,14 +64,13 @@ type IDTokenVerifier struct { | |||
| // This constructor can be used to create a verifier directly using the issuer URL and
 | ||||
| // JSON Web Key Set URL without using discovery:
 | ||||
| //
 | ||||
| //		keySet := oidc.NewRemoteKeySet(ctx, "https://www.googleapis.com/oauth2/v3/certs")
 | ||||
| //		verifier := oidc.NewVerifier("https://accounts.google.com", keySet, config)
 | ||||
| //	keySet := oidc.NewRemoteKeySet(ctx, "https://www.googleapis.com/oauth2/v3/certs")
 | ||||
| //	verifier := oidc.NewVerifier("https://accounts.google.com", keySet, config)
 | ||||
| //
 | ||||
| // Or a static key set (e.g. for testing):
 | ||||
| //
 | ||||
| //		keySet := &oidc.StaticKeySet{PublicKeys: []crypto.PublicKey{pub1, pub2}}
 | ||||
| //		verifier := oidc.NewVerifier("https://accounts.google.com", keySet, config)
 | ||||
| //
 | ||||
| //	keySet := &oidc.StaticKeySet{PublicKeys: []crypto.PublicKey{pub1, pub2}}
 | ||||
| //	verifier := oidc.NewVerifier("https://accounts.google.com", keySet, config)
 | ||||
| func NewVerifier(issuerURL string, keySet KeySet, config *Config) *IDTokenVerifier { | ||||
| 	return &IDTokenVerifier{keySet: keySet, config: config, issuer: issuerURL} | ||||
| } | ||||
|  | @ -120,8 +119,22 @@ type Config struct { | |||
| 	InsecureSkipSignatureCheck bool | ||||
| } | ||||
| 
 | ||||
| // VerifierContext returns an IDTokenVerifier that uses the provider's key set to
 | ||||
| // verify JWTs. As opposed to Verifier, the context is used for all requests to
 | ||||
| // the upstream JWKs endpoint.
 | ||||
| func (p *Provider) VerifierContext(ctx context.Context, config *Config) *IDTokenVerifier { | ||||
| 	return p.newVerifier(NewRemoteKeySet(ctx, p.jwksURL), config) | ||||
| } | ||||
| 
 | ||||
| // Verifier returns an IDTokenVerifier that uses the provider's key set to verify JWTs.
 | ||||
| //
 | ||||
| // The returned verifier uses a background context for all requests to the upstream
 | ||||
| // JWKs endpoint. To control that context, use VerifierContext instead.
 | ||||
| func (p *Provider) Verifier(config *Config) *IDTokenVerifier { | ||||
| 	return p.newVerifier(p.remoteKeySet(), config) | ||||
| } | ||||
| 
 | ||||
| func (p *Provider) newVerifier(keySet KeySet, config *Config) *IDTokenVerifier { | ||||
| 	if len(config.SupportedSigningAlgs) == 0 && len(p.algorithms) > 0 { | ||||
| 		// Make a copy so we don't modify the config values.
 | ||||
| 		cp := &Config{} | ||||
|  | @ -129,7 +142,7 @@ func (p *Provider) Verifier(config *Config) *IDTokenVerifier { | |||
| 		cp.SupportedSigningAlgs = p.algorithms | ||||
| 		config = cp | ||||
| 	} | ||||
| 	return NewVerifier(p.issuer, p.remoteKeySet, config) | ||||
| 	return NewVerifier(p.issuer, keySet, config) | ||||
| } | ||||
| 
 | ||||
| func parseJWT(p string) ([]byte, error) { | ||||
|  | @ -193,19 +206,18 @@ func resolveDistributedClaim(ctx context.Context, verifier *IDTokenVerifier, src | |||
| //
 | ||||
| // See: https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
 | ||||
| //
 | ||||
| //    oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
 | ||||
| //    if err != nil {
 | ||||
| //        // handle error
 | ||||
| //    }
 | ||||
| //	oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
 | ||||
| //	if err != nil {
 | ||||
| //	    // handle error
 | ||||
| //	}
 | ||||
| //
 | ||||
| //    // Extract the ID Token from oauth2 token.
 | ||||
| //    rawIDToken, ok := oauth2Token.Extra("id_token").(string)
 | ||||
| //    if !ok {
 | ||||
| //        // handle error
 | ||||
| //    }
 | ||||
| //
 | ||||
| //    token, err := verifier.Verify(ctx, rawIDToken)
 | ||||
| //	// Extract the ID Token from oauth2 token.
 | ||||
| //	rawIDToken, ok := oauth2Token.Extra("id_token").(string)
 | ||||
| //	if !ok {
 | ||||
| //	    // handle error
 | ||||
| //	}
 | ||||
| //
 | ||||
| //	token, err := verifier.Verify(ctx, rawIDToken)
 | ||||
| func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDToken, error) { | ||||
| 	// Throw out tokens with invalid claims before trying to verify the token. This lets
 | ||||
| 	// us do cheap checks before possibly re-syncing keys.
 | ||||
|  |  | |||
|  | @ -1,10 +0,0 @@ | |||
| language: go | ||||
| 
 | ||||
| install: | ||||
|   - go get -d -v ./... | ||||
|   - go get -u github.com/stretchr/testify/require | ||||
| 
 | ||||
| go: | ||||
|   - 1.7 | ||||
|   - 1.8 | ||||
|   - tip | ||||
|  | @ -1,202 +0,0 @@ | |||
| 
 | ||||
|                                  Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|                         http://www.apache.org/licenses/ | ||||
| 
 | ||||
|    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
| 
 | ||||
|    1. Definitions. | ||||
| 
 | ||||
|       "License" shall mean the terms and conditions for use, reproduction, | ||||
|       and distribution as defined by Sections 1 through 9 of this document. | ||||
| 
 | ||||
|       "Licensor" shall mean the copyright owner or entity authorized by | ||||
|       the copyright owner that is granting the License. | ||||
| 
 | ||||
|       "Legal Entity" shall mean the union of the acting entity and all | ||||
|       other entities that control, are controlled by, or are under common | ||||
|       control with that entity. For the purposes of this definition, | ||||
|       "control" means (i) the power, direct or indirect, to cause the | ||||
|       direction or management of such entity, whether by contract or | ||||
|       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
|       outstanding shares, or (iii) beneficial ownership of such entity. | ||||
| 
 | ||||
|       "You" (or "Your") shall mean an individual or Legal Entity | ||||
|       exercising permissions granted by this License. | ||||
| 
 | ||||
|       "Source" form shall mean the preferred form for making modifications, | ||||
|       including but not limited to software source code, documentation | ||||
|       source, and configuration files. | ||||
| 
 | ||||
|       "Object" form shall mean any form resulting from mechanical | ||||
|       transformation or translation of a Source form, including but | ||||
|       not limited to compiled object code, generated documentation, | ||||
|       and conversions to other media types. | ||||
| 
 | ||||
|       "Work" shall mean the work of authorship, whether in Source or | ||||
|       Object form, made available under the License, as indicated by a | ||||
|       copyright notice that is included in or attached to the work | ||||
|       (an example is provided in the Appendix below). | ||||
| 
 | ||||
|       "Derivative Works" shall mean any work, whether in Source or Object | ||||
|       form, that is based on (or derived from) the Work and for which the | ||||
|       editorial revisions, annotations, elaborations, or other modifications | ||||
|       represent, as a whole, an original work of authorship. For the purposes | ||||
|       of this License, Derivative Works shall not include works that remain | ||||
|       separable from, or merely link (or bind by name) to the interfaces of, | ||||
|       the Work and Derivative Works thereof. | ||||
| 
 | ||||
|       "Contribution" shall mean any work of authorship, including | ||||
|       the original version of the Work and any modifications or additions | ||||
|       to that Work or Derivative Works thereof, that is intentionally | ||||
|       submitted to Licensor for inclusion in the Work by the copyright owner | ||||
|       or by an individual or Legal Entity authorized to submit on behalf of | ||||
|       the copyright owner. For the purposes of this definition, "submitted" | ||||
|       means any form of electronic, verbal, or written communication sent | ||||
|       to the Licensor or its representatives, including but not limited to | ||||
|       communication on electronic mailing lists, source code control systems, | ||||
|       and issue tracking systems that are managed by, or on behalf of, the | ||||
|       Licensor for the purpose of discussing and improving the Work, but | ||||
|       excluding communication that is conspicuously marked or otherwise | ||||
|       designated in writing by the copyright owner as "Not a Contribution." | ||||
| 
 | ||||
|       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||
|       on behalf of whom a Contribution has been received by Licensor and | ||||
|       subsequently incorporated within the Work. | ||||
| 
 | ||||
|    2. Grant of Copyright License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       copyright license to reproduce, prepare Derivative Works of, | ||||
|       publicly display, publicly perform, sublicense, and distribute the | ||||
|       Work and such Derivative Works in Source or Object form. | ||||
| 
 | ||||
|    3. Grant of Patent License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       (except as stated in this section) patent license to make, have made, | ||||
|       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||
|       where such license applies only to those patent claims licensable | ||||
|       by such Contributor that are necessarily infringed by their | ||||
|       Contribution(s) alone or by combination of their Contribution(s) | ||||
|       with the Work to which such Contribution(s) was submitted. If You | ||||
|       institute patent litigation against any entity (including a | ||||
|       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||
|       or a Contribution incorporated within the Work constitutes direct | ||||
|       or contributory patent infringement, then any patent licenses | ||||
|       granted to You under this License for that Work shall terminate | ||||
|       as of the date such litigation is filed. | ||||
| 
 | ||||
|    4. Redistribution. You may reproduce and distribute copies of the | ||||
|       Work or Derivative Works thereof in any medium, with or without | ||||
|       modifications, and in Source or Object form, provided that You | ||||
|       meet the following conditions: | ||||
| 
 | ||||
|       (a) You must give any other recipients of the Work or | ||||
|           Derivative Works a copy of this License; and | ||||
| 
 | ||||
|       (b) You must cause any modified files to carry prominent notices | ||||
|           stating that You changed the files; and | ||||
| 
 | ||||
|       (c) You must retain, in the Source form of any Derivative Works | ||||
|           that You distribute, all copyright, patent, trademark, and | ||||
|           attribution notices from the Source form of the Work, | ||||
|           excluding those notices that do not pertain to any part of | ||||
|           the Derivative Works; and | ||||
| 
 | ||||
|       (d) If the Work includes a "NOTICE" text file as part of its | ||||
|           distribution, then any Derivative Works that You distribute must | ||||
|           include a readable copy of the attribution notices contained | ||||
|           within such NOTICE file, excluding those notices that do not | ||||
|           pertain to any part of the Derivative Works, in at least one | ||||
|           of the following places: within a NOTICE text file distributed | ||||
|           as part of the Derivative Works; within the Source form or | ||||
|           documentation, if provided along with the Derivative Works; or, | ||||
|           within a display generated by the Derivative Works, if and | ||||
|           wherever such third-party notices normally appear. The contents | ||||
|           of the NOTICE file are for informational purposes only and | ||||
|           do not modify the License. You may add Your own attribution | ||||
|           notices within Derivative Works that You distribute, alongside | ||||
|           or as an addendum to the NOTICE text from the Work, provided | ||||
|           that such additional attribution notices cannot be construed | ||||
|           as modifying the License. | ||||
| 
 | ||||
|       You may add Your own copyright statement to Your modifications and | ||||
|       may provide additional or different license terms and conditions | ||||
|       for use, reproduction, or distribution of Your modifications, or | ||||
|       for any such Derivative Works as a whole, provided Your use, | ||||
|       reproduction, and distribution of the Work otherwise complies with | ||||
|       the conditions stated in this License. | ||||
| 
 | ||||
|    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||
|       any Contribution intentionally submitted for inclusion in the Work | ||||
|       by You to the Licensor shall be under the terms and conditions of | ||||
|       this License, without any additional terms or conditions. | ||||
|       Notwithstanding the above, nothing herein shall supersede or modify | ||||
|       the terms of any separate license agreement you may have executed | ||||
|       with Licensor regarding such Contributions. | ||||
| 
 | ||||
|    6. Trademarks. This License does not grant permission to use the trade | ||||
|       names, trademarks, service marks, or product names of the Licensor, | ||||
|       except as required for reasonable and customary use in describing the | ||||
|       origin of the Work and reproducing the content of the NOTICE file. | ||||
| 
 | ||||
|    7. Disclaimer of Warranty. Unless required by applicable law or | ||||
|       agreed to in writing, Licensor provides the Work (and each | ||||
|       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
|       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
|       implied, including, without limitation, any warranties or conditions | ||||
|       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||
|       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||
|       appropriateness of using or redistributing the Work and assume any | ||||
|       risks associated with Your exercise of permissions under this License. | ||||
| 
 | ||||
|    8. Limitation of Liability. In no event and under no legal theory, | ||||
|       whether in tort (including negligence), contract, or otherwise, | ||||
|       unless required by applicable law (such as deliberate and grossly | ||||
|       negligent acts) or agreed to in writing, shall any Contributor be | ||||
|       liable to You for damages, including any direct, indirect, special, | ||||
|       incidental, or consequential damages of any character arising as a | ||||
|       result of this License or out of the use or inability to use the | ||||
|       Work (including but not limited to damages for loss of goodwill, | ||||
|       work stoppage, computer failure or malfunction, or any and all | ||||
|       other commercial damages or losses), even if such Contributor | ||||
|       has been advised of the possibility of such damages. | ||||
| 
 | ||||
|    9. Accepting Warranty or Additional Liability. While redistributing | ||||
|       the Work or Derivative Works thereof, You may choose to offer, | ||||
|       and charge a fee for, acceptance of support, warranty, indemnity, | ||||
|       or other liability obligations and/or rights consistent with this | ||||
|       License. However, in accepting such obligations, You may act only | ||||
|       on Your own behalf and on Your sole responsibility, not on behalf | ||||
|       of any other Contributor, and only if You agree to indemnify, | ||||
|       defend, and hold each Contributor harmless for any liability | ||||
|       incurred by, or claims asserted against, such Contributor by reason | ||||
|       of your accepting any such warranty or additional liability. | ||||
| 
 | ||||
|    END OF TERMS AND CONDITIONS | ||||
| 
 | ||||
|    APPENDIX: How to apply the Apache License to your work. | ||||
| 
 | ||||
|       To apply the Apache License to your work, attach the following | ||||
|       boilerplate notice, with the fields enclosed by brackets "[]" | ||||
|       replaced with your own identifying information. (Don't include | ||||
|       the brackets!)  The text should be enclosed in the appropriate | ||||
|       comment syntax for the file format. We also recommend that a | ||||
|       file or class name and description of purpose be included on the | ||||
|       same "printed page" as the copyright notice for easier | ||||
|       identification within third-party archives. | ||||
| 
 | ||||
|    Copyright [yyyy] [name of copyright owner] | ||||
| 
 | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
| 
 | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
| 
 | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
|  | @ -1,107 +0,0 @@ | |||
| # cachecontrol: HTTP Caching Parser and Interpretation | ||||
| 
 | ||||
| [](https://godoc.org/github.com/pquerna/cachecontrol)[](https://travis-ci.org/pquerna/cachecontrol) | ||||
| 
 | ||||
|   | ||||
| 
 | ||||
| `cachecontrol` implements [RFC 7234](http://tools.ietf.org/html/rfc7234) __Hypertext Transfer Protocol (HTTP/1.1): Caching__.  It does this by parsing the `Cache-Control` and other headers, providing information about requests and responses -- but `cachecontrol` does not implement an actual cache backend, just the control plane to make decisions about if a particular response is cachable. | ||||
| 
 | ||||
| # Usage | ||||
| 
 | ||||
| `cachecontrol.CachableResponse` returns an array of [reasons](https://godoc.org/github.com/pquerna/cachecontrol/cacheobject#Reason) why a response should not be cached and when it expires.  In the case that `len(reasons) == 0`, the response is cachable according to the RFC.  However, some people want non-compliant caches for various business use cases, so each reason is specifically named, so if your cache wants to cache `POST` requests, it can easily do that, but still be RFC compliant in other situations. | ||||
| 
 | ||||
| # Examples | ||||
| 
 | ||||
| ## Can you cache Example.com? | ||||
| 
 | ||||
| ```go | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/pquerna/cachecontrol" | ||||
| 
 | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	req, _ := http.NewRequest("GET", "http://www.example.com/", nil) | ||||
| 
 | ||||
| 	res, _ := http.DefaultClient.Do(req) | ||||
| 	_, _ = ioutil.ReadAll(res.Body) | ||||
| 
 | ||||
| 	reasons, expires, _ := cachecontrol.CachableResponse(req, res, cachecontrol.Options{}) | ||||
| 
 | ||||
| 	fmt.Println("Reasons to not cache: ", reasons) | ||||
| 	fmt.Println("Expiration: ", expires.String()) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ## Can I use this in a high performance caching server? | ||||
| 
 | ||||
| `cachecontrol` is divided into two packages: `cachecontrol` with a high level API, and a lower level `cacheobject` package.  Use [Object](https://godoc.org/github.com/pquerna/cachecontrol/cacheobject#Object) in a high performance use case where you have previously parsed headers containing dates or would like to avoid memory allocations. | ||||
| 
 | ||||
| ```go | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/pquerna/cachecontrol/cacheobject" | ||||
| 
 | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	req, _ := http.NewRequest("GET", "http://www.example.com/", nil) | ||||
| 
 | ||||
| 	res, _ := http.DefaultClient.Do(req) | ||||
| 	_, _ = ioutil.ReadAll(res.Body) | ||||
| 
 | ||||
| 	reqDir, _ := cacheobject.ParseRequestCacheControl(req.Header.Get("Cache-Control")) | ||||
| 
 | ||||
| 	resDir, _ := cacheobject.ParseResponseCacheControl(res.Header.Get("Cache-Control")) | ||||
| 	expiresHeader, _ := http.ParseTime(res.Header.Get("Expires")) | ||||
| 	dateHeader, _ := http.ParseTime(res.Header.Get("Date")) | ||||
| 	lastModifiedHeader, _ := http.ParseTime(res.Header.Get("Last-Modified")) | ||||
| 
 | ||||
| 	obj := cacheobject.Object{ | ||||
| 		RespDirectives:         resDir, | ||||
| 		RespHeaders:            res.Header, | ||||
| 		RespStatusCode:         res.StatusCode, | ||||
| 		RespExpiresHeader:      expiresHeader, | ||||
| 		RespDateHeader:         dateHeader, | ||||
| 		RespLastModifiedHeader: lastModifiedHeader, | ||||
| 
 | ||||
| 		ReqDirectives: reqDir, | ||||
| 		ReqHeaders:    req.Header, | ||||
| 		ReqMethod:     req.Method, | ||||
| 
 | ||||
| 		NowUTC: time.Now().UTC(), | ||||
| 	} | ||||
| 	rv := cacheobject.ObjectResults{} | ||||
| 
 | ||||
| 	cacheobject.CachableObject(&obj, &rv) | ||||
| 	cacheobject.ExpirationObject(&obj, &rv) | ||||
| 
 | ||||
| 	fmt.Println("Errors: ", rv.OutErr) | ||||
| 	fmt.Println("Reasons to not cache: ", rv.OutReasons) | ||||
| 	fmt.Println("Warning headers to add: ", rv.OutWarnings) | ||||
| 	fmt.Println("Expiration: ", rv.OutExpirationTime.String()) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ## Improvements, bugs, adding features, and taking cachecontrol new directions! | ||||
| 
 | ||||
| Please [open issues in Github](https://github.com/pquerna/cachecontrol/issues) for ideas, bugs, and general thoughts.  Pull requests are of course preferred :) | ||||
| 
 | ||||
| # Credits | ||||
| 
 | ||||
| `cachecontrol` has recieved significant contributions from: | ||||
| 
 | ||||
| * [Paul Querna](https://github.com/pquerna)  | ||||
| 
 | ||||
| ## License | ||||
| 
 | ||||
| `cachecontrol` is licensed under the [Apache License, Version 2.0](./LICENSE) | ||||
|  | @ -1,48 +0,0 @@ | |||
| /** | ||||
|  *  Copyright 2015 Paul Querna | ||||
|  * | ||||
|  *  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *  you may not use this file except in compliance with the License. | ||||
|  *  You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  *  Unless required by applicable law or agreed to in writing, software | ||||
|  *  distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *  See the License for the specific language governing permissions and | ||||
|  *  limitations under the License. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| package cachecontrol | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/pquerna/cachecontrol/cacheobject" | ||||
| 
 | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| type Options struct { | ||||
| 	// Set to True for a prviate cache, which is not shared amoung users (eg, in a browser)
 | ||||
| 	// Set to False for a "shared" cache, which is more common in a server context.
 | ||||
| 	PrivateCache bool | ||||
| } | ||||
| 
 | ||||
| // Given an HTTP Request, the future Status Code, and an ResponseWriter,
 | ||||
| // determine the possible reasons a response SHOULD NOT be cached.
 | ||||
| func CachableResponseWriter(req *http.Request, | ||||
| 	statusCode int, | ||||
| 	resp http.ResponseWriter, | ||||
| 	opts Options) ([]cacheobject.Reason, time.Time, error) { | ||||
| 	return cacheobject.UsingRequestResponse(req, statusCode, resp.Header(), opts.PrivateCache) | ||||
| } | ||||
| 
 | ||||
| // Given an HTTP Request and Response, determine the possible reasons a response SHOULD NOT
 | ||||
| // be cached.
 | ||||
| func CachableResponse(req *http.Request, | ||||
| 	resp *http.Response, | ||||
| 	opts Options) ([]cacheobject.Reason, time.Time, error) { | ||||
| 	return cacheobject.UsingRequestResponse(req, resp.StatusCode, resp.Header, opts.PrivateCache) | ||||
| } | ||||
|  | @ -1,546 +0,0 @@ | |||
| /** | ||||
|  *  Copyright 2015 Paul Querna | ||||
|  * | ||||
|  *  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *  you may not use this file except in compliance with the License. | ||||
|  *  You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  *  Unless required by applicable law or agreed to in writing, software | ||||
|  *  distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *  See the License for the specific language governing permissions and | ||||
|  *  limitations under the License. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| package cacheobject | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"math" | ||||
| 	"net/http" | ||||
| 	"net/textproto" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // TODO(pquerna): add extensions from here: http://www.iana.org/assignments/http-cache-directives/http-cache-directives.xhtml
 | ||||
| 
 | ||||
| var ( | ||||
| 	ErrQuoteMismatch         = errors.New("Missing closing quote") | ||||
| 	ErrMaxAgeDeltaSeconds    = errors.New("Failed to parse delta-seconds in `max-age`") | ||||
| 	ErrSMaxAgeDeltaSeconds   = errors.New("Failed to parse delta-seconds in `s-maxage`") | ||||
| 	ErrMaxStaleDeltaSeconds  = errors.New("Failed to parse delta-seconds in `min-fresh`") | ||||
| 	ErrMinFreshDeltaSeconds  = errors.New("Failed to parse delta-seconds in `min-fresh`") | ||||
| 	ErrNoCacheNoArgs         = errors.New("Unexpected argument to `no-cache`") | ||||
| 	ErrNoStoreNoArgs         = errors.New("Unexpected argument to `no-store`") | ||||
| 	ErrNoTransformNoArgs     = errors.New("Unexpected argument to `no-transform`") | ||||
| 	ErrOnlyIfCachedNoArgs    = errors.New("Unexpected argument to `only-if-cached`") | ||||
| 	ErrMustRevalidateNoArgs  = errors.New("Unexpected argument to `must-revalidate`") | ||||
| 	ErrPublicNoArgs          = errors.New("Unexpected argument to `public`") | ||||
| 	ErrProxyRevalidateNoArgs = errors.New("Unexpected argument to `proxy-revalidate`") | ||||
| 	// Experimental
 | ||||
| 	ErrImmutableNoArgs                  = errors.New("Unexpected argument to `immutable`") | ||||
| 	ErrStaleIfErrorDeltaSeconds         = errors.New("Failed to parse delta-seconds in `stale-if-error`") | ||||
| 	ErrStaleWhileRevalidateDeltaSeconds = errors.New("Failed to parse delta-seconds in `stale-while-revalidate`") | ||||
| ) | ||||
| 
 | ||||
| func whitespace(b byte) bool { | ||||
| 	if b == '\t' || b == ' ' { | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func parse(value string, cd cacheDirective) error { | ||||
| 	var err error = nil | ||||
| 	i := 0 | ||||
| 
 | ||||
| 	for i < len(value) && err == nil { | ||||
| 		// eat leading whitespace or commas
 | ||||
| 		if whitespace(value[i]) || value[i] == ',' { | ||||
| 			i++ | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		j := i + 1 | ||||
| 
 | ||||
| 		for j < len(value) { | ||||
| 			if !isToken(value[j]) { | ||||
| 				break | ||||
| 			} | ||||
| 			j++ | ||||
| 		} | ||||
| 
 | ||||
| 		token := strings.ToLower(value[i:j]) | ||||
| 		tokenHasFields := hasFieldNames(token) | ||||
| 		/* | ||||
| 			println("GOT TOKEN:") | ||||
| 			println("	i -> ", i) | ||||
| 			println("	j -> ", j) | ||||
| 			println("	token -> ", token) | ||||
| 		*/ | ||||
| 
 | ||||
| 		if j+1 < len(value) && value[j] == '=' { | ||||
| 			k := j + 1 | ||||
| 			// minimum size two bytes of "", but we let httpUnquote handle it.
 | ||||
| 			if k < len(value) && value[k] == '"' { | ||||
| 				eaten, result := httpUnquote(value[k:]) | ||||
| 				if eaten == -1 { | ||||
| 					return ErrQuoteMismatch | ||||
| 				} | ||||
| 				i = k + eaten | ||||
| 
 | ||||
| 				err = cd.addPair(token, result) | ||||
| 			} else { | ||||
| 				z := k | ||||
| 				for z < len(value) { | ||||
| 					if tokenHasFields { | ||||
| 						if whitespace(value[z]) { | ||||
| 							break | ||||
| 						} | ||||
| 					} else { | ||||
| 						if whitespace(value[z]) || value[z] == ',' { | ||||
| 							break | ||||
| 						} | ||||
| 					} | ||||
| 					z++ | ||||
| 				} | ||||
| 				i = z | ||||
| 
 | ||||
| 				result := value[k:z] | ||||
| 				if result != "" && result[len(result)-1] == ',' { | ||||
| 					result = result[:len(result)-1] | ||||
| 				} | ||||
| 
 | ||||
| 				err = cd.addPair(token, result) | ||||
| 			} | ||||
| 		} else { | ||||
| 			if token != "," { | ||||
| 				err = cd.addToken(token) | ||||
| 			} | ||||
| 			i = j | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // DeltaSeconds specifies a non-negative integer, representing
 | ||||
| // time in seconds: http://tools.ietf.org/html/rfc7234#section-1.2.1
 | ||||
| //
 | ||||
| // When set to -1, this means unset.
 | ||||
| //
 | ||||
| type DeltaSeconds int32 | ||||
| 
 | ||||
| // Parser for delta-seconds, a uint31, more or less:
 | ||||
| // http://tools.ietf.org/html/rfc7234#section-1.2.1
 | ||||
| func parseDeltaSeconds(v string) (DeltaSeconds, error) { | ||||
| 	n, err := strconv.ParseUint(v, 10, 32) | ||||
| 	if err != nil { | ||||
| 		if numError, ok := err.(*strconv.NumError); ok { | ||||
| 			if numError.Err == strconv.ErrRange { | ||||
| 				return DeltaSeconds(math.MaxInt32), nil | ||||
| 			} | ||||
| 		} | ||||
| 		return DeltaSeconds(-1), err | ||||
| 	} else { | ||||
| 		if n > math.MaxInt32 { | ||||
| 			return DeltaSeconds(math.MaxInt32), nil | ||||
| 		} else { | ||||
| 			return DeltaSeconds(n), nil | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Fields present in a header.
 | ||||
| type FieldNames map[string]bool | ||||
| 
 | ||||
| // internal interface for shared methods of RequestCacheDirectives and ResponseCacheDirectives
 | ||||
| type cacheDirective interface { | ||||
| 	addToken(s string) error | ||||
| 	addPair(s string, v string) error | ||||
| } | ||||
| 
 | ||||
| // LOW LEVEL API: Repersentation of possible request directives in a `Cache-Control` header: http://tools.ietf.org/html/rfc7234#section-5.2.1
 | ||||
| //
 | ||||
| // Note: Many fields will be `nil` in practice.
 | ||||
| //
 | ||||
| type RequestCacheDirectives struct { | ||||
| 
 | ||||
| 	// max-age(delta seconds): http://tools.ietf.org/html/rfc7234#section-5.2.1.1
 | ||||
| 	//
 | ||||
| 	// The "max-age" request directive indicates that the client is
 | ||||
| 	// unwilling to accept a response whose age is greater than the
 | ||||
| 	// specified number of seconds.  Unless the max-stale request directive
 | ||||
| 	// is also present, the client is not willing to accept a stale
 | ||||
| 	// response.
 | ||||
| 	MaxAge DeltaSeconds | ||||
| 
 | ||||
| 	// max-stale(delta seconds): http://tools.ietf.org/html/rfc7234#section-5.2.1.2
 | ||||
| 	//
 | ||||
| 	// The "max-stale" request directive indicates that the client is
 | ||||
| 	// willing to accept a response that has exceeded its freshness
 | ||||
| 	// lifetime.  If max-stale is assigned a value, then the client is
 | ||||
| 	// willing to accept a response that has exceeded its freshness lifetime
 | ||||
| 	// by no more than the specified number of seconds.  If no value is
 | ||||
| 	// assigned to max-stale, then the client is willing to accept a stale
 | ||||
| 	// response of any age.
 | ||||
| 	MaxStale DeltaSeconds | ||||
| 
 | ||||
| 	// min-fresh(delta seconds): http://tools.ietf.org/html/rfc7234#section-5.2.1.3
 | ||||
| 	//
 | ||||
| 	// The "min-fresh" request directive indicates that the client is
 | ||||
| 	// willing to accept a response whose freshness lifetime is no less than
 | ||||
| 	// its current age plus the specified time in seconds.  That is, the
 | ||||
| 	// client wants a response that will still be fresh for at least the
 | ||||
| 	// specified number of seconds.
 | ||||
| 	MinFresh DeltaSeconds | ||||
| 
 | ||||
| 	// no-cache(bool): http://tools.ietf.org/html/rfc7234#section-5.2.1.4
 | ||||
| 	//
 | ||||
| 	// The "no-cache" request directive indicates that a cache MUST NOT use
 | ||||
| 	// a stored response to satisfy the request without successful
 | ||||
| 	// validation on the origin server.
 | ||||
| 	NoCache bool | ||||
| 
 | ||||
| 	// no-store(bool): http://tools.ietf.org/html/rfc7234#section-5.2.1.5
 | ||||
| 	//
 | ||||
| 	// The "no-store" request directive indicates that a cache MUST NOT
 | ||||
| 	// store any part of either this request or any response to it.  This
 | ||||
| 	// directive applies to both private and shared caches.
 | ||||
| 	NoStore bool | ||||
| 
 | ||||
| 	// no-transform(bool): http://tools.ietf.org/html/rfc7234#section-5.2.1.6
 | ||||
| 	//
 | ||||
| 	// The "no-transform" request directive indicates that an intermediary
 | ||||
| 	// (whether or not it implements a cache) MUST NOT transform the
 | ||||
| 	// payload, as defined in Section 5.7.2 of RFC7230.
 | ||||
| 	NoTransform bool | ||||
| 
 | ||||
| 	// only-if-cached(bool): http://tools.ietf.org/html/rfc7234#section-5.2.1.7
 | ||||
| 	//
 | ||||
| 	// The "only-if-cached" request directive indicates that the client only
 | ||||
| 	// wishes to obtain a stored response.
 | ||||
| 	OnlyIfCached bool | ||||
| 
 | ||||
| 	// Extensions: http://tools.ietf.org/html/rfc7234#section-5.2.3
 | ||||
| 	//
 | ||||
| 	// The Cache-Control header field can be extended through the use of one
 | ||||
| 	// or more cache-extension tokens, each with an optional value.  A cache
 | ||||
| 	// MUST ignore unrecognized cache directives.
 | ||||
| 	Extensions []string | ||||
| } | ||||
| 
 | ||||
| func (cd *RequestCacheDirectives) addToken(token string) error { | ||||
| 	var err error = nil | ||||
| 
 | ||||
| 	switch token { | ||||
| 	case "max-age": | ||||
| 		err = ErrMaxAgeDeltaSeconds | ||||
| 	case "max-stale": | ||||
| 		err = ErrMaxStaleDeltaSeconds | ||||
| 	case "min-fresh": | ||||
| 		err = ErrMinFreshDeltaSeconds | ||||
| 	case "no-cache": | ||||
| 		cd.NoCache = true | ||||
| 	case "no-store": | ||||
| 		cd.NoStore = true | ||||
| 	case "no-transform": | ||||
| 		cd.NoTransform = true | ||||
| 	case "only-if-cached": | ||||
| 		cd.OnlyIfCached = true | ||||
| 	default: | ||||
| 		cd.Extensions = append(cd.Extensions, token) | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (cd *RequestCacheDirectives) addPair(token string, v string) error { | ||||
| 	var err error = nil | ||||
| 
 | ||||
| 	switch token { | ||||
| 	case "max-age": | ||||
| 		cd.MaxAge, err = parseDeltaSeconds(v) | ||||
| 		if err != nil { | ||||
| 			err = ErrMaxAgeDeltaSeconds | ||||
| 		} | ||||
| 	case "max-stale": | ||||
| 		cd.MaxStale, err = parseDeltaSeconds(v) | ||||
| 		if err != nil { | ||||
| 			err = ErrMaxStaleDeltaSeconds | ||||
| 		} | ||||
| 	case "min-fresh": | ||||
| 		cd.MinFresh, err = parseDeltaSeconds(v) | ||||
| 		if err != nil { | ||||
| 			err = ErrMinFreshDeltaSeconds | ||||
| 		} | ||||
| 	case "no-cache": | ||||
| 		err = ErrNoCacheNoArgs | ||||
| 	case "no-store": | ||||
| 		err = ErrNoStoreNoArgs | ||||
| 	case "no-transform": | ||||
| 		err = ErrNoTransformNoArgs | ||||
| 	case "only-if-cached": | ||||
| 		err = ErrOnlyIfCachedNoArgs | ||||
| 	default: | ||||
| 		// TODO(pquerna): this sucks, making user re-parse
 | ||||
| 		cd.Extensions = append(cd.Extensions, token+"="+v) | ||||
| 	} | ||||
| 
 | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // LOW LEVEL API: Parses a Cache Control Header from a Request into a set of directives.
 | ||||
| func ParseRequestCacheControl(value string) (*RequestCacheDirectives, error) { | ||||
| 	cd := &RequestCacheDirectives{ | ||||
| 		MaxAge:   -1, | ||||
| 		MaxStale: -1, | ||||
| 		MinFresh: -1, | ||||
| 	} | ||||
| 
 | ||||
| 	err := parse(value, cd) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return cd, nil | ||||
| } | ||||
| 
 | ||||
| // LOW LEVEL API: Repersentation of possible response directives in a `Cache-Control` header: http://tools.ietf.org/html/rfc7234#section-5.2.2
 | ||||
| //
 | ||||
| // Note: Many fields will be `nil` in practice.
 | ||||
| //
 | ||||
| type ResponseCacheDirectives struct { | ||||
| 
 | ||||
| 	// must-revalidate(bool): http://tools.ietf.org/html/rfc7234#section-5.2.2.1
 | ||||
| 	//
 | ||||
| 	// The "must-revalidate" response directive indicates that once it has
 | ||||
| 	// become stale, a cache MUST NOT use the response to satisfy subsequent
 | ||||
| 	// requests without successful validation on the origin server.
 | ||||
| 	MustRevalidate bool | ||||
| 
 | ||||
| 	// no-cache(FieldName): http://tools.ietf.org/html/rfc7234#section-5.2.2.2
 | ||||
| 	//
 | ||||
| 	// The "no-cache" response directive indicates that the response MUST
 | ||||
| 	// NOT be used to satisfy a subsequent request without successful
 | ||||
| 	// validation on the origin server.
 | ||||
| 	//
 | ||||
| 	// If the no-cache response directive specifies one or more field-names,
 | ||||
| 	// then a cache MAY use the response to satisfy a subsequent request,
 | ||||
| 	// subject to any other restrictions on caching.  However, any header
 | ||||
| 	// fields in the response that have the field-name(s) listed MUST NOT be
 | ||||
| 	// sent in the response to a subsequent request without successful
 | ||||
| 	// revalidation with the origin server.
 | ||||
| 	NoCache FieldNames | ||||
| 
 | ||||
| 	// no-cache(cast-to-bool): http://tools.ietf.org/html/rfc7234#section-5.2.2.2
 | ||||
| 	//
 | ||||
| 	// While the RFC defines optional field-names on a no-cache directive,
 | ||||
| 	// many applications only want to know if any no-cache directives were
 | ||||
| 	// present at all.
 | ||||
| 	NoCachePresent bool | ||||
| 
 | ||||
| 	// no-store(bool): http://tools.ietf.org/html/rfc7234#section-5.2.2.3
 | ||||
| 	//
 | ||||
| 	// The "no-store" request directive indicates that a cache MUST NOT
 | ||||
| 	// store any part of either this request or any response to it.  This
 | ||||
| 	// directive applies to both private and shared caches.
 | ||||
| 	NoStore bool | ||||
| 
 | ||||
| 	// no-transform(bool): http://tools.ietf.org/html/rfc7234#section-5.2.2.4
 | ||||
| 	//
 | ||||
| 	// The "no-transform" response directive indicates that an intermediary
 | ||||
| 	// (regardless of whether it implements a cache) MUST NOT transform the
 | ||||
| 	// payload, as defined in Section 5.7.2 of RFC7230.
 | ||||
| 	NoTransform bool | ||||
| 
 | ||||
| 	// public(bool): http://tools.ietf.org/html/rfc7234#section-5.2.2.5
 | ||||
| 	//
 | ||||
| 	// The "public" response directive indicates that any cache MAY store
 | ||||
| 	// the response, even if the response would normally be non-cacheable or
 | ||||
| 	// cacheable only within a private cache.
 | ||||
| 	Public bool | ||||
| 
 | ||||
| 	// private(FieldName): http://tools.ietf.org/html/rfc7234#section-5.2.2.6
 | ||||
| 	//
 | ||||
| 	// The "private" response directive indicates that the response message
 | ||||
| 	// is intended for a single user and MUST NOT be stored by a shared
 | ||||
| 	// cache.  A private cache MAY store the response and reuse it for later
 | ||||
| 	// requests, even if the response would normally be non-cacheable.
 | ||||
| 	//
 | ||||
| 	// If the private response directive specifies one or more field-names,
 | ||||
| 	// this requirement is limited to the field-values associated with the
 | ||||
| 	// listed response header fields.  That is, a shared cache MUST NOT
 | ||||
| 	// store the specified field-names(s), whereas it MAY store the
 | ||||
| 	// remainder of the response message.
 | ||||
| 	Private FieldNames | ||||
| 
 | ||||
| 	// private(cast-to-bool): http://tools.ietf.org/html/rfc7234#section-5.2.2.6
 | ||||
| 	//
 | ||||
| 	// While the RFC defines optional field-names on a private directive,
 | ||||
| 	// many applications only want to know if any private directives were
 | ||||
| 	// present at all.
 | ||||
| 	PrivatePresent bool | ||||
| 
 | ||||
| 	// proxy-revalidate(bool): http://tools.ietf.org/html/rfc7234#section-5.2.2.7
 | ||||
| 	//
 | ||||
| 	// The "proxy-revalidate" response directive has the same meaning as the
 | ||||
| 	// must-revalidate response directive, except that it does not apply to
 | ||||
| 	// private caches.
 | ||||
| 	ProxyRevalidate bool | ||||
| 
 | ||||
| 	// max-age(delta seconds): http://tools.ietf.org/html/rfc7234#section-5.2.2.8
 | ||||
| 	//
 | ||||
| 	// The "max-age" response directive indicates that the response is to be
 | ||||
| 	// considered stale after its age is greater than the specified number
 | ||||
| 	// of seconds.
 | ||||
| 	MaxAge DeltaSeconds | ||||
| 
 | ||||
| 	// s-maxage(delta seconds): http://tools.ietf.org/html/rfc7234#section-5.2.2.9
 | ||||
| 	//
 | ||||
| 	// The "s-maxage" response directive indicates that, in shared caches,
 | ||||
| 	// the maximum age specified by this directive overrides the maximum age
 | ||||
| 	// specified by either the max-age directive or the Expires header
 | ||||
| 	// field.  The s-maxage directive also implies the semantics of the
 | ||||
| 	// proxy-revalidate response directive.
 | ||||
| 	SMaxAge DeltaSeconds | ||||
| 
 | ||||
| 	////
 | ||||
| 	// Experimental features
 | ||||
| 	// - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Extension_Cache-Control_directives
 | ||||
| 	// - https://www.fastly.com/blog/stale-while-revalidate-stale-if-error-available-today
 | ||||
| 	////
 | ||||
| 
 | ||||
| 	// immutable(cast-to-bool): experimental feature
 | ||||
| 	Immutable bool | ||||
| 
 | ||||
| 	// stale-if-error(delta seconds): experimental feature
 | ||||
| 	StaleIfError DeltaSeconds | ||||
| 
 | ||||
| 	// stale-while-revalidate(delta seconds): experimental feature
 | ||||
| 	StaleWhileRevalidate DeltaSeconds | ||||
| 
 | ||||
| 	// Extensions: http://tools.ietf.org/html/rfc7234#section-5.2.3
 | ||||
| 	//
 | ||||
| 	// The Cache-Control header field can be extended through the use of one
 | ||||
| 	// or more cache-extension tokens, each with an optional value.  A cache
 | ||||
| 	// MUST ignore unrecognized cache directives.
 | ||||
| 	Extensions []string | ||||
| } | ||||
| 
 | ||||
| // LOW LEVEL API: Parses a Cache Control Header from a Response into a set of directives.
 | ||||
| func ParseResponseCacheControl(value string) (*ResponseCacheDirectives, error) { | ||||
| 	cd := &ResponseCacheDirectives{ | ||||
| 		MaxAge:  -1, | ||||
| 		SMaxAge: -1, | ||||
| 		// Exerimantal stale timeouts
 | ||||
| 		StaleIfError:         -1, | ||||
| 		StaleWhileRevalidate: -1, | ||||
| 	} | ||||
| 
 | ||||
| 	err := parse(value, cd) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return cd, nil | ||||
| } | ||||
| 
 | ||||
| func (cd *ResponseCacheDirectives) addToken(token string) error { | ||||
| 	var err error = nil | ||||
| 	switch token { | ||||
| 	case "must-revalidate": | ||||
| 		cd.MustRevalidate = true | ||||
| 	case "no-cache": | ||||
| 		cd.NoCachePresent = true | ||||
| 	case "no-store": | ||||
| 		cd.NoStore = true | ||||
| 	case "no-transform": | ||||
| 		cd.NoTransform = true | ||||
| 	case "public": | ||||
| 		cd.Public = true | ||||
| 	case "private": | ||||
| 		cd.PrivatePresent = true | ||||
| 	case "proxy-revalidate": | ||||
| 		cd.ProxyRevalidate = true | ||||
| 	case "max-age": | ||||
| 		err = ErrMaxAgeDeltaSeconds | ||||
| 	case "s-maxage": | ||||
| 		err = ErrSMaxAgeDeltaSeconds | ||||
| 	// Experimental
 | ||||
| 	case "immutable": | ||||
| 		cd.Immutable = true | ||||
| 	case "stale-if-error": | ||||
| 		err = ErrMaxAgeDeltaSeconds | ||||
| 	case "stale-while-revalidate": | ||||
| 		err = ErrMaxAgeDeltaSeconds | ||||
| 	default: | ||||
| 		cd.Extensions = append(cd.Extensions, token) | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func hasFieldNames(token string) bool { | ||||
| 	switch token { | ||||
| 	case "no-cache": | ||||
| 		return true | ||||
| 	case "private": | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func (cd *ResponseCacheDirectives) addPair(token string, v string) error { | ||||
| 	var err error = nil | ||||
| 
 | ||||
| 	switch token { | ||||
| 	case "must-revalidate": | ||||
| 		err = ErrMustRevalidateNoArgs | ||||
| 	case "no-cache": | ||||
| 		cd.NoCachePresent = true | ||||
| 		tokens := strings.Split(v, ",") | ||||
| 		if cd.NoCache == nil { | ||||
| 			cd.NoCache = make(FieldNames) | ||||
| 		} | ||||
| 		for _, t := range tokens { | ||||
| 			k := http.CanonicalHeaderKey(textproto.TrimString(t)) | ||||
| 			cd.NoCache[k] = true | ||||
| 		} | ||||
| 	case "no-store": | ||||
| 		err = ErrNoStoreNoArgs | ||||
| 	case "no-transform": | ||||
| 		err = ErrNoTransformNoArgs | ||||
| 	case "public": | ||||
| 		err = ErrPublicNoArgs | ||||
| 	case "private": | ||||
| 		cd.PrivatePresent = true | ||||
| 		tokens := strings.Split(v, ",") | ||||
| 		if cd.Private == nil { | ||||
| 			cd.Private = make(FieldNames) | ||||
| 		} | ||||
| 		for _, t := range tokens { | ||||
| 			k := http.CanonicalHeaderKey(textproto.TrimString(t)) | ||||
| 			cd.Private[k] = true | ||||
| 		} | ||||
| 	case "proxy-revalidate": | ||||
| 		err = ErrProxyRevalidateNoArgs | ||||
| 	case "max-age": | ||||
| 		cd.MaxAge, err = parseDeltaSeconds(v) | ||||
| 	case "s-maxage": | ||||
| 		cd.SMaxAge, err = parseDeltaSeconds(v) | ||||
| 	// Experimental
 | ||||
| 	case "immutable": | ||||
| 		err = ErrImmutableNoArgs | ||||
| 	case "stale-if-error": | ||||
| 		cd.StaleIfError, err = parseDeltaSeconds(v) | ||||
| 	case "stale-while-revalidate": | ||||
| 		cd.StaleWhileRevalidate, err = parseDeltaSeconds(v) | ||||
| 	default: | ||||
| 		// TODO(pquerna): this sucks, making user re-parse, and its technically not 'quoted' like the original,
 | ||||
| 		// but this is still easier, just a SplitN on "="
 | ||||
| 		cd.Extensions = append(cd.Extensions, token+"="+v) | ||||
| 	} | ||||
| 
 | ||||
| 	return err | ||||
| } | ||||
|  | @ -1,93 +0,0 @@ | |||
| // Copyright 2009 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package cacheobject | ||||
| 
 | ||||
| // This file deals with lexical matters of HTTP
 | ||||
| 
 | ||||
| func isSeparator(c byte) bool { | ||||
| 	switch c { | ||||
| 	case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t': | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func isCtl(c byte) bool { return (0 <= c && c <= 31) || c == 127 } | ||||
| 
 | ||||
| func isChar(c byte) bool { return 0 <= c && c <= 127 } | ||||
| 
 | ||||
| func isAnyText(c byte) bool { return !isCtl(c) } | ||||
| 
 | ||||
| func isQdText(c byte) bool { return isAnyText(c) && c != '"' } | ||||
| 
 | ||||
| func isToken(c byte) bool { return isChar(c) && !isCtl(c) && !isSeparator(c) } | ||||
| 
 | ||||
| // Valid escaped sequences are not specified in RFC 2616, so for now, we assume
 | ||||
| // that they coincide with the common sense ones used by GO. Malformed
 | ||||
| // characters should probably not be treated as errors by a robust (forgiving)
 | ||||
| // parser, so we replace them with the '?' character.
 | ||||
| func httpUnquotePair(b byte) byte { | ||||
| 	// skip the first byte, which should always be '\'
 | ||||
| 	switch b { | ||||
| 	case 'a': | ||||
| 		return '\a' | ||||
| 	case 'b': | ||||
| 		return '\b' | ||||
| 	case 'f': | ||||
| 		return '\f' | ||||
| 	case 'n': | ||||
| 		return '\n' | ||||
| 	case 'r': | ||||
| 		return '\r' | ||||
| 	case 't': | ||||
| 		return '\t' | ||||
| 	case 'v': | ||||
| 		return '\v' | ||||
| 	case '\\': | ||||
| 		return '\\' | ||||
| 	case '\'': | ||||
| 		return '\'' | ||||
| 	case '"': | ||||
| 		return '"' | ||||
| 	} | ||||
| 	return '?' | ||||
| } | ||||
| 
 | ||||
| // raw must begin with a valid quoted string. Only the first quoted string is
 | ||||
| // parsed and is unquoted in result. eaten is the number of bytes parsed, or -1
 | ||||
| // upon failure.
 | ||||
| func httpUnquote(raw string) (eaten int, result string) { | ||||
| 	buf := make([]byte, len(raw)) | ||||
| 	if raw[0] != '"' { | ||||
| 		return -1, "" | ||||
| 	} | ||||
| 	eaten = 1 | ||||
| 	j := 0 // # of bytes written in buf
 | ||||
| 	for i := 1; i < len(raw); i++ { | ||||
| 		switch b := raw[i]; b { | ||||
| 		case '"': | ||||
| 			eaten++ | ||||
| 			buf = buf[0:j] | ||||
| 			return i + 1, string(buf) | ||||
| 		case '\\': | ||||
| 			if len(raw) < i+2 { | ||||
| 				return -1, "" | ||||
| 			} | ||||
| 			buf[j] = httpUnquotePair(raw[i+1]) | ||||
| 			eaten += 2 | ||||
| 			j++ | ||||
| 			i++ | ||||
| 		default: | ||||
| 			if isQdText(b) { | ||||
| 				buf[j] = b | ||||
| 			} else { | ||||
| 				buf[j] = '?' | ||||
| 			} | ||||
| 			eaten++ | ||||
| 			j++ | ||||
| 		} | ||||
| 	} | ||||
| 	return -1, "" | ||||
| } | ||||
|  | @ -1,387 +0,0 @@ | |||
| /** | ||||
|  *  Copyright 2015 Paul Querna | ||||
|  * | ||||
|  *  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *  you may not use this file except in compliance with the License. | ||||
|  *  You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  *  Unless required by applicable law or agreed to in writing, software | ||||
|  *  distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *  See the License for the specific language governing permissions and | ||||
|  *  limitations under the License. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| package cacheobject | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // LOW LEVEL API: Repersents a potentially cachable HTTP object.
 | ||||
| //
 | ||||
| // This struct is designed to be serialized efficiently, so in a high
 | ||||
| // performance caching server, things like Date-Strings don't need to be
 | ||||
| // parsed for every use of a cached object.
 | ||||
| type Object struct { | ||||
| 	CacheIsPrivate bool | ||||
| 
 | ||||
| 	RespDirectives         *ResponseCacheDirectives | ||||
| 	RespHeaders            http.Header | ||||
| 	RespStatusCode         int | ||||
| 	RespExpiresHeader      time.Time | ||||
| 	RespDateHeader         time.Time | ||||
| 	RespLastModifiedHeader time.Time | ||||
| 
 | ||||
| 	ReqDirectives *RequestCacheDirectives | ||||
| 	ReqHeaders    http.Header | ||||
| 	ReqMethod     string | ||||
| 
 | ||||
| 	NowUTC time.Time | ||||
| } | ||||
| 
 | ||||
| // LOW LEVEL API: Repersents the results of examinig an Object with
 | ||||
| // CachableObject and ExpirationObject.
 | ||||
| //
 | ||||
| // TODO(pquerna): decide if this is a good idea or bad
 | ||||
| type ObjectResults struct { | ||||
| 	OutReasons        []Reason | ||||
| 	OutWarnings       []Warning | ||||
| 	OutExpirationTime time.Time | ||||
| 	OutErr            error | ||||
| } | ||||
| 
 | ||||
| // LOW LEVEL API: Check if a object is cachable.
 | ||||
| func CachableObject(obj *Object, rv *ObjectResults) { | ||||
| 	rv.OutReasons = nil | ||||
| 	rv.OutWarnings = nil | ||||
| 	rv.OutErr = nil | ||||
| 
 | ||||
| 	switch obj.ReqMethod { | ||||
| 	case "GET": | ||||
| 		break | ||||
| 	case "HEAD": | ||||
| 		break | ||||
| 	case "POST": | ||||
| 		/** | ||||
| 		  POST: http://tools.ietf.org/html/rfc7231#section-4.3.3
 | ||||
| 
 | ||||
| 		  Responses to POST requests are only cacheable when they include | ||||
| 		  explicit freshness information (see Section 4.2.1 of [RFC7234]). | ||||
| 		  However, POST caching is not widely implemented.  For cases where an | ||||
| 		  origin server wishes the client to be able to cache the result of a | ||||
| 		  POST in a way that can be reused by a later GET, the origin server | ||||
| 		  MAY send a 200 (OK) response containing the result and a | ||||
| 		  Content-Location header field that has the same value as the POST's | ||||
| 		  effective request URI (Section 3.1.4.2). | ||||
| 		*/ | ||||
| 		if !hasFreshness(obj.ReqDirectives, obj.RespDirectives, obj.RespHeaders, obj.RespExpiresHeader, obj.CacheIsPrivate) { | ||||
| 			rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodPOST) | ||||
| 		} | ||||
| 
 | ||||
| 	case "PUT": | ||||
| 		rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodPUT) | ||||
| 
 | ||||
| 	case "DELETE": | ||||
| 		rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodDELETE) | ||||
| 
 | ||||
| 	case "CONNECT": | ||||
| 		rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodCONNECT) | ||||
| 
 | ||||
| 	case "OPTIONS": | ||||
| 		rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodOPTIONS) | ||||
| 
 | ||||
| 	case "TRACE": | ||||
| 		rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodTRACE) | ||||
| 
 | ||||
| 	// HTTP Extension Methods: http://www.iana.org/assignments/http-methods/http-methods.xhtml
 | ||||
| 	//
 | ||||
| 	// To my knowledge, none of them are cachable. Please open a ticket if this is not the case!
 | ||||
| 	//
 | ||||
| 	default: | ||||
| 		rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodUnkown) | ||||
| 	} | ||||
| 
 | ||||
| 	if obj.ReqDirectives.NoStore { | ||||
| 		rv.OutReasons = append(rv.OutReasons, ReasonRequestNoStore) | ||||
| 	} | ||||
| 
 | ||||
| 	// Storing Responses to Authenticated Requests: http://tools.ietf.org/html/rfc7234#section-3.2
 | ||||
| 	authz := obj.ReqHeaders.Get("Authorization") | ||||
| 	if authz != "" { | ||||
| 		if obj.RespDirectives.MustRevalidate || | ||||
| 			obj.RespDirectives.Public || | ||||
| 			obj.RespDirectives.SMaxAge != -1 { | ||||
| 			// Expires of some kind present, this is potentially OK.
 | ||||
| 		} else { | ||||
| 			rv.OutReasons = append(rv.OutReasons, ReasonRequestAuthorizationHeader) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if obj.RespDirectives.PrivatePresent && !obj.CacheIsPrivate { | ||||
| 		rv.OutReasons = append(rv.OutReasons, ReasonResponsePrivate) | ||||
| 	} | ||||
| 
 | ||||
| 	if obj.RespDirectives.NoStore { | ||||
| 		rv.OutReasons = append(rv.OutReasons, ReasonResponseNoStore) | ||||
| 	} | ||||
| 
 | ||||
| 	/* | ||||
| 	   the response either: | ||||
| 
 | ||||
| 	     *  contains an Expires header field (see Section 5.3), or | ||||
| 
 | ||||
| 	     *  contains a max-age response directive (see Section 5.2.2.8), or | ||||
| 
 | ||||
| 	     *  contains a s-maxage response directive (see Section 5.2.2.9) | ||||
| 	        and the cache is shared, or | ||||
| 
 | ||||
| 	     *  contains a Cache Control Extension (see Section 5.2.3) that | ||||
| 	        allows it to be cached, or | ||||
| 
 | ||||
| 	     *  has a status code that is defined as cacheable by default (see | ||||
| 	        Section 4.2.2), or | ||||
| 
 | ||||
| 	     *  contains a public response directive (see Section 5.2.2.5). | ||||
| 	*/ | ||||
| 
 | ||||
| 	expires := obj.RespHeaders.Get("Expires") != "" | ||||
| 	statusCachable := cachableStatusCode(obj.RespStatusCode) | ||||
| 
 | ||||
| 	if expires || | ||||
| 		obj.RespDirectives.MaxAge != -1 || | ||||
| 		(obj.RespDirectives.SMaxAge != -1 && !obj.CacheIsPrivate) || | ||||
| 		statusCachable || | ||||
| 		obj.RespDirectives.Public { | ||||
| 		/* cachable by default, at least one of the above conditions was true */ | ||||
| 	} else { | ||||
| 		rv.OutReasons = append(rv.OutReasons, ReasonResponseUncachableByDefault) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| var twentyFourHours = time.Duration(24 * time.Hour) | ||||
| 
 | ||||
| const debug = false | ||||
| 
 | ||||
| // LOW LEVEL API: Update an objects expiration time.
 | ||||
| func ExpirationObject(obj *Object, rv *ObjectResults) { | ||||
| 	/** | ||||
| 	 * Okay, lets calculate Freshness/Expiration now. woo: | ||||
| 	 *  http://tools.ietf.org/html/rfc7234#section-4.2
 | ||||
| 	 */ | ||||
| 
 | ||||
| 	/* | ||||
| 	   o  If the cache is shared and the s-maxage response directive | ||||
| 	      (Section 5.2.2.9) is present, use its value, or | ||||
| 
 | ||||
| 	   o  If the max-age response directive (Section 5.2.2.8) is present, | ||||
| 	      use its value, or | ||||
| 
 | ||||
| 	   o  If the Expires response header field (Section 5.3) is present, use | ||||
| 	      its value minus the value of the Date response header field, or | ||||
| 
 | ||||
| 	   o  Otherwise, no explicit expiration time is present in the response. | ||||
| 	      A heuristic freshness lifetime might be applicable; see | ||||
| 	      Section 4.2.2. | ||||
| 	*/ | ||||
| 
 | ||||
| 	var expiresTime time.Time | ||||
| 
 | ||||
| 	if obj.RespDirectives.SMaxAge != -1 && !obj.CacheIsPrivate { | ||||
| 		expiresTime = obj.NowUTC.Add(time.Second * time.Duration(obj.RespDirectives.SMaxAge)) | ||||
| 	} else if obj.RespDirectives.MaxAge != -1 { | ||||
| 		expiresTime = obj.NowUTC.UTC().Add(time.Second * time.Duration(obj.RespDirectives.MaxAge)) | ||||
| 	} else if !obj.RespExpiresHeader.IsZero() { | ||||
| 		serverDate := obj.RespDateHeader | ||||
| 		if serverDate.IsZero() { | ||||
| 			// common enough case when a Date: header has not yet been added to an
 | ||||
| 			// active response.
 | ||||
| 			serverDate = obj.NowUTC | ||||
| 		} | ||||
| 		expiresTime = obj.NowUTC.Add(obj.RespExpiresHeader.Sub(serverDate)) | ||||
| 	} else if !obj.RespLastModifiedHeader.IsZero() { | ||||
| 		// heuristic freshness lifetime
 | ||||
| 		rv.OutWarnings = append(rv.OutWarnings, WarningHeuristicExpiration) | ||||
| 
 | ||||
| 		// http://httpd.apache.org/docs/2.4/mod/mod_cache.html#cachelastmodifiedfactor
 | ||||
| 		// CacheMaxExpire defaults to 24 hours
 | ||||
| 		// CacheLastModifiedFactor: is 0.1
 | ||||
| 		//
 | ||||
| 		// expiry-period = MIN(time-since-last-modified-date * factor, 24 hours)
 | ||||
| 		//
 | ||||
| 		// obj.NowUTC
 | ||||
| 
 | ||||
| 		since := obj.RespLastModifiedHeader.Sub(obj.NowUTC) | ||||
| 		since = time.Duration(float64(since) * -0.1) | ||||
| 
 | ||||
| 		if since > twentyFourHours { | ||||
| 			expiresTime = obj.NowUTC.Add(twentyFourHours) | ||||
| 		} else { | ||||
| 			expiresTime = obj.NowUTC.Add(since) | ||||
| 		} | ||||
| 
 | ||||
| 		if debug { | ||||
| 			println("Now UTC: ", obj.NowUTC.String()) | ||||
| 			println("Last-Modified: ", obj.RespLastModifiedHeader.String()) | ||||
| 			println("Since: ", since.String()) | ||||
| 			println("TwentyFourHours: ", twentyFourHours.String()) | ||||
| 			println("Expiration: ", expiresTime.String()) | ||||
| 		} | ||||
| 	} else { | ||||
| 		// TODO(pquerna): what should the default behavoir be for expiration time?
 | ||||
| 	} | ||||
| 
 | ||||
| 	rv.OutExpirationTime = expiresTime | ||||
| } | ||||
| 
 | ||||
| // Evaluate cachability based on an HTTP request, and parts of the response.
 | ||||
| func UsingRequestResponse(req *http.Request, | ||||
| 	statusCode int, | ||||
| 	respHeaders http.Header, | ||||
| 	privateCache bool) ([]Reason, time.Time, error) { | ||||
| 	reasons, time, _, _, err := UsingRequestResponseWithObject(req, statusCode, respHeaders, privateCache) | ||||
| 	return reasons, time, err | ||||
| } | ||||
| 
 | ||||
| // Evaluate cachability based on an HTTP request, and parts of the response.
 | ||||
| // Returns the parsed Object as well.
 | ||||
| func UsingRequestResponseWithObject(req *http.Request, | ||||
| 	statusCode int, | ||||
| 	respHeaders http.Header, | ||||
| 	privateCache bool) ([]Reason, time.Time, []Warning, *Object, error) { | ||||
| 	var reqHeaders http.Header | ||||
| 	var reqMethod string | ||||
| 
 | ||||
| 	var reqDir *RequestCacheDirectives = nil | ||||
| 	respDir, err := ParseResponseCacheControl(respHeaders.Get("Cache-Control")) | ||||
| 	if err != nil { | ||||
| 		return nil, time.Time{}, nil, nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if req != nil { | ||||
| 		reqDir, err = ParseRequestCacheControl(req.Header.Get("Cache-Control")) | ||||
| 		if err != nil { | ||||
| 			return nil, time.Time{}, nil, nil, err | ||||
| 		} | ||||
| 		reqHeaders = req.Header | ||||
| 		reqMethod = req.Method | ||||
| 	} | ||||
| 
 | ||||
| 	var expiresHeader time.Time | ||||
| 	var dateHeader time.Time | ||||
| 	var lastModifiedHeader time.Time | ||||
| 
 | ||||
| 	if respHeaders.Get("Expires") != "" { | ||||
| 		expiresHeader, err = http.ParseTime(respHeaders.Get("Expires")) | ||||
| 		if err != nil { | ||||
| 			// sometimes servers will return `Expires: 0` or `Expires: -1` to
 | ||||
| 			// indicate expired content
 | ||||
| 			expiresHeader = time.Time{} | ||||
| 		} | ||||
| 		expiresHeader = expiresHeader.UTC() | ||||
| 	} | ||||
| 
 | ||||
| 	if respHeaders.Get("Date") != "" { | ||||
| 		dateHeader, err = http.ParseTime(respHeaders.Get("Date")) | ||||
| 		if err != nil { | ||||
| 			return nil, time.Time{}, nil, nil, err | ||||
| 		} | ||||
| 		dateHeader = dateHeader.UTC() | ||||
| 	} | ||||
| 
 | ||||
| 	if respHeaders.Get("Last-Modified") != "" { | ||||
| 		lastModifiedHeader, err = http.ParseTime(respHeaders.Get("Last-Modified")) | ||||
| 		if err != nil { | ||||
| 			return nil, time.Time{}, nil, nil, err | ||||
| 		} | ||||
| 		lastModifiedHeader = lastModifiedHeader.UTC() | ||||
| 	} | ||||
| 
 | ||||
| 	obj := Object{ | ||||
| 		CacheIsPrivate: privateCache, | ||||
| 
 | ||||
| 		RespDirectives:         respDir, | ||||
| 		RespHeaders:            respHeaders, | ||||
| 		RespStatusCode:         statusCode, | ||||
| 		RespExpiresHeader:      expiresHeader, | ||||
| 		RespDateHeader:         dateHeader, | ||||
| 		RespLastModifiedHeader: lastModifiedHeader, | ||||
| 
 | ||||
| 		ReqDirectives: reqDir, | ||||
| 		ReqHeaders:    reqHeaders, | ||||
| 		ReqMethod:     reqMethod, | ||||
| 
 | ||||
| 		NowUTC: time.Now().UTC(), | ||||
| 	} | ||||
| 	rv := ObjectResults{} | ||||
| 
 | ||||
| 	CachableObject(&obj, &rv) | ||||
| 	if rv.OutErr != nil { | ||||
| 		return nil, time.Time{}, nil, nil, rv.OutErr | ||||
| 	} | ||||
| 
 | ||||
| 	ExpirationObject(&obj, &rv) | ||||
| 	if rv.OutErr != nil { | ||||
| 		return nil, time.Time{}, nil, nil, rv.OutErr | ||||
| 	} | ||||
| 
 | ||||
| 	return rv.OutReasons, rv.OutExpirationTime, rv.OutWarnings, &obj, nil | ||||
| } | ||||
| 
 | ||||
| // calculate if a freshness directive is present: http://tools.ietf.org/html/rfc7234#section-4.2.1
 | ||||
| func hasFreshness(reqDir *RequestCacheDirectives, respDir *ResponseCacheDirectives, respHeaders http.Header, respExpires time.Time, privateCache bool) bool { | ||||
| 	if !privateCache && respDir.SMaxAge != -1 { | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	if respDir.MaxAge != -1 { | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	if !respExpires.IsZero() || respHeaders.Get("Expires") != "" { | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func cachableStatusCode(statusCode int) bool { | ||||
| 	/* | ||||
| 		Responses with status codes that are defined as cacheable by default | ||||
| 		(e.g., 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, and 501 in | ||||
| 		this specification) can be reused by a cache with heuristic | ||||
| 		expiration unless otherwise indicated by the method definition or | ||||
| 		explicit cache controls [RFC7234]; all other status codes are not | ||||
| 		cacheable by default. | ||||
| 	*/ | ||||
| 	switch statusCode { | ||||
| 	case 200: | ||||
| 		return true | ||||
| 	case 203: | ||||
| 		return true | ||||
| 	case 204: | ||||
| 		return true | ||||
| 	case 206: | ||||
| 		return true | ||||
| 	case 300: | ||||
| 		return true | ||||
| 	case 301: | ||||
| 		return true | ||||
| 	case 404: | ||||
| 		return true | ||||
| 	case 405: | ||||
| 		return true | ||||
| 	case 410: | ||||
| 		return true | ||||
| 	case 414: | ||||
| 		return true | ||||
| 	case 501: | ||||
| 		return true | ||||
| 	default: | ||||
| 		return false | ||||
| 	} | ||||
| } | ||||
|  | @ -1,95 +0,0 @@ | |||
| /** | ||||
|  *  Copyright 2015 Paul Querna | ||||
|  * | ||||
|  *  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *  you may not use this file except in compliance with the License. | ||||
|  *  You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  *  Unless required by applicable law or agreed to in writing, software | ||||
|  *  distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *  See the License for the specific language governing permissions and | ||||
|  *  limitations under the License. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| package cacheobject | ||||
| 
 | ||||
| // Repersents a potential Reason to not cache an object.
 | ||||
| //
 | ||||
| // Applications may wish to ignore specific reasons, which will make them non-RFC
 | ||||
| // compliant, but this type gives them specific cases they can choose to ignore,
 | ||||
| // making them compliant in as many cases as they can.
 | ||||
| type Reason int | ||||
| 
 | ||||
| const ( | ||||
| 
 | ||||
| 	// The request method was POST and an Expiration header was not supplied.
 | ||||
| 	ReasonRequestMethodPOST Reason = iota | ||||
| 
 | ||||
| 	// The request method was PUT and PUTs are not cachable.
 | ||||
| 	ReasonRequestMethodPUT | ||||
| 
 | ||||
| 	// The request method was DELETE and DELETEs are not cachable.
 | ||||
| 	ReasonRequestMethodDELETE | ||||
| 
 | ||||
| 	// The request method was CONNECT and CONNECTs are not cachable.
 | ||||
| 	ReasonRequestMethodCONNECT | ||||
| 
 | ||||
| 	// The request method was OPTIONS and OPTIONS are not cachable.
 | ||||
| 	ReasonRequestMethodOPTIONS | ||||
| 
 | ||||
| 	// The request method was TRACE and TRACEs are not cachable.
 | ||||
| 	ReasonRequestMethodTRACE | ||||
| 
 | ||||
| 	// The request method was not recognized by cachecontrol, and should not be cached.
 | ||||
| 	ReasonRequestMethodUnkown | ||||
| 
 | ||||
| 	// The request included an Cache-Control: no-store header
 | ||||
| 	ReasonRequestNoStore | ||||
| 
 | ||||
| 	// The request included an Authorization header without an explicit Public or Expiration time: http://tools.ietf.org/html/rfc7234#section-3.2
 | ||||
| 	ReasonRequestAuthorizationHeader | ||||
| 
 | ||||
| 	// The response included an Cache-Control: no-store header
 | ||||
| 	ReasonResponseNoStore | ||||
| 
 | ||||
| 	// The response included an Cache-Control: private header and this is not a Private cache
 | ||||
| 	ReasonResponsePrivate | ||||
| 
 | ||||
| 	// The response failed to meet at least one of the conditions specified in RFC 7234 section 3: http://tools.ietf.org/html/rfc7234#section-3
 | ||||
| 	ReasonResponseUncachableByDefault | ||||
| ) | ||||
| 
 | ||||
| func (r Reason) String() string { | ||||
| 	switch r { | ||||
| 	case ReasonRequestMethodPOST: | ||||
| 		return "ReasonRequestMethodPOST" | ||||
| 	case ReasonRequestMethodPUT: | ||||
| 		return "ReasonRequestMethodPUT" | ||||
| 	case ReasonRequestMethodDELETE: | ||||
| 		return "ReasonRequestMethodDELETE" | ||||
| 	case ReasonRequestMethodCONNECT: | ||||
| 		return "ReasonRequestMethodCONNECT" | ||||
| 	case ReasonRequestMethodOPTIONS: | ||||
| 		return "ReasonRequestMethodOPTIONS" | ||||
| 	case ReasonRequestMethodTRACE: | ||||
| 		return "ReasonRequestMethodTRACE" | ||||
| 	case ReasonRequestMethodUnkown: | ||||
| 		return "ReasonRequestMethodUnkown" | ||||
| 	case ReasonRequestNoStore: | ||||
| 		return "ReasonRequestNoStore" | ||||
| 	case ReasonRequestAuthorizationHeader: | ||||
| 		return "ReasonRequestAuthorizationHeader" | ||||
| 	case ReasonResponseNoStore: | ||||
| 		return "ReasonResponseNoStore" | ||||
| 	case ReasonResponsePrivate: | ||||
| 		return "ReasonResponsePrivate" | ||||
| 	case ReasonResponseUncachableByDefault: | ||||
| 		return "ReasonResponseUncachableByDefault" | ||||
| 	} | ||||
| 
 | ||||
| 	panic(r) | ||||
| } | ||||
|  | @ -1,107 +0,0 @@ | |||
| /** | ||||
|  *  Copyright 2015 Paul Querna | ||||
|  * | ||||
|  *  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *  you may not use this file except in compliance with the License. | ||||
|  *  You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  *  Unless required by applicable law or agreed to in writing, software | ||||
|  *  distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *  See the License for the specific language governing permissions and | ||||
|  *  limitations under the License. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| package cacheobject | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Repersents an HTTP Warning: http://tools.ietf.org/html/rfc7234#section-5.5
 | ||||
| type Warning int | ||||
| 
 | ||||
| const ( | ||||
| 	// Response is Stale
 | ||||
| 	// A cache SHOULD generate this whenever the sent response is stale.
 | ||||
| 	WarningResponseIsStale Warning = 110 | ||||
| 
 | ||||
| 	// Revalidation Failed
 | ||||
| 	// A cache SHOULD generate this when sending a stale
 | ||||
| 	// response because an attempt to validate the response failed, due to an
 | ||||
| 	// inability to reach the server.
 | ||||
| 	WarningRevalidationFailed Warning = 111 | ||||
| 
 | ||||
| 	// Disconnected Operation
 | ||||
| 	// A cache SHOULD generate this if it is intentionally disconnected from
 | ||||
| 	// the rest of the network for a period of time.
 | ||||
| 	WarningDisconnectedOperation Warning = 112 | ||||
| 
 | ||||
| 	// Heuristic Expiration
 | ||||
| 	//
 | ||||
| 	// A cache SHOULD generate this if it heuristically chose a freshness
 | ||||
| 	// lifetime greater than 24 hours and the response's age is greater than
 | ||||
| 	// 24 hours.
 | ||||
| 	WarningHeuristicExpiration Warning = 113 | ||||
| 
 | ||||
| 	// Miscellaneous Warning
 | ||||
| 	//
 | ||||
| 	// The warning text can include arbitrary information to be presented to
 | ||||
| 	// a human user or logged.  A system receiving this warning MUST NOT
 | ||||
| 	// take any automated action, besides presenting the warning to the
 | ||||
| 	// user.
 | ||||
| 	WarningMiscellaneousWarning Warning = 199 | ||||
| 
 | ||||
| 	// Transformation Applied
 | ||||
| 	//
 | ||||
| 	// This Warning code MUST be added by a proxy if it applies any
 | ||||
| 	// transformation to the representation, such as changing the
 | ||||
| 	// content-coding, media-type, or modifying the representation data,
 | ||||
| 	// unless this Warning code already appears in the response.
 | ||||
| 	WarningTransformationApplied Warning = 214 | ||||
| 
 | ||||
| 	// Miscellaneous Persistent Warning
 | ||||
| 	//
 | ||||
| 	// The warning text can include arbitrary information to be presented to
 | ||||
| 	// a human user or logged.  A system receiving this warning MUST NOT
 | ||||
| 	// take any automated action.
 | ||||
| 	WarningMiscellaneousPersistentWarning Warning = 299 | ||||
| ) | ||||
| 
 | ||||
| func (w Warning) HeaderString(agent string, date time.Time) string { | ||||
| 	if agent == "" { | ||||
| 		agent = "-" | ||||
| 	} else { | ||||
| 		// TODO(pquerna): this doesn't escape agent if it contains bad things.
 | ||||
| 		agent = `"` + agent + `"` | ||||
| 	} | ||||
| 	return fmt.Sprintf(`%d %s "%s" %s`, w, agent, w.String(), date.Format(http.TimeFormat)) | ||||
| } | ||||
| 
 | ||||
| func (w Warning) String() string { | ||||
| 	switch w { | ||||
| 	case WarningResponseIsStale: | ||||
| 		return "Response is Stale" | ||||
| 	case WarningRevalidationFailed: | ||||
| 		return "Revalidation Failed" | ||||
| 	case WarningDisconnectedOperation: | ||||
| 		return "Disconnected Operation" | ||||
| 	case WarningHeuristicExpiration: | ||||
| 		return "Heuristic Expiration" | ||||
| 	case WarningMiscellaneousWarning: | ||||
| 		// TODO(pquerna): ideally had a better way to override this one code.
 | ||||
| 		return "Miscellaneous Warning" | ||||
| 	case WarningTransformationApplied: | ||||
| 		return "Transformation Applied" | ||||
| 	case WarningMiscellaneousPersistentWarning: | ||||
| 		// TODO(pquerna): same as WarningMiscellaneousWarning
 | ||||
| 		return "Miscellaneous Persistent Warning" | ||||
| 	} | ||||
| 
 | ||||
| 	panic(w) | ||||
| } | ||||
|  | @ -1,25 +0,0 @@ | |||
| /** | ||||
|  *  Copyright 2015 Paul Querna | ||||
|  * | ||||
|  *  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *  you may not use this file except in compliance with the License. | ||||
|  *  You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  *  Unless required by applicable law or agreed to in writing, software | ||||
|  *  distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *  See the License for the specific language governing permissions and | ||||
|  *  limitations under the License. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| // Package cachecontrol implements the logic for HTTP Caching
 | ||||
| //
 | ||||
| // Deciding if an HTTP Response can be cached is often harder
 | ||||
| // and more bug prone than an actual cache storage backend.
 | ||||
| // cachecontrol provides a simple interface to determine if
 | ||||
| // request and response pairs are cachable as defined under
 | ||||
| // RFC 7234 http://tools.ietf.org/html/rfc7234
 | ||||
| package cachecontrol | ||||
|  | @ -1,71 +0,0 @@ | |||
| // Copyright 2016 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| // Package ctxhttp provides helper functions for performing context-aware HTTP requests.
 | ||||
| package ctxhttp // import "golang.org/x/net/context/ctxhttp"
 | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // Do sends an HTTP request with the provided http.Client and returns
 | ||||
| // an HTTP response.
 | ||||
| //
 | ||||
| // If the client is nil, http.DefaultClient is used.
 | ||||
| //
 | ||||
| // The provided ctx must be non-nil. If it is canceled or times out,
 | ||||
| // ctx.Err() will be returned.
 | ||||
| func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) { | ||||
| 	if client == nil { | ||||
| 		client = http.DefaultClient | ||||
| 	} | ||||
| 	resp, err := client.Do(req.WithContext(ctx)) | ||||
| 	// If we got an error, and the context has been canceled,
 | ||||
| 	// the context's error is probably more useful.
 | ||||
| 	if err != nil { | ||||
| 		select { | ||||
| 		case <-ctx.Done(): | ||||
| 			err = ctx.Err() | ||||
| 		default: | ||||
| 		} | ||||
| 	} | ||||
| 	return resp, err | ||||
| } | ||||
| 
 | ||||
| // Get issues a GET request via the Do function.
 | ||||
| func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) { | ||||
| 	req, err := http.NewRequest("GET", url, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return Do(ctx, client, req) | ||||
| } | ||||
| 
 | ||||
| // Head issues a HEAD request via the Do function.
 | ||||
| func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) { | ||||
| 	req, err := http.NewRequest("HEAD", url, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return Do(ctx, client, req) | ||||
| } | ||||
| 
 | ||||
| // Post issues a POST request via the Do function.
 | ||||
| func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) { | ||||
| 	req, err := http.NewRequest("POST", url, body) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	req.Header.Set("Content-Type", bodyType) | ||||
| 	return Do(ctx, client, req) | ||||
| } | ||||
| 
 | ||||
| // PostForm issues a POST request via the Do function.
 | ||||
| func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) { | ||||
| 	return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) | ||||
| } | ||||
|  | @ -19,8 +19,6 @@ import ( | |||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/net/context/ctxhttp" | ||||
| ) | ||||
| 
 | ||||
| // Token represents the credentials used to authorize
 | ||||
|  | @ -229,7 +227,7 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, | |||
| } | ||||
| 
 | ||||
| func doTokenRoundTrip(ctx context.Context, req *http.Request) (*Token, error) { | ||||
| 	r, err := ctxhttp.Do(ctx, ContextClient(ctx), req) | ||||
| 	r, err := ContextClient(ctx).Do(req.WithContext(ctx)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  |  | |||
|  | @ -1,2 +0,0 @@ | |||
| /bin | ||||
| /gopath | ||||
|  | @ -1,16 +0,0 @@ | |||
| language: go | ||||
| 
 | ||||
| go: | ||||
|   - "1.12" | ||||
|   - "1.13" | ||||
| 
 | ||||
| install: | ||||
|  - go get -v -t github.com/coreos/go-oidc/... | ||||
|  - go get golang.org/x/tools/cmd/cover | ||||
|  - go get golang.org/x/lint/golint | ||||
| 
 | ||||
| script: | ||||
|  - ./test | ||||
| 
 | ||||
| notifications: | ||||
|   email: false | ||||
|  | @ -1,71 +0,0 @@ | |||
| # How to Contribute | ||||
| 
 | ||||
| CoreOS projects are [Apache 2.0 licensed](LICENSE) and accept contributions via | ||||
| GitHub pull requests.  This document outlines some of the conventions on | ||||
| development workflow, commit message formatting, contact points and other | ||||
| resources to make it easier to get your contribution accepted. | ||||
| 
 | ||||
| # Certificate of Origin | ||||
| 
 | ||||
| By contributing to this project you agree to the Developer Certificate of | ||||
| Origin (DCO). This document was created by the Linux Kernel community and is a | ||||
| simple statement that you, as a contributor, have the legal right to make the | ||||
| contribution. See the [DCO](DCO) file for details. | ||||
| 
 | ||||
| # Email and Chat | ||||
| 
 | ||||
| The project currently uses the general CoreOS email list and IRC channel: | ||||
| - Email: [coreos-dev](https://groups.google.com/forum/#!forum/coreos-dev) | ||||
| - IRC: #[coreos](irc://irc.freenode.org:6667/#coreos) IRC channel on freenode.org | ||||
| 
 | ||||
| Please avoid emailing maintainers found in the MAINTAINERS file directly. They | ||||
| are very busy and read the mailing lists. | ||||
| 
 | ||||
| ## Getting Started | ||||
| 
 | ||||
| - Fork the repository on GitHub | ||||
| - Read the [README](README.md) for build and test instructions | ||||
| - Play with the project, submit bugs, submit patches! | ||||
| 
 | ||||
| ## Contribution Flow | ||||
| 
 | ||||
| This is a rough outline of what a contributor's workflow looks like: | ||||
| 
 | ||||
| - Create a topic branch from where you want to base your work (usually master). | ||||
| - Make commits of logical units. | ||||
| - Make sure your commit messages are in the proper format (see below). | ||||
| - Push your changes to a topic branch in your fork of the repository. | ||||
| - Make sure the tests pass, and add any new tests as appropriate. | ||||
| - Submit a pull request to the original repository. | ||||
| 
 | ||||
| Thanks for your contributions! | ||||
| 
 | ||||
| ### Format of the Commit Message | ||||
| 
 | ||||
| We follow a rough convention for commit messages that is designed to answer two | ||||
| questions: what changed and why. The subject line should feature the what and | ||||
| the body of the commit should describe the why. | ||||
| 
 | ||||
| ``` | ||||
| scripts: add the test-cluster command | ||||
| 
 | ||||
| this uses tmux to setup a test cluster that you can easily kill and | ||||
| start for debugging. | ||||
| 
 | ||||
| Fixes #38 | ||||
| ``` | ||||
| 
 | ||||
| The format can be described more formally as follows: | ||||
| 
 | ||||
| ``` | ||||
| <subsystem>: <what changed> | ||||
| <BLANK LINE> | ||||
| <why this change was made> | ||||
| <BLANK LINE> | ||||
| <footer> | ||||
| ``` | ||||
| 
 | ||||
| The first line is the subject and should be no longer than 70 characters, the | ||||
| second line is always blank, and other lines should be wrapped at 80 characters. | ||||
| This allows the message to be easier to read on GitHub as well as in various | ||||
| git tools. | ||||
|  | @ -1,36 +0,0 @@ | |||
| Developer Certificate of Origin | ||||
| Version 1.1 | ||||
| 
 | ||||
| Copyright (C) 2004, 2006 The Linux Foundation and its contributors. | ||||
| 660 York Street, Suite 102, | ||||
| San Francisco, CA 94110 USA | ||||
| 
 | ||||
| Everyone is permitted to copy and distribute verbatim copies of this | ||||
| license document, but changing it is not allowed. | ||||
| 
 | ||||
| 
 | ||||
| Developer's Certificate of Origin 1.1 | ||||
| 
 | ||||
| By making a contribution to this project, I certify that: | ||||
| 
 | ||||
| (a) The contribution was created in whole or in part by me and I | ||||
|     have the right to submit it under the open source license | ||||
|     indicated in the file; or | ||||
| 
 | ||||
| (b) The contribution is based upon previous work that, to the best | ||||
|     of my knowledge, is covered under an appropriate open source | ||||
|     license and I have the right under that license to submit that | ||||
|     work with modifications, whether created in whole or in part | ||||
|     by me, under the same open source license (unless I am | ||||
|     permitted to submit under a different license), as indicated | ||||
|     in the file; or | ||||
| 
 | ||||
| (c) The contribution was provided directly to me by some other | ||||
|     person who certified (a), (b) or (c) and I have not modified | ||||
|     it. | ||||
| 
 | ||||
| (d) I understand and agree that this project and the contribution | ||||
|     are public and that a record of the contribution (including all | ||||
|     personal information I submit with it, including my sign-off) is | ||||
|     maintained indefinitely and may be redistributed consistent with | ||||
|     this project or the open source license(s) involved. | ||||
|  | @ -1,202 +0,0 @@ | |||
| Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|                         http://www.apache.org/licenses/ | ||||
| 
 | ||||
|    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
| 
 | ||||
|    1. Definitions. | ||||
| 
 | ||||
|       "License" shall mean the terms and conditions for use, reproduction, | ||||
|       and distribution as defined by Sections 1 through 9 of this document. | ||||
| 
 | ||||
|       "Licensor" shall mean the copyright owner or entity authorized by | ||||
|       the copyright owner that is granting the License. | ||||
| 
 | ||||
|       "Legal Entity" shall mean the union of the acting entity and all | ||||
|       other entities that control, are controlled by, or are under common | ||||
|       control with that entity. For the purposes of this definition, | ||||
|       "control" means (i) the power, direct or indirect, to cause the | ||||
|       direction or management of such entity, whether by contract or | ||||
|       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
|       outstanding shares, or (iii) beneficial ownership of such entity. | ||||
| 
 | ||||
|       "You" (or "Your") shall mean an individual or Legal Entity | ||||
|       exercising permissions granted by this License. | ||||
| 
 | ||||
|       "Source" form shall mean the preferred form for making modifications, | ||||
|       including but not limited to software source code, documentation | ||||
|       source, and configuration files. | ||||
| 
 | ||||
|       "Object" form shall mean any form resulting from mechanical | ||||
|       transformation or translation of a Source form, including but | ||||
|       not limited to compiled object code, generated documentation, | ||||
|       and conversions to other media types. | ||||
| 
 | ||||
|       "Work" shall mean the work of authorship, whether in Source or | ||||
|       Object form, made available under the License, as indicated by a | ||||
|       copyright notice that is included in or attached to the work | ||||
|       (an example is provided in the Appendix below). | ||||
| 
 | ||||
|       "Derivative Works" shall mean any work, whether in Source or Object | ||||
|       form, that is based on (or derived from) the Work and for which the | ||||
|       editorial revisions, annotations, elaborations, or other modifications | ||||
|       represent, as a whole, an original work of authorship. For the purposes | ||||
|       of this License, Derivative Works shall not include works that remain | ||||
|       separable from, or merely link (or bind by name) to the interfaces of, | ||||
|       the Work and Derivative Works thereof. | ||||
| 
 | ||||
|       "Contribution" shall mean any work of authorship, including | ||||
|       the original version of the Work and any modifications or additions | ||||
|       to that Work or Derivative Works thereof, that is intentionally | ||||
|       submitted to Licensor for inclusion in the Work by the copyright owner | ||||
|       or by an individual or Legal Entity authorized to submit on behalf of | ||||
|       the copyright owner. For the purposes of this definition, "submitted" | ||||
|       means any form of electronic, verbal, or written communication sent | ||||
|       to the Licensor or its representatives, including but not limited to | ||||
|       communication on electronic mailing lists, source code control systems, | ||||
|       and issue tracking systems that are managed by, or on behalf of, the | ||||
|       Licensor for the purpose of discussing and improving the Work, but | ||||
|       excluding communication that is conspicuously marked or otherwise | ||||
|       designated in writing by the copyright owner as "Not a Contribution." | ||||
| 
 | ||||
|       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||
|       on behalf of whom a Contribution has been received by Licensor and | ||||
|       subsequently incorporated within the Work. | ||||
| 
 | ||||
|    2. Grant of Copyright License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       copyright license to reproduce, prepare Derivative Works of, | ||||
|       publicly display, publicly perform, sublicense, and distribute the | ||||
|       Work and such Derivative Works in Source or Object form. | ||||
| 
 | ||||
|    3. Grant of Patent License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       (except as stated in this section) patent license to make, have made, | ||||
|       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||
|       where such license applies only to those patent claims licensable | ||||
|       by such Contributor that are necessarily infringed by their | ||||
|       Contribution(s) alone or by combination of their Contribution(s) | ||||
|       with the Work to which such Contribution(s) was submitted. If You | ||||
|       institute patent litigation against any entity (including a | ||||
|       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||
|       or a Contribution incorporated within the Work constitutes direct | ||||
|       or contributory patent infringement, then any patent licenses | ||||
|       granted to You under this License for that Work shall terminate | ||||
|       as of the date such litigation is filed. | ||||
| 
 | ||||
|    4. Redistribution. You may reproduce and distribute copies of the | ||||
|       Work or Derivative Works thereof in any medium, with or without | ||||
|       modifications, and in Source or Object form, provided that You | ||||
|       meet the following conditions: | ||||
| 
 | ||||
|       (a) You must give any other recipients of the Work or | ||||
|           Derivative Works a copy of this License; and | ||||
| 
 | ||||
|       (b) You must cause any modified files to carry prominent notices | ||||
|           stating that You changed the files; and | ||||
| 
 | ||||
|       (c) You must retain, in the Source form of any Derivative Works | ||||
|           that You distribute, all copyright, patent, trademark, and | ||||
|           attribution notices from the Source form of the Work, | ||||
|           excluding those notices that do not pertain to any part of | ||||
|           the Derivative Works; and | ||||
| 
 | ||||
|       (d) If the Work includes a "NOTICE" text file as part of its | ||||
|           distribution, then any Derivative Works that You distribute must | ||||
|           include a readable copy of the attribution notices contained | ||||
|           within such NOTICE file, excluding those notices that do not | ||||
|           pertain to any part of the Derivative Works, in at least one | ||||
|           of the following places: within a NOTICE text file distributed | ||||
|           as part of the Derivative Works; within the Source form or | ||||
|           documentation, if provided along with the Derivative Works; or, | ||||
|           within a display generated by the Derivative Works, if and | ||||
|           wherever such third-party notices normally appear. The contents | ||||
|           of the NOTICE file are for informational purposes only and | ||||
|           do not modify the License. You may add Your own attribution | ||||
|           notices within Derivative Works that You distribute, alongside | ||||
|           or as an addendum to the NOTICE text from the Work, provided | ||||
|           that such additional attribution notices cannot be construed | ||||
|           as modifying the License. | ||||
| 
 | ||||
|       You may add Your own copyright statement to Your modifications and | ||||
|       may provide additional or different license terms and conditions | ||||
|       for use, reproduction, or distribution of Your modifications, or | ||||
|       for any such Derivative Works as a whole, provided Your use, | ||||
|       reproduction, and distribution of the Work otherwise complies with | ||||
|       the conditions stated in this License. | ||||
| 
 | ||||
|    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||
|       any Contribution intentionally submitted for inclusion in the Work | ||||
|       by You to the Licensor shall be under the terms and conditions of | ||||
|       this License, without any additional terms or conditions. | ||||
|       Notwithstanding the above, nothing herein shall supersede or modify | ||||
|       the terms of any separate license agreement you may have executed | ||||
|       with Licensor regarding such Contributions. | ||||
| 
 | ||||
|    6. Trademarks. This License does not grant permission to use the trade | ||||
|       names, trademarks, service marks, or product names of the Licensor, | ||||
|       except as required for reasonable and customary use in describing the | ||||
|       origin of the Work and reproducing the content of the NOTICE file. | ||||
| 
 | ||||
|    7. Disclaimer of Warranty. Unless required by applicable law or | ||||
|       agreed to in writing, Licensor provides the Work (and each | ||||
|       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
|       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
|       implied, including, without limitation, any warranties or conditions | ||||
|       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||
|       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||
|       appropriateness of using or redistributing the Work and assume any | ||||
|       risks associated with Your exercise of permissions under this License. | ||||
| 
 | ||||
|    8. Limitation of Liability. In no event and under no legal theory, | ||||
|       whether in tort (including negligence), contract, or otherwise, | ||||
|       unless required by applicable law (such as deliberate and grossly | ||||
|       negligent acts) or agreed to in writing, shall any Contributor be | ||||
|       liable to You for damages, including any direct, indirect, special, | ||||
|       incidental, or consequential damages of any character arising as a | ||||
|       result of this License or out of the use or inability to use the | ||||
|       Work (including but not limited to damages for loss of goodwill, | ||||
|       work stoppage, computer failure or malfunction, or any and all | ||||
|       other commercial damages or losses), even if such Contributor | ||||
|       has been advised of the possibility of such damages. | ||||
| 
 | ||||
|    9. Accepting Warranty or Additional Liability. While redistributing | ||||
|       the Work or Derivative Works thereof, You may choose to offer, | ||||
|       and charge a fee for, acceptance of support, warranty, indemnity, | ||||
|       or other liability obligations and/or rights consistent with this | ||||
|       License. However, in accepting such obligations, You may act only | ||||
|       on Your own behalf and on Your sole responsibility, not on behalf | ||||
|       of any other Contributor, and only if You agree to indemnify, | ||||
|       defend, and hold each Contributor harmless for any liability | ||||
|       incurred by, or claims asserted against, such Contributor by reason | ||||
|       of your accepting any such warranty or additional liability. | ||||
| 
 | ||||
|    END OF TERMS AND CONDITIONS | ||||
| 
 | ||||
|    APPENDIX: How to apply the Apache License to your work. | ||||
| 
 | ||||
|       To apply the Apache License to your work, attach the following | ||||
|       boilerplate notice, with the fields enclosed by brackets "{}" | ||||
|       replaced with your own identifying information. (Don't include | ||||
|       the brackets!)  The text should be enclosed in the appropriate | ||||
|       comment syntax for the file format. We also recommend that a | ||||
|       file or class name and description of purpose be included on the | ||||
|       same "printed page" as the copyright notice for easier | ||||
|       identification within third-party archives. | ||||
| 
 | ||||
|    Copyright {yyyy} {name of copyright owner} | ||||
| 
 | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
| 
 | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
| 
 | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
| 
 | ||||
|  | @ -1,3 +0,0 @@ | |||
| Eric Chiang <ericchiang@google.com> (@ericchiang) | ||||
| Mike Danese <mikedanese@google.com> (@mikedanese) | ||||
| Rithu Leena John <rjohn@redhat.com> (@rithujohn191) | ||||
|  | @ -1,5 +0,0 @@ | |||
| CoreOS Project | ||||
| Copyright 2014 CoreOS, Inc | ||||
| 
 | ||||
| This product includes software developed at CoreOS, Inc. | ||||
| (http://www.coreos.com/). | ||||
|  | @ -1,72 +0,0 @@ | |||
| # go-oidc | ||||
| 
 | ||||
| [](https://godoc.org/github.com/coreos/go-oidc) | ||||
| [](https://travis-ci.org/coreos/go-oidc) | ||||
| 
 | ||||
| ## OpenID Connect support for Go | ||||
| 
 | ||||
| This package enables OpenID Connect support for the [golang.org/x/oauth2](https://godoc.org/golang.org/x/oauth2) package. | ||||
| 
 | ||||
| ```go | ||||
| provider, err := oidc.NewProvider(ctx, "https://accounts.google.com") | ||||
| if err != nil { | ||||
|     // handle error | ||||
| } | ||||
| 
 | ||||
| // Configure an OpenID Connect aware OAuth2 client. | ||||
| oauth2Config := oauth2.Config{ | ||||
|     ClientID:     clientID, | ||||
|     ClientSecret: clientSecret, | ||||
|     RedirectURL:  redirectURL, | ||||
| 
 | ||||
|     // Discovery returns the OAuth2 endpoints. | ||||
|     Endpoint: provider.Endpoint(), | ||||
| 
 | ||||
|     // "openid" is a required scope for OpenID Connect flows. | ||||
|     Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| OAuth2 redirects are unchanged. | ||||
| 
 | ||||
| ```go | ||||
| func handleRedirect(w http.ResponseWriter, r *http.Request) { | ||||
|     http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| The on responses, the provider can be used to verify ID Tokens. | ||||
| 
 | ||||
| ```go | ||||
| var verifier = provider.Verifier(&oidc.Config{ClientID: clientID}) | ||||
| 
 | ||||
| func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) { | ||||
|     // Verify state and errors. | ||||
| 
 | ||||
|     oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code")) | ||||
|     if err != nil { | ||||
|         // handle error | ||||
|     } | ||||
| 
 | ||||
|     // Extract the ID Token from OAuth2 token. | ||||
|     rawIDToken, ok := oauth2Token.Extra("id_token").(string) | ||||
|     if !ok { | ||||
|         // handle missing token | ||||
|     } | ||||
| 
 | ||||
|     // Parse and verify ID Token payload. | ||||
|     idToken, err := verifier.Verify(ctx, rawIDToken) | ||||
|     if err != nil { | ||||
|         // handle error | ||||
|     } | ||||
| 
 | ||||
|     // Extract custom claims | ||||
|     var claims struct { | ||||
|         Email    string `json:"email"` | ||||
|         Verified bool   `json:"email_verified"` | ||||
|     } | ||||
|     if err := idToken.Claims(&claims); err != nil { | ||||
|         // handle error | ||||
|     } | ||||
| } | ||||
| ``` | ||||
|  | @ -1,61 +0,0 @@ | |||
| ## CoreOS Community Code of Conduct | ||||
| 
 | ||||
| ### Contributor Code of Conduct | ||||
| 
 | ||||
| As contributors and maintainers of this project, and in the interest of | ||||
| fostering an open and welcoming community, we pledge to respect all people who | ||||
| contribute through reporting issues, posting feature requests, updating | ||||
| documentation, submitting pull requests or patches, and other activities. | ||||
| 
 | ||||
| We are committed to making participation in this project a harassment-free | ||||
| experience for everyone, regardless of level of experience, gender, gender | ||||
| identity and expression, sexual orientation, disability, personal appearance, | ||||
| body size, race, ethnicity, age, religion, or nationality. | ||||
| 
 | ||||
| Examples of unacceptable behavior by participants include: | ||||
| 
 | ||||
| * The use of sexualized language or imagery | ||||
| * Personal attacks | ||||
| * Trolling or insulting/derogatory comments | ||||
| * Public or private harassment | ||||
| * Publishing others' private information, such as physical or electronic addresses, without explicit permission | ||||
| * Other unethical or unprofessional conduct. | ||||
| 
 | ||||
| Project maintainers have the right and responsibility to remove, edit, or | ||||
| reject comments, commits, code, wiki edits, issues, and other contributions | ||||
| that are not aligned to this Code of Conduct. By adopting this Code of Conduct, | ||||
| project maintainers commit themselves to fairly and consistently applying these | ||||
| principles to every aspect of managing this project. Project maintainers who do | ||||
| not follow or enforce the Code of Conduct may be permanently removed from the | ||||
| project team. | ||||
| 
 | ||||
| This code of conduct applies both within project spaces and in public spaces | ||||
| when an individual is representing the project or its community. | ||||
| 
 | ||||
| Instances of abusive, harassing, or otherwise unacceptable behavior may be | ||||
| reported by contacting a project maintainer, Brandon Philips | ||||
| <brandon.philips@coreos.com>, and/or Rithu John <rithu.john@coreos.com>. | ||||
| 
 | ||||
| This Code of Conduct is adapted from the Contributor Covenant | ||||
| (http://contributor-covenant.org), version 1.2.0, available at | ||||
| http://contributor-covenant.org/version/1/2/0/ | ||||
| 
 | ||||
| ### CoreOS Events Code of Conduct | ||||
| 
 | ||||
| CoreOS events are working conferences intended for professional networking and | ||||
| collaboration in the CoreOS community. Attendees are expected to behave | ||||
| according to professional standards and in accordance with their employer’s | ||||
| policies on appropriate workplace behavior. | ||||
| 
 | ||||
| While at CoreOS events or related social networking opportunities, attendees | ||||
| should not engage in discriminatory or offensive speech or actions including | ||||
| but not limited to gender, sexuality, race, age, disability, or religion. | ||||
| Speakers should be especially aware of these concerns. | ||||
| 
 | ||||
| CoreOS does not condone any statements by speakers contrary to these standards. | ||||
| CoreOS reserves the right to deny entrance and/or eject from an event (without | ||||
| refund) any individual found to be engaging in discriminatory or offensive | ||||
| speech or actions. | ||||
| 
 | ||||
| Please bring any concerns to the immediate attention of designated on-site | ||||
| staff, Brandon Philips <brandon.philips@coreos.com>, and/or Rithu John <rithu.john@coreos.com>. | ||||
|  | @ -1,20 +0,0 @@ | |||
| // +build !golint
 | ||||
| 
 | ||||
| // Don't lint this file. We don't want to have to add a comment to each constant.
 | ||||
| 
 | ||||
| package oidc | ||||
| 
 | ||||
| const ( | ||||
| 	// JOSE asymmetric signing algorithm values as defined by RFC 7518
 | ||||
| 	//
 | ||||
| 	// see: https://tools.ietf.org/html/rfc7518#section-3.1
 | ||||
| 	RS256 = "RS256" // RSASSA-PKCS-v1.5 using SHA-256
 | ||||
| 	RS384 = "RS384" // RSASSA-PKCS-v1.5 using SHA-384
 | ||||
| 	RS512 = "RS512" // RSASSA-PKCS-v1.5 using SHA-512
 | ||||
| 	ES256 = "ES256" // ECDSA using P-256 and SHA-256
 | ||||
| 	ES384 = "ES384" // ECDSA using P-384 and SHA-384
 | ||||
| 	ES512 = "ES512" // ECDSA using P-521 and SHA-512
 | ||||
| 	PS256 = "PS256" // RSASSA-PSS using SHA256 and MGF1-SHA256
 | ||||
| 	PS384 = "PS384" // RSASSA-PSS using SHA384 and MGF1-SHA384
 | ||||
| 	PS512 = "PS512" // RSASSA-PSS using SHA512 and MGF1-SHA512
 | ||||
| ) | ||||
|  | @ -1,228 +0,0 @@ | |||
| package oidc | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pquerna/cachecontrol" | ||||
| 	jose "gopkg.in/square/go-jose.v2" | ||||
| ) | ||||
| 
 | ||||
| // keysExpiryDelta is the allowed clock skew between a client and the OpenID Connect
 | ||||
| // server.
 | ||||
| //
 | ||||
| // When keys expire, they are valid for this amount of time after.
 | ||||
| //
 | ||||
| // If the keys have not expired, and an ID Token claims it was signed by a key not in
 | ||||
| // the cache, if and only if the keys expire in this amount of time, the keys will be
 | ||||
| // updated.
 | ||||
| const keysExpiryDelta = 30 * time.Second | ||||
| 
 | ||||
| // NewRemoteKeySet returns a KeySet that can validate JSON web tokens by using HTTP
 | ||||
| // GETs to fetch JSON web token sets hosted at a remote URL. This is automatically
 | ||||
| // used by NewProvider using the URLs returned by OpenID Connect discovery, but is
 | ||||
| // exposed for providers that don't support discovery or to prevent round trips to the
 | ||||
| // discovery URL.
 | ||||
| //
 | ||||
| // The returned KeySet is a long lived verifier that caches keys based on cache-control
 | ||||
| // headers. Reuse a common remote key set instead of creating new ones as needed.
 | ||||
| //
 | ||||
| // The behavior of the returned KeySet is undefined once the context is canceled.
 | ||||
| func NewRemoteKeySet(ctx context.Context, jwksURL string) KeySet { | ||||
| 	return newRemoteKeySet(ctx, jwksURL, time.Now) | ||||
| } | ||||
| 
 | ||||
| func newRemoteKeySet(ctx context.Context, jwksURL string, now func() time.Time) *remoteKeySet { | ||||
| 	if now == nil { | ||||
| 		now = time.Now | ||||
| 	} | ||||
| 	return &remoteKeySet{jwksURL: jwksURL, ctx: ctx, now: now} | ||||
| } | ||||
| 
 | ||||
| type remoteKeySet struct { | ||||
| 	jwksURL string | ||||
| 	ctx     context.Context | ||||
| 	now     func() time.Time | ||||
| 
 | ||||
| 	// guard all other fields
 | ||||
| 	mu sync.Mutex | ||||
| 
 | ||||
| 	// inflight suppresses parallel execution of updateKeys and allows
 | ||||
| 	// multiple goroutines to wait for its result.
 | ||||
| 	inflight *inflight | ||||
| 
 | ||||
| 	// A set of cached keys and their expiry.
 | ||||
| 	cachedKeys []jose.JSONWebKey | ||||
| 	expiry     time.Time | ||||
| } | ||||
| 
 | ||||
| // inflight is used to wait on some in-flight request from multiple goroutines.
 | ||||
| type inflight struct { | ||||
| 	doneCh chan struct{} | ||||
| 
 | ||||
| 	keys []jose.JSONWebKey | ||||
| 	err  error | ||||
| } | ||||
| 
 | ||||
| func newInflight() *inflight { | ||||
| 	return &inflight{doneCh: make(chan struct{})} | ||||
| } | ||||
| 
 | ||||
| // wait returns a channel that multiple goroutines can receive on. Once it returns
 | ||||
| // a value, the inflight request is done and result() can be inspected.
 | ||||
| func (i *inflight) wait() <-chan struct{} { | ||||
| 	return i.doneCh | ||||
| } | ||||
| 
 | ||||
| // done can only be called by a single goroutine. It records the result of the
 | ||||
| // inflight request and signals other goroutines that the result is safe to
 | ||||
| // inspect.
 | ||||
| func (i *inflight) done(keys []jose.JSONWebKey, err error) { | ||||
| 	i.keys = keys | ||||
| 	i.err = err | ||||
| 	close(i.doneCh) | ||||
| } | ||||
| 
 | ||||
| // result cannot be called until the wait() channel has returned a value.
 | ||||
| func (i *inflight) result() ([]jose.JSONWebKey, error) { | ||||
| 	return i.keys, i.err | ||||
| } | ||||
| 
 | ||||
| func (r *remoteKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, error) { | ||||
| 	jws, err := jose.ParseSigned(jwt) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("oidc: malformed jwt: %v", err) | ||||
| 	} | ||||
| 	return r.verify(ctx, jws) | ||||
| } | ||||
| 
 | ||||
| func (r *remoteKeySet) verify(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) { | ||||
| 	// We don't support JWTs signed with multiple signatures.
 | ||||
| 	keyID := "" | ||||
| 	for _, sig := range jws.Signatures { | ||||
| 		keyID = sig.Header.KeyID | ||||
| 		break | ||||
| 	} | ||||
| 
 | ||||
| 	keys, expiry := r.keysFromCache() | ||||
| 
 | ||||
| 	// Don't check expiry yet. This optimizes for when the provider is unavailable.
 | ||||
| 	for _, key := range keys { | ||||
| 		if keyID == "" || key.KeyID == keyID { | ||||
| 			if payload, err := jws.Verify(&key); err == nil { | ||||
| 				return payload, nil | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if !r.now().Add(keysExpiryDelta).After(expiry) { | ||||
| 		// Keys haven't expired, don't refresh.
 | ||||
| 		return nil, errors.New("failed to verify id token signature") | ||||
| 	} | ||||
| 
 | ||||
| 	keys, err := r.keysFromRemote(ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("fetching keys %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	for _, key := range keys { | ||||
| 		if keyID == "" || key.KeyID == keyID { | ||||
| 			if payload, err := jws.Verify(&key); err == nil { | ||||
| 				return payload, nil | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil, errors.New("failed to verify id token signature") | ||||
| } | ||||
| 
 | ||||
| func (r *remoteKeySet) keysFromCache() (keys []jose.JSONWebKey, expiry time.Time) { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	return r.cachedKeys, r.expiry | ||||
| } | ||||
| 
 | ||||
| // keysFromRemote syncs the key set from the remote set, records the values in the
 | ||||
| // cache, and returns the key set.
 | ||||
| func (r *remoteKeySet) keysFromRemote(ctx context.Context) ([]jose.JSONWebKey, error) { | ||||
| 	// Need to lock to inspect the inflight request field.
 | ||||
| 	r.mu.Lock() | ||||
| 	// If there's not a current inflight request, create one.
 | ||||
| 	if r.inflight == nil { | ||||
| 		r.inflight = newInflight() | ||||
| 
 | ||||
| 		// This goroutine has exclusive ownership over the current inflight
 | ||||
| 		// request. It releases the resource by nil'ing the inflight field
 | ||||
| 		// once the goroutine is done.
 | ||||
| 		go func() { | ||||
| 			// Sync keys and finish inflight when that's done.
 | ||||
| 			keys, expiry, err := r.updateKeys() | ||||
| 
 | ||||
| 			r.inflight.done(keys, err) | ||||
| 
 | ||||
| 			// Lock to update the keys and indicate that there is no longer an
 | ||||
| 			// inflight request.
 | ||||
| 			r.mu.Lock() | ||||
| 			defer r.mu.Unlock() | ||||
| 
 | ||||
| 			if err == nil { | ||||
| 				r.cachedKeys = keys | ||||
| 				r.expiry = expiry | ||||
| 			} | ||||
| 
 | ||||
| 			// Free inflight so a different request can run.
 | ||||
| 			r.inflight = nil | ||||
| 		}() | ||||
| 	} | ||||
| 	inflight := r.inflight | ||||
| 	r.mu.Unlock() | ||||
| 
 | ||||
| 	select { | ||||
| 	case <-ctx.Done(): | ||||
| 		return nil, ctx.Err() | ||||
| 	case <-inflight.wait(): | ||||
| 		return inflight.result() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (r *remoteKeySet) updateKeys() ([]jose.JSONWebKey, time.Time, error) { | ||||
| 	req, err := http.NewRequest("GET", r.jwksURL, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, time.Time{}, fmt.Errorf("oidc: can't create request: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	resp, err := doRequest(r.ctx, req) | ||||
| 	if err != nil { | ||||
| 		return nil, time.Time{}, fmt.Errorf("oidc: get keys failed %v", err) | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	body, err := ioutil.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return nil, time.Time{}, fmt.Errorf("unable to read response body: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if resp.StatusCode != http.StatusOK { | ||||
| 		return nil, time.Time{}, fmt.Errorf("oidc: get keys failed: %s %s", resp.Status, body) | ||||
| 	} | ||||
| 
 | ||||
| 	var keySet jose.JSONWebKeySet | ||||
| 	err = unmarshalResp(resp, body, &keySet) | ||||
| 	if err != nil { | ||||
| 		return nil, time.Time{}, fmt.Errorf("oidc: failed to decode keys: %v %s", err, body) | ||||
| 	} | ||||
| 
 | ||||
| 	// If the server doesn't provide cache control headers, assume the
 | ||||
| 	// keys expire immediately.
 | ||||
| 	expiry := r.now() | ||||
| 
 | ||||
| 	_, e, err := cachecontrol.CachableResponse(req, resp, cachecontrol.Options{}) | ||||
| 	if err == nil && e.After(expiry) { | ||||
| 		expiry = e | ||||
| 	} | ||||
| 	return keySet.Keys, expiry, nil | ||||
| } | ||||
|  | @ -1,409 +0,0 @@ | |||
| // Package oidc implements OpenID Connect client logic for the golang.org/x/oauth2 package.
 | ||||
| package oidc | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto/sha256" | ||||
| 	"crypto/sha512" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"hash" | ||||
| 	"io/ioutil" | ||||
| 	"mime" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/oauth2" | ||||
| 	jose "gopkg.in/square/go-jose.v2" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	// ScopeOpenID is the mandatory scope for all OpenID Connect OAuth2 requests.
 | ||||
| 	ScopeOpenID = "openid" | ||||
| 
 | ||||
| 	// ScopeOfflineAccess is an optional scope defined by OpenID Connect for requesting
 | ||||
| 	// OAuth2 refresh tokens.
 | ||||
| 	//
 | ||||
| 	// Support for this scope differs between OpenID Connect providers. For instance
 | ||||
| 	// Google rejects it, favoring appending "access_type=offline" as part of the
 | ||||
| 	// authorization request instead.
 | ||||
| 	//
 | ||||
| 	// See: https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
 | ||||
| 	ScopeOfflineAccess = "offline_access" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	errNoAtHash      = errors.New("id token did not have an access token hash") | ||||
| 	errInvalidAtHash = errors.New("access token hash does not match value in ID token") | ||||
| ) | ||||
| 
 | ||||
| // ClientContext returns a new Context that carries the provided HTTP client.
 | ||||
| //
 | ||||
| // This method sets the same context key used by the golang.org/x/oauth2 package,
 | ||||
| // so the returned context works for that package too.
 | ||||
| //
 | ||||
| //    myClient := &http.Client{}
 | ||||
| //    ctx := oidc.ClientContext(parentContext, myClient)
 | ||||
| //
 | ||||
| //    // This will use the custom client
 | ||||
| //    provider, err := oidc.NewProvider(ctx, "https://accounts.example.com")
 | ||||
| //
 | ||||
| func ClientContext(ctx context.Context, client *http.Client) context.Context { | ||||
| 	return context.WithValue(ctx, oauth2.HTTPClient, client) | ||||
| } | ||||
| 
 | ||||
| func doRequest(ctx context.Context, req *http.Request) (*http.Response, error) { | ||||
| 	client := http.DefaultClient | ||||
| 	if c, ok := ctx.Value(oauth2.HTTPClient).(*http.Client); ok { | ||||
| 		client = c | ||||
| 	} | ||||
| 	return client.Do(req.WithContext(ctx)) | ||||
| } | ||||
| 
 | ||||
| // Provider represents an OpenID Connect server's configuration.
 | ||||
| type Provider struct { | ||||
| 	issuer      string | ||||
| 	authURL     string | ||||
| 	tokenURL    string | ||||
| 	userInfoURL string | ||||
| 	algorithms  []string | ||||
| 
 | ||||
| 	// Raw claims returned by the server.
 | ||||
| 	rawClaims []byte | ||||
| 
 | ||||
| 	remoteKeySet KeySet | ||||
| } | ||||
| 
 | ||||
| type cachedKeys struct { | ||||
| 	keys   []jose.JSONWebKey | ||||
| 	expiry time.Time | ||||
| } | ||||
| 
 | ||||
| type providerJSON struct { | ||||
| 	Issuer      string   `json:"issuer"` | ||||
| 	AuthURL     string   `json:"authorization_endpoint"` | ||||
| 	TokenURL    string   `json:"token_endpoint"` | ||||
| 	JWKSURL     string   `json:"jwks_uri"` | ||||
| 	UserInfoURL string   `json:"userinfo_endpoint"` | ||||
| 	Algorithms  []string `json:"id_token_signing_alg_values_supported"` | ||||
| } | ||||
| 
 | ||||
| // supportedAlgorithms is a list of algorithms explicitly supported by this
 | ||||
| // package. If a provider supports other algorithms, such as HS256 or none,
 | ||||
| // those values won't be passed to the IDTokenVerifier.
 | ||||
| var supportedAlgorithms = map[string]bool{ | ||||
| 	RS256: true, | ||||
| 	RS384: true, | ||||
| 	RS512: true, | ||||
| 	ES256: true, | ||||
| 	ES384: true, | ||||
| 	ES512: true, | ||||
| 	PS256: true, | ||||
| 	PS384: true, | ||||
| 	PS512: true, | ||||
| } | ||||
| 
 | ||||
| // NewProvider uses the OpenID Connect discovery mechanism to construct a Provider.
 | ||||
| //
 | ||||
| // The issuer is the URL identifier for the service. For example: "https://accounts.google.com"
 | ||||
| // or "https://login.salesforce.com".
 | ||||
| func NewProvider(ctx context.Context, issuer string) (*Provider, error) { | ||||
| 	wellKnown := strings.TrimSuffix(issuer, "/") + "/.well-known/openid-configuration" | ||||
| 	req, err := http.NewRequest("GET", wellKnown, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	resp, err := doRequest(ctx, req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	body, err := ioutil.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("unable to read response body: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if resp.StatusCode != http.StatusOK { | ||||
| 		return nil, fmt.Errorf("%s: %s", resp.Status, body) | ||||
| 	} | ||||
| 
 | ||||
| 	var p providerJSON | ||||
| 	err = unmarshalResp(resp, body, &p) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("oidc: failed to decode provider discovery object: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if p.Issuer != issuer { | ||||
| 		return nil, fmt.Errorf("oidc: issuer did not match the issuer returned by provider, expected %q got %q", issuer, p.Issuer) | ||||
| 	} | ||||
| 	var algs []string | ||||
| 	for _, a := range p.Algorithms { | ||||
| 		if supportedAlgorithms[a] { | ||||
| 			algs = append(algs, a) | ||||
| 		} | ||||
| 	} | ||||
| 	return &Provider{ | ||||
| 		issuer:       p.Issuer, | ||||
| 		authURL:      p.AuthURL, | ||||
| 		tokenURL:     p.TokenURL, | ||||
| 		userInfoURL:  p.UserInfoURL, | ||||
| 		algorithms:   algs, | ||||
| 		rawClaims:    body, | ||||
| 		remoteKeySet: NewRemoteKeySet(ctx, p.JWKSURL), | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // Claims unmarshals raw fields returned by the server during discovery.
 | ||||
| //
 | ||||
| //    var claims struct {
 | ||||
| //        ScopesSupported []string `json:"scopes_supported"`
 | ||||
| //        ClaimsSupported []string `json:"claims_supported"`
 | ||||
| //    }
 | ||||
| //
 | ||||
| //    if err := provider.Claims(&claims); err != nil {
 | ||||
| //        // handle unmarshaling error
 | ||||
| //    }
 | ||||
| //
 | ||||
| // For a list of fields defined by the OpenID Connect spec see:
 | ||||
| // https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
 | ||||
| func (p *Provider) Claims(v interface{}) error { | ||||
| 	if p.rawClaims == nil { | ||||
| 		return errors.New("oidc: claims not set") | ||||
| 	} | ||||
| 	return json.Unmarshal(p.rawClaims, v) | ||||
| } | ||||
| 
 | ||||
| // Endpoint returns the OAuth2 auth and token endpoints for the given provider.
 | ||||
| func (p *Provider) Endpoint() oauth2.Endpoint { | ||||
| 	return oauth2.Endpoint{AuthURL: p.authURL, TokenURL: p.tokenURL} | ||||
| } | ||||
| 
 | ||||
| // UserInfo represents the OpenID Connect userinfo claims.
 | ||||
| type UserInfo struct { | ||||
| 	Subject       string `json:"sub"` | ||||
| 	Profile       string `json:"profile"` | ||||
| 	Email         string `json:"email"` | ||||
| 	EmailVerified bool   `json:"email_verified"` | ||||
| 
 | ||||
| 	claims []byte | ||||
| } | ||||
| 
 | ||||
| // Claims unmarshals the raw JSON object claims into the provided object.
 | ||||
| func (u *UserInfo) Claims(v interface{}) error { | ||||
| 	if u.claims == nil { | ||||
| 		return errors.New("oidc: claims not set") | ||||
| 	} | ||||
| 	return json.Unmarshal(u.claims, v) | ||||
| } | ||||
| 
 | ||||
| // UserInfo uses the token source to query the provider's user info endpoint.
 | ||||
| func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource) (*UserInfo, error) { | ||||
| 	if p.userInfoURL == "" { | ||||
| 		return nil, errors.New("oidc: user info endpoint is not supported by this provider") | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", p.userInfoURL, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("oidc: create GET request: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	token, err := tokenSource.Token() | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("oidc: get access token: %v", err) | ||||
| 	} | ||||
| 	token.SetAuthHeader(req) | ||||
| 
 | ||||
| 	resp, err := doRequest(ctx, req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 	body, err := ioutil.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if resp.StatusCode != http.StatusOK { | ||||
| 		return nil, fmt.Errorf("%s: %s", resp.Status, body) | ||||
| 	} | ||||
| 
 | ||||
| 	var userInfo UserInfo | ||||
| 	if err := json.Unmarshal(body, &userInfo); err != nil { | ||||
| 		return nil, fmt.Errorf("oidc: failed to decode userinfo: %v", err) | ||||
| 	} | ||||
| 	userInfo.claims = body | ||||
| 	return &userInfo, nil | ||||
| } | ||||
| 
 | ||||
| // IDToken is an OpenID Connect extension that provides a predictable representation
 | ||||
| // of an authorization event.
 | ||||
| //
 | ||||
| // The ID Token only holds fields OpenID Connect requires. To access additional
 | ||||
| // claims returned by the server, use the Claims method.
 | ||||
| type IDToken struct { | ||||
| 	// The URL of the server which issued this token. OpenID Connect
 | ||||
| 	// requires this value always be identical to the URL used for
 | ||||
| 	// initial discovery.
 | ||||
| 	//
 | ||||
| 	// Note: Because of a known issue with Google Accounts' implementation
 | ||||
| 	// this value may differ when using Google.
 | ||||
| 	//
 | ||||
| 	// See: https://developers.google.com/identity/protocols/OpenIDConnect#obtainuserinfo
 | ||||
| 	Issuer string | ||||
| 
 | ||||
| 	// The client ID, or set of client IDs, that this token is issued for. For
 | ||||
| 	// common uses, this is the client that initialized the auth flow.
 | ||||
| 	//
 | ||||
| 	// This package ensures the audience contains an expected value.
 | ||||
| 	Audience []string | ||||
| 
 | ||||
| 	// A unique string which identifies the end user.
 | ||||
| 	Subject string | ||||
| 
 | ||||
| 	// Expiry of the token. Ths package will not process tokens that have
 | ||||
| 	// expired unless that validation is explicitly turned off.
 | ||||
| 	Expiry time.Time | ||||
| 	// When the token was issued by the provider.
 | ||||
| 	IssuedAt time.Time | ||||
| 
 | ||||
| 	// Initial nonce provided during the authentication redirect.
 | ||||
| 	//
 | ||||
| 	// This package does NOT provided verification on the value of this field
 | ||||
| 	// and it's the user's responsibility to ensure it contains a valid value.
 | ||||
| 	Nonce string | ||||
| 
 | ||||
| 	// at_hash claim, if set in the ID token. Callers can verify an access token
 | ||||
| 	// that corresponds to the ID token using the VerifyAccessToken method.
 | ||||
| 	AccessTokenHash string | ||||
| 
 | ||||
| 	// signature algorithm used for ID token, needed to compute a verification hash of an
 | ||||
| 	// access token
 | ||||
| 	sigAlgorithm string | ||||
| 
 | ||||
| 	// Raw payload of the id_token.
 | ||||
| 	claims []byte | ||||
| 
 | ||||
| 	// Map of distributed claim names to claim sources
 | ||||
| 	distributedClaims map[string]claimSource | ||||
| } | ||||
| 
 | ||||
| // Claims unmarshals the raw JSON payload of the ID Token into a provided struct.
 | ||||
| //
 | ||||
| //		idToken, err := idTokenVerifier.Verify(rawIDToken)
 | ||||
| //		if err != nil {
 | ||||
| //			// handle error
 | ||||
| //		}
 | ||||
| //		var claims struct {
 | ||||
| //			Email         string `json:"email"`
 | ||||
| //			EmailVerified bool   `json:"email_verified"`
 | ||||
| //		}
 | ||||
| //		if err := idToken.Claims(&claims); err != nil {
 | ||||
| //			// handle error
 | ||||
| //		}
 | ||||
| //
 | ||||
| func (i *IDToken) Claims(v interface{}) error { | ||||
| 	if i.claims == nil { | ||||
| 		return errors.New("oidc: claims not set") | ||||
| 	} | ||||
| 	return json.Unmarshal(i.claims, v) | ||||
| } | ||||
| 
 | ||||
| // VerifyAccessToken verifies that the hash of the access token that corresponds to the iD token
 | ||||
| // matches the hash in the id token. It returns an error if the hashes  don't match.
 | ||||
| // It is the caller's responsibility to ensure that the optional access token hash is present for the ID token
 | ||||
| // before calling this method. See https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken
 | ||||
| func (i *IDToken) VerifyAccessToken(accessToken string) error { | ||||
| 	if i.AccessTokenHash == "" { | ||||
| 		return errNoAtHash | ||||
| 	} | ||||
| 	var h hash.Hash | ||||
| 	switch i.sigAlgorithm { | ||||
| 	case RS256, ES256, PS256: | ||||
| 		h = sha256.New() | ||||
| 	case RS384, ES384, PS384: | ||||
| 		h = sha512.New384() | ||||
| 	case RS512, ES512, PS512: | ||||
| 		h = sha512.New() | ||||
| 	default: | ||||
| 		return fmt.Errorf("oidc: unsupported signing algorithm %q", i.sigAlgorithm) | ||||
| 	} | ||||
| 	h.Write([]byte(accessToken)) // hash documents that Write will never return an error
 | ||||
| 	sum := h.Sum(nil)[:h.Size()/2] | ||||
| 	actual := base64.RawURLEncoding.EncodeToString(sum) | ||||
| 	if actual != i.AccessTokenHash { | ||||
| 		return errInvalidAtHash | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| type idToken struct { | ||||
| 	Issuer       string                 `json:"iss"` | ||||
| 	Subject      string                 `json:"sub"` | ||||
| 	Audience     audience               `json:"aud"` | ||||
| 	Expiry       jsonTime               `json:"exp"` | ||||
| 	IssuedAt     jsonTime               `json:"iat"` | ||||
| 	NotBefore    *jsonTime              `json:"nbf"` | ||||
| 	Nonce        string                 `json:"nonce"` | ||||
| 	AtHash       string                 `json:"at_hash"` | ||||
| 	ClaimNames   map[string]string      `json:"_claim_names"` | ||||
| 	ClaimSources map[string]claimSource `json:"_claim_sources"` | ||||
| } | ||||
| 
 | ||||
| type claimSource struct { | ||||
| 	Endpoint    string `json:"endpoint"` | ||||
| 	AccessToken string `json:"access_token"` | ||||
| } | ||||
| 
 | ||||
| type audience []string | ||||
| 
 | ||||
| func (a *audience) UnmarshalJSON(b []byte) error { | ||||
| 	var s string | ||||
| 	if json.Unmarshal(b, &s) == nil { | ||||
| 		*a = audience{s} | ||||
| 		return nil | ||||
| 	} | ||||
| 	var auds []string | ||||
| 	if err := json.Unmarshal(b, &auds); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	*a = audience(auds) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| type jsonTime time.Time | ||||
| 
 | ||||
| func (j *jsonTime) UnmarshalJSON(b []byte) error { | ||||
| 	var n json.Number | ||||
| 	if err := json.Unmarshal(b, &n); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	var unix int64 | ||||
| 
 | ||||
| 	if t, err := n.Int64(); err == nil { | ||||
| 		unix = t | ||||
| 	} else { | ||||
| 		f, err := n.Float64() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		unix = int64(f) | ||||
| 	} | ||||
| 	*j = jsonTime(time.Unix(unix, 0)) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func unmarshalResp(r *http.Response, body []byte, v interface{}) error { | ||||
| 	err := json.Unmarshal(body, &v) | ||||
| 	if err == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	ct := r.Header.Get("Content-Type") | ||||
| 	mediaType, _, parseErr := mime.ParseMediaType(ct) | ||||
| 	if parseErr == nil && mediaType == "application/json" { | ||||
| 		return fmt.Errorf("got Content-Type = application/json, but could not unmarshal as JSON: %v", err) | ||||
| 	} | ||||
| 	return fmt.Errorf("expected Content-Type = application/json, got %q: %v", ct, err) | ||||
| } | ||||
|  | @ -1,16 +0,0 @@ | |||
| #!/bin/bash | ||||
| 
 | ||||
| set -e | ||||
| 
 | ||||
| # Filter out any files with a !golint build tag. | ||||
| LINTABLE=$( go list -tags=golint -f ' | ||||
|   {{- range $i, $file := .GoFiles -}} | ||||
|     {{ $file }} {{ end }} | ||||
|   {{ range $i, $file := .TestGoFiles -}} | ||||
|     {{ $file }} {{ end }}' github.com/coreos/go-oidc ) | ||||
| 
 | ||||
| go test -v -i -race github.com/coreos/go-oidc/... | ||||
| go test -v -race github.com/coreos/go-oidc/... | ||||
| golint -set_exit_status $LINTABLE | ||||
| go vet github.com/coreos/go-oidc/... | ||||
| go build -v ./example/... | ||||
|  | @ -1,336 +0,0 @@ | |||
| package oidc | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/oauth2" | ||||
| 	jose "gopkg.in/square/go-jose.v2" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	issuerGoogleAccounts         = "https://accounts.google.com" | ||||
| 	issuerGoogleAccountsNoScheme = "accounts.google.com" | ||||
| ) | ||||
| 
 | ||||
| // KeySet is a set of publc JSON Web Keys that can be used to validate the signature
 | ||||
| // of JSON web tokens. This is expected to be backed by a remote key set through
 | ||||
| // provider metadata discovery or an in-memory set of keys delivered out-of-band.
 | ||||
| type KeySet interface { | ||||
| 	// VerifySignature parses the JSON web token, verifies the signature, and returns
 | ||||
| 	// the raw payload. Header and claim fields are validated by other parts of the
 | ||||
| 	// package. For example, the KeySet does not need to check values such as signature
 | ||||
| 	// algorithm, issuer, and audience since the IDTokenVerifier validates these values
 | ||||
| 	// independently.
 | ||||
| 	//
 | ||||
| 	// If VerifySignature makes HTTP requests to verify the token, it's expected to
 | ||||
| 	// use any HTTP client associated with the context through ClientContext.
 | ||||
| 	VerifySignature(ctx context.Context, jwt string) (payload []byte, err error) | ||||
| } | ||||
| 
 | ||||
| // IDTokenVerifier provides verification for ID Tokens.
 | ||||
| type IDTokenVerifier struct { | ||||
| 	keySet KeySet | ||||
| 	config *Config | ||||
| 	issuer string | ||||
| } | ||||
| 
 | ||||
| // NewVerifier returns a verifier manually constructed from a key set and issuer URL.
 | ||||
| //
 | ||||
| // It's easier to use provider discovery to construct an IDTokenVerifier than creating
 | ||||
| // one directly. This method is intended to be used with provider that don't support
 | ||||
| // metadata discovery, or avoiding round trips when the key set URL is already known.
 | ||||
| //
 | ||||
| // This constructor can be used to create a verifier directly using the issuer URL and
 | ||||
| // JSON Web Key Set URL without using discovery:
 | ||||
| //
 | ||||
| //		keySet := oidc.NewRemoteKeySet(ctx, "https://www.googleapis.com/oauth2/v3/certs")
 | ||||
| //		verifier := oidc.NewVerifier("https://accounts.google.com", keySet, config)
 | ||||
| //
 | ||||
| // Since KeySet is an interface, this constructor can also be used to supply custom
 | ||||
| // public key sources. For example, if a user wanted to supply public keys out-of-band
 | ||||
| // and hold them statically in-memory:
 | ||||
| //
 | ||||
| //		// Custom KeySet implementation.
 | ||||
| //		keySet := newStatisKeySet(publicKeys...)
 | ||||
| //
 | ||||
| //		// Verifier uses the custom KeySet implementation.
 | ||||
| //		verifier := oidc.NewVerifier("https://auth.example.com", keySet, config)
 | ||||
| //
 | ||||
| func NewVerifier(issuerURL string, keySet KeySet, config *Config) *IDTokenVerifier { | ||||
| 	return &IDTokenVerifier{keySet: keySet, config: config, issuer: issuerURL} | ||||
| } | ||||
| 
 | ||||
| // Config is the configuration for an IDTokenVerifier.
 | ||||
| type Config struct { | ||||
| 	// Expected audience of the token. For a majority of the cases this is expected to be
 | ||||
| 	// the ID of the client that initialized the login flow. It may occasionally differ if
 | ||||
| 	// the provider supports the authorizing party (azp) claim.
 | ||||
| 	//
 | ||||
| 	// If not provided, users must explicitly set SkipClientIDCheck.
 | ||||
| 	ClientID string | ||||
| 	// If specified, only this set of algorithms may be used to sign the JWT.
 | ||||
| 	//
 | ||||
| 	// If the IDTokenVerifier is created from a provider with (*Provider).Verifier, this
 | ||||
| 	// defaults to the set of algorithms the provider supports. Otherwise this values
 | ||||
| 	// defaults to RS256.
 | ||||
| 	SupportedSigningAlgs []string | ||||
| 
 | ||||
| 	// If true, no ClientID check performed. Must be true if ClientID field is empty.
 | ||||
| 	SkipClientIDCheck bool | ||||
| 	// If true, token expiry is not checked.
 | ||||
| 	SkipExpiryCheck bool | ||||
| 
 | ||||
| 	// SkipIssuerCheck is intended for specialized cases where the the caller wishes to
 | ||||
| 	// defer issuer validation. When enabled, callers MUST independently verify the Token's
 | ||||
| 	// Issuer is a known good value.
 | ||||
| 	//
 | ||||
| 	// Mismatched issuers often indicate client mis-configuration. If mismatches are
 | ||||
| 	// unexpected, evaluate if the provided issuer URL is incorrect instead of enabling
 | ||||
| 	// this option.
 | ||||
| 	SkipIssuerCheck bool | ||||
| 
 | ||||
| 	// Time function to check Token expiry. Defaults to time.Now
 | ||||
| 	Now func() time.Time | ||||
| } | ||||
| 
 | ||||
| // Verifier returns an IDTokenVerifier that uses the provider's key set to verify JWTs.
 | ||||
| //
 | ||||
| // The returned IDTokenVerifier is tied to the Provider's context and its behavior is
 | ||||
| // undefined once the Provider's context is canceled.
 | ||||
| func (p *Provider) Verifier(config *Config) *IDTokenVerifier { | ||||
| 	if len(config.SupportedSigningAlgs) == 0 && len(p.algorithms) > 0 { | ||||
| 		// Make a copy so we don't modify the config values.
 | ||||
| 		cp := &Config{} | ||||
| 		*cp = *config | ||||
| 		cp.SupportedSigningAlgs = p.algorithms | ||||
| 		config = cp | ||||
| 	} | ||||
| 	return NewVerifier(p.issuer, p.remoteKeySet, config) | ||||
| } | ||||
| 
 | ||||
| func parseJWT(p string) ([]byte, error) { | ||||
| 	parts := strings.Split(p, ".") | ||||
| 	if len(parts) < 2 { | ||||
| 		return nil, fmt.Errorf("oidc: malformed jwt, expected 3 parts got %d", len(parts)) | ||||
| 	} | ||||
| 	payload, err := base64.RawURLEncoding.DecodeString(parts[1]) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("oidc: malformed jwt payload: %v", err) | ||||
| 	} | ||||
| 	return payload, nil | ||||
| } | ||||
| 
 | ||||
| func contains(sli []string, ele string) bool { | ||||
| 	for _, s := range sli { | ||||
| 		if s == ele { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // Returns the Claims from the distributed JWT token
 | ||||
| func resolveDistributedClaim(ctx context.Context, verifier *IDTokenVerifier, src claimSource) ([]byte, error) { | ||||
| 	req, err := http.NewRequest("GET", src.Endpoint, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("malformed request: %v", err) | ||||
| 	} | ||||
| 	if src.AccessToken != "" { | ||||
| 		req.Header.Set("Authorization", "Bearer "+src.AccessToken) | ||||
| 	} | ||||
| 
 | ||||
| 	resp, err := doRequest(ctx, req) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("oidc: Request to endpoint failed: %v", err) | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	body, err := ioutil.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("unable to read response body: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if resp.StatusCode != http.StatusOK { | ||||
| 		return nil, fmt.Errorf("oidc: request failed: %v", resp.StatusCode) | ||||
| 	} | ||||
| 
 | ||||
| 	token, err := verifier.Verify(ctx, string(body)) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("malformed response body: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return token.claims, nil | ||||
| } | ||||
| 
 | ||||
| func parseClaim(raw []byte, name string, v interface{}) error { | ||||
| 	var parsed map[string]json.RawMessage | ||||
| 	if err := json.Unmarshal(raw, &parsed); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	val, ok := parsed[name] | ||||
| 	if !ok { | ||||
| 		return fmt.Errorf("claim doesn't exist: %s", name) | ||||
| 	} | ||||
| 
 | ||||
| 	return json.Unmarshal([]byte(val), v) | ||||
| } | ||||
| 
 | ||||
| // Verify parses a raw ID Token, verifies it's been signed by the provider, preforms
 | ||||
| // any additional checks depending on the Config, and returns the payload.
 | ||||
| //
 | ||||
| // Verify does NOT do nonce validation, which is the callers responsibility.
 | ||||
| //
 | ||||
| // See: https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
 | ||||
| //
 | ||||
| //    oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
 | ||||
| //    if err != nil {
 | ||||
| //        // handle error
 | ||||
| //    }
 | ||||
| //
 | ||||
| //    // Extract the ID Token from oauth2 token.
 | ||||
| //    rawIDToken, ok := oauth2Token.Extra("id_token").(string)
 | ||||
| //    if !ok {
 | ||||
| //        // handle error
 | ||||
| //    }
 | ||||
| //
 | ||||
| //    token, err := verifier.Verify(ctx, rawIDToken)
 | ||||
| //
 | ||||
| func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDToken, error) { | ||||
| 	jws, err := jose.ParseSigned(rawIDToken) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("oidc: malformed jwt: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Throw out tokens with invalid claims before trying to verify the token. This lets
 | ||||
| 	// us do cheap checks before possibly re-syncing keys.
 | ||||
| 	payload, err := parseJWT(rawIDToken) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("oidc: malformed jwt: %v", err) | ||||
| 	} | ||||
| 	var token idToken | ||||
| 	if err := json.Unmarshal(payload, &token); err != nil { | ||||
| 		return nil, fmt.Errorf("oidc: failed to unmarshal claims: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	distributedClaims := make(map[string]claimSource) | ||||
| 
 | ||||
| 	//step through the token to map claim names to claim sources"
 | ||||
| 	for cn, src := range token.ClaimNames { | ||||
| 		if src == "" { | ||||
| 			return nil, fmt.Errorf("oidc: failed to obtain source from claim name") | ||||
| 		} | ||||
| 		s, ok := token.ClaimSources[src] | ||||
| 		if !ok { | ||||
| 			return nil, fmt.Errorf("oidc: source does not exist") | ||||
| 		} | ||||
| 		distributedClaims[cn] = s | ||||
| 	} | ||||
| 
 | ||||
| 	t := &IDToken{ | ||||
| 		Issuer:            token.Issuer, | ||||
| 		Subject:           token.Subject, | ||||
| 		Audience:          []string(token.Audience), | ||||
| 		Expiry:            time.Time(token.Expiry), | ||||
| 		IssuedAt:          time.Time(token.IssuedAt), | ||||
| 		Nonce:             token.Nonce, | ||||
| 		AccessTokenHash:   token.AtHash, | ||||
| 		claims:            payload, | ||||
| 		distributedClaims: distributedClaims, | ||||
| 	} | ||||
| 
 | ||||
| 	// Check issuer.
 | ||||
| 	if !v.config.SkipIssuerCheck && t.Issuer != v.issuer { | ||||
| 		// Google sometimes returns "accounts.google.com" as the issuer claim instead of
 | ||||
| 		// the required "https://accounts.google.com". Detect this case and allow it only
 | ||||
| 		// for Google.
 | ||||
| 		//
 | ||||
| 		// We will not add hooks to let other providers go off spec like this.
 | ||||
| 		if !(v.issuer == issuerGoogleAccounts && t.Issuer == issuerGoogleAccountsNoScheme) { | ||||
| 			return nil, fmt.Errorf("oidc: id token issued by a different provider, expected %q got %q", v.issuer, t.Issuer) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// If a client ID has been provided, make sure it's part of the audience. SkipClientIDCheck must be true if ClientID is empty.
 | ||||
| 	//
 | ||||
| 	// This check DOES NOT ensure that the ClientID is the party to which the ID Token was issued (i.e. Authorized party).
 | ||||
| 	if !v.config.SkipClientIDCheck { | ||||
| 		if v.config.ClientID != "" { | ||||
| 			if !contains(t.Audience, v.config.ClientID) { | ||||
| 				return nil, fmt.Errorf("oidc: expected audience %q got %q", v.config.ClientID, t.Audience) | ||||
| 			} | ||||
| 		} else { | ||||
| 			return nil, fmt.Errorf("oidc: invalid configuration, clientID must be provided or SkipClientIDCheck must be set") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// If a SkipExpiryCheck is false, make sure token is not expired.
 | ||||
| 	if !v.config.SkipExpiryCheck { | ||||
| 		now := time.Now | ||||
| 		if v.config.Now != nil { | ||||
| 			now = v.config.Now | ||||
| 		} | ||||
| 		nowTime := now() | ||||
| 
 | ||||
| 		if t.Expiry.Before(nowTime) { | ||||
| 			return nil, fmt.Errorf("oidc: token is expired (Token Expiry: %v)", t.Expiry) | ||||
| 		} | ||||
| 
 | ||||
| 		// If nbf claim is provided in token, ensure that it is indeed in the past.
 | ||||
| 		if token.NotBefore != nil { | ||||
| 			nbfTime := time.Time(*token.NotBefore) | ||||
| 			leeway := 1 * time.Minute | ||||
| 
 | ||||
| 			if nowTime.Add(leeway).Before(nbfTime) { | ||||
| 				return nil, fmt.Errorf("oidc: current time %v before the nbf (not before) time: %v", nowTime, nbfTime) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	switch len(jws.Signatures) { | ||||
| 	case 0: | ||||
| 		return nil, fmt.Errorf("oidc: id token not signed") | ||||
| 	case 1: | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("oidc: multiple signatures on id token not supported") | ||||
| 	} | ||||
| 
 | ||||
| 	sig := jws.Signatures[0] | ||||
| 	supportedSigAlgs := v.config.SupportedSigningAlgs | ||||
| 	if len(supportedSigAlgs) == 0 { | ||||
| 		supportedSigAlgs = []string{RS256} | ||||
| 	} | ||||
| 
 | ||||
| 	if !contains(supportedSigAlgs, sig.Header.Algorithm) { | ||||
| 		return nil, fmt.Errorf("oidc: id token signed with unsupported algorithm, expected %q got %q", supportedSigAlgs, sig.Header.Algorithm) | ||||
| 	} | ||||
| 
 | ||||
| 	t.sigAlgorithm = sig.Header.Algorithm | ||||
| 
 | ||||
| 	gotPayload, err := v.keySet.VerifySignature(ctx, rawIDToken) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to verify signature: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Ensure that the payload returned by the square actually matches the payload parsed earlier.
 | ||||
| 	if !bytes.Equal(gotPayload, payload) { | ||||
| 		return nil, errors.New("oidc: internal error, payload parsed did not match previous payload") | ||||
| 	} | ||||
| 
 | ||||
| 	return t, nil | ||||
| } | ||||
| 
 | ||||
| // Nonce returns an auth code option which requires the ID Token created by the
 | ||||
| // OpenID Connect provider to contain the specified nonce.
 | ||||
| func Nonce(nonce string) oauth2.AuthCodeOption { | ||||
| 	return oauth2.SetAuthURLParam("nonce", nonce) | ||||
| } | ||||
|  | @ -1 +0,0 @@ | |||
| '|Ę&{tÄU|gGę(ěŹCy=+¨śňcű:u:/pś#~žü["±4¤!nŮAŞDK<Šuf˙hĹażÂ:şü¸ˇ´B/ŁŘ¤ą¤ň_<C588>hÎŰSăT*wĚxĽŻťą-ç|ťŕŔÓ<C594>ŃÄäóĚ㣗A$$â6ŁÁâG)8nĎpűĆˡ3ĚšśoďĎvŽB–3ż]xÝ“Ó2l§G•|qRŢŻ
ö2
5R–Ó×Ç$´ń˝YčˇŢÝ™l‘Ë«yAI"ŰŚ<C5B0>®íĂ»ąĽkÄ|Kĺţ[9ĆâŇĺ=°ú˙źń|@S•3ó#ćťx?ľV„,ľ‚SĆÝőśwPíogŇ6&V6	©D.dBŠ7 | ||||
|  | @ -1,8 +0,0 @@ | |||
| *~ | ||||
| .*.swp | ||||
| *.out | ||||
| *.test | ||||
| *.pem | ||||
| *.cov | ||||
| jose-util/jose-util | ||||
| jose-util.t.err | ||||
|  | @ -1,45 +0,0 @@ | |||
| language: go | ||||
| 
 | ||||
| sudo: false | ||||
| 
 | ||||
| matrix: | ||||
|   fast_finish: true | ||||
|   allow_failures: | ||||
|     - go: tip | ||||
| 
 | ||||
| go: | ||||
| - '1.14.x' | ||||
| - '1.15.x' | ||||
| - tip | ||||
| 
 | ||||
| go_import_path: gopkg.in/square/go-jose.v2 | ||||
| 
 | ||||
| before_script: | ||||
| - export PATH=$HOME/.local/bin:$PATH | ||||
| 
 | ||||
| before_install: | ||||
| # Install encrypted gitcookies to get around bandwidth-limits | ||||
| # that is causing Travis-CI builds to fail. For more info, see | ||||
| # https://github.com/golang/go/issues/12933 | ||||
| - openssl aes-256-cbc -K $encrypted_1528c3c2cafd_key -iv $encrypted_1528c3c2cafd_iv -in .gitcookies.sh.enc -out .gitcookies.sh -d || true | ||||
| - bash .gitcookies.sh || true | ||||
| - go get github.com/wadey/gocovmerge | ||||
| - go get github.com/mattn/goveralls | ||||
| - go get github.com/stretchr/testify/assert | ||||
| - go get github.com/stretchr/testify/require | ||||
| - go get github.com/google/go-cmp/cmp | ||||
| - go get golang.org/x/tools/cmd/cover || true | ||||
| - go get code.google.com/p/go.tools/cmd/cover || true | ||||
| - pip install cram --user | ||||
| 
 | ||||
| script: | ||||
| - go test . -v -covermode=count -coverprofile=profile.cov | ||||
| - go test ./cipher -v -covermode=count -coverprofile=cipher/profile.cov | ||||
| - go test ./jwt -v -covermode=count -coverprofile=jwt/profile.cov | ||||
| - go test ./json -v # no coverage for forked encoding/json package | ||||
| - cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t # cram tests jose-util | ||||
| - cd .. | ||||
| 
 | ||||
| after_success: | ||||
| - gocovmerge *.cov */*.cov > merged.coverprofile | ||||
| - $HOME/gopath/bin/goveralls -coverprofile merged.coverprofile -service=travis-ci | ||||
|  | @ -1,10 +0,0 @@ | |||
| Serious about security | ||||
| ====================== | ||||
| 
 | ||||
| Square recognizes the important contributions the security research community | ||||
| can make. We therefore encourage reporting security issues with the code | ||||
| contained in this repository. | ||||
| 
 | ||||
| If you believe you have discovered a security vulnerability, please follow the | ||||
| guidelines at <https://bugcrowd.com/squareopensource>. | ||||
| 
 | ||||
|  | @ -1,14 +0,0 @@ | |||
| # Contributing | ||||
| 
 | ||||
| If you would like to contribute code to go-jose you can do so through GitHub by | ||||
| forking the repository and sending a pull request. | ||||
| 
 | ||||
| When submitting code, please make every effort to follow existing conventions | ||||
| and style in order to keep the code as readable as possible. Please also make | ||||
| sure all tests pass by running `go test`, and format your code with `go fmt`. | ||||
| We also recommend using `golint` and `errcheck`. | ||||
| 
 | ||||
| Before your code can be accepted into the project you must also sign the | ||||
| [Individual Contributor License Agreement][1]. | ||||
| 
 | ||||
|  [1]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1 | ||||
|  | @ -1,202 +0,0 @@ | |||
| 
 | ||||
|                                  Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|                         http://www.apache.org/licenses/ | ||||
| 
 | ||||
|    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
| 
 | ||||
|    1. Definitions. | ||||
| 
 | ||||
|       "License" shall mean the terms and conditions for use, reproduction, | ||||
|       and distribution as defined by Sections 1 through 9 of this document. | ||||
| 
 | ||||
|       "Licensor" shall mean the copyright owner or entity authorized by | ||||
|       the copyright owner that is granting the License. | ||||
| 
 | ||||
|       "Legal Entity" shall mean the union of the acting entity and all | ||||
|       other entities that control, are controlled by, or are under common | ||||
|       control with that entity. For the purposes of this definition, | ||||
|       "control" means (i) the power, direct or indirect, to cause the | ||||
|       direction or management of such entity, whether by contract or | ||||
|       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
|       outstanding shares, or (iii) beneficial ownership of such entity. | ||||
| 
 | ||||
|       "You" (or "Your") shall mean an individual or Legal Entity | ||||
|       exercising permissions granted by this License. | ||||
| 
 | ||||
|       "Source" form shall mean the preferred form for making modifications, | ||||
|       including but not limited to software source code, documentation | ||||
|       source, and configuration files. | ||||
| 
 | ||||
|       "Object" form shall mean any form resulting from mechanical | ||||
|       transformation or translation of a Source form, including but | ||||
|       not limited to compiled object code, generated documentation, | ||||
|       and conversions to other media types. | ||||
| 
 | ||||
|       "Work" shall mean the work of authorship, whether in Source or | ||||
|       Object form, made available under the License, as indicated by a | ||||
|       copyright notice that is included in or attached to the work | ||||
|       (an example is provided in the Appendix below). | ||||
| 
 | ||||
|       "Derivative Works" shall mean any work, whether in Source or Object | ||||
|       form, that is based on (or derived from) the Work and for which the | ||||
|       editorial revisions, annotations, elaborations, or other modifications | ||||
|       represent, as a whole, an original work of authorship. For the purposes | ||||
|       of this License, Derivative Works shall not include works that remain | ||||
|       separable from, or merely link (or bind by name) to the interfaces of, | ||||
|       the Work and Derivative Works thereof. | ||||
| 
 | ||||
|       "Contribution" shall mean any work of authorship, including | ||||
|       the original version of the Work and any modifications or additions | ||||
|       to that Work or Derivative Works thereof, that is intentionally | ||||
|       submitted to Licensor for inclusion in the Work by the copyright owner | ||||
|       or by an individual or Legal Entity authorized to submit on behalf of | ||||
|       the copyright owner. For the purposes of this definition, "submitted" | ||||
|       means any form of electronic, verbal, or written communication sent | ||||
|       to the Licensor or its representatives, including but not limited to | ||||
|       communication on electronic mailing lists, source code control systems, | ||||
|       and issue tracking systems that are managed by, or on behalf of, the | ||||
|       Licensor for the purpose of discussing and improving the Work, but | ||||
|       excluding communication that is conspicuously marked or otherwise | ||||
|       designated in writing by the copyright owner as "Not a Contribution." | ||||
| 
 | ||||
|       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||
|       on behalf of whom a Contribution has been received by Licensor and | ||||
|       subsequently incorporated within the Work. | ||||
| 
 | ||||
|    2. Grant of Copyright License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       copyright license to reproduce, prepare Derivative Works of, | ||||
|       publicly display, publicly perform, sublicense, and distribute the | ||||
|       Work and such Derivative Works in Source or Object form. | ||||
| 
 | ||||
|    3. Grant of Patent License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       (except as stated in this section) patent license to make, have made, | ||||
|       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||
|       where such license applies only to those patent claims licensable | ||||
|       by such Contributor that are necessarily infringed by their | ||||
|       Contribution(s) alone or by combination of their Contribution(s) | ||||
|       with the Work to which such Contribution(s) was submitted. If You | ||||
|       institute patent litigation against any entity (including a | ||||
|       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||
|       or a Contribution incorporated within the Work constitutes direct | ||||
|       or contributory patent infringement, then any patent licenses | ||||
|       granted to You under this License for that Work shall terminate | ||||
|       as of the date such litigation is filed. | ||||
| 
 | ||||
|    4. Redistribution. You may reproduce and distribute copies of the | ||||
|       Work or Derivative Works thereof in any medium, with or without | ||||
|       modifications, and in Source or Object form, provided that You | ||||
|       meet the following conditions: | ||||
| 
 | ||||
|       (a) You must give any other recipients of the Work or | ||||
|           Derivative Works a copy of this License; and | ||||
| 
 | ||||
|       (b) You must cause any modified files to carry prominent notices | ||||
|           stating that You changed the files; and | ||||
| 
 | ||||
|       (c) You must retain, in the Source form of any Derivative Works | ||||
|           that You distribute, all copyright, patent, trademark, and | ||||
|           attribution notices from the Source form of the Work, | ||||
|           excluding those notices that do not pertain to any part of | ||||
|           the Derivative Works; and | ||||
| 
 | ||||
|       (d) If the Work includes a "NOTICE" text file as part of its | ||||
|           distribution, then any Derivative Works that You distribute must | ||||
|           include a readable copy of the attribution notices contained | ||||
|           within such NOTICE file, excluding those notices that do not | ||||
|           pertain to any part of the Derivative Works, in at least one | ||||
|           of the following places: within a NOTICE text file distributed | ||||
|           as part of the Derivative Works; within the Source form or | ||||
|           documentation, if provided along with the Derivative Works; or, | ||||
|           within a display generated by the Derivative Works, if and | ||||
|           wherever such third-party notices normally appear. The contents | ||||
|           of the NOTICE file are for informational purposes only and | ||||
|           do not modify the License. You may add Your own attribution | ||||
|           notices within Derivative Works that You distribute, alongside | ||||
|           or as an addendum to the NOTICE text from the Work, provided | ||||
|           that such additional attribution notices cannot be construed | ||||
|           as modifying the License. | ||||
| 
 | ||||
|       You may add Your own copyright statement to Your modifications and | ||||
|       may provide additional or different license terms and conditions | ||||
|       for use, reproduction, or distribution of Your modifications, or | ||||
|       for any such Derivative Works as a whole, provided Your use, | ||||
|       reproduction, and distribution of the Work otherwise complies with | ||||
|       the conditions stated in this License. | ||||
| 
 | ||||
|    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||
|       any Contribution intentionally submitted for inclusion in the Work | ||||
|       by You to the Licensor shall be under the terms and conditions of | ||||
|       this License, without any additional terms or conditions. | ||||
|       Notwithstanding the above, nothing herein shall supersede or modify | ||||
|       the terms of any separate license agreement you may have executed | ||||
|       with Licensor regarding such Contributions. | ||||
| 
 | ||||
|    6. Trademarks. This License does not grant permission to use the trade | ||||
|       names, trademarks, service marks, or product names of the Licensor, | ||||
|       except as required for reasonable and customary use in describing the | ||||
|       origin of the Work and reproducing the content of the NOTICE file. | ||||
| 
 | ||||
|    7. Disclaimer of Warranty. Unless required by applicable law or | ||||
|       agreed to in writing, Licensor provides the Work (and each | ||||
|       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
|       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
|       implied, including, without limitation, any warranties or conditions | ||||
|       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||
|       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||
|       appropriateness of using or redistributing the Work and assume any | ||||
|       risks associated with Your exercise of permissions under this License. | ||||
| 
 | ||||
|    8. Limitation of Liability. In no event and under no legal theory, | ||||
|       whether in tort (including negligence), contract, or otherwise, | ||||
|       unless required by applicable law (such as deliberate and grossly | ||||
|       negligent acts) or agreed to in writing, shall any Contributor be | ||||
|       liable to You for damages, including any direct, indirect, special, | ||||
|       incidental, or consequential damages of any character arising as a | ||||
|       result of this License or out of the use or inability to use the | ||||
|       Work (including but not limited to damages for loss of goodwill, | ||||
|       work stoppage, computer failure or malfunction, or any and all | ||||
|       other commercial damages or losses), even if such Contributor | ||||
|       has been advised of the possibility of such damages. | ||||
| 
 | ||||
|    9. Accepting Warranty or Additional Liability. While redistributing | ||||
|       the Work or Derivative Works thereof, You may choose to offer, | ||||
|       and charge a fee for, acceptance of support, warranty, indemnity, | ||||
|       or other liability obligations and/or rights consistent with this | ||||
|       License. However, in accepting such obligations, You may act only | ||||
|       on Your own behalf and on Your sole responsibility, not on behalf | ||||
|       of any other Contributor, and only if You agree to indemnify, | ||||
|       defend, and hold each Contributor harmless for any liability | ||||
|       incurred by, or claims asserted against, such Contributor by reason | ||||
|       of your accepting any such warranty or additional liability. | ||||
| 
 | ||||
|    END OF TERMS AND CONDITIONS | ||||
| 
 | ||||
|    APPENDIX: How to apply the Apache License to your work. | ||||
| 
 | ||||
|       To apply the Apache License to your work, attach the following | ||||
|       boilerplate notice, with the fields enclosed by brackets "[]" | ||||
|       replaced with your own identifying information. (Don't include | ||||
|       the brackets!)  The text should be enclosed in the appropriate | ||||
|       comment syntax for the file format. We also recommend that a | ||||
|       file or class name and description of purpose be included on the | ||||
|       same "printed page" as the copyright notice for easier | ||||
|       identification within third-party archives. | ||||
| 
 | ||||
|    Copyright [yyyy] [name of copyright owner] | ||||
| 
 | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
| 
 | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
| 
 | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
|  | @ -1,118 +0,0 @@ | |||
| # Go JOSE  | ||||
| 
 | ||||
| [](https://godoc.org/gopkg.in/square/go-jose.v1) | ||||
| [](https://godoc.org/gopkg.in/square/go-jose.v2) | ||||
| [](https://raw.githubusercontent.com/square/go-jose/master/LICENSE) | ||||
| [](https://travis-ci.org/square/go-jose) | ||||
| [](https://coveralls.io/r/square/go-jose) | ||||
| 
 | ||||
| Package jose aims to provide an implementation of the Javascript Object Signing | ||||
| and Encryption set of standards. This includes support for JSON Web Encryption, | ||||
| JSON Web Signature, and JSON Web Token standards. | ||||
| 
 | ||||
| **Disclaimer**: This library contains encryption software that is subject to | ||||
| the U.S. Export Administration Regulations. You may not export, re-export, | ||||
| transfer or download this code or any part of it in violation of any United | ||||
| States law, directive or regulation. In particular this software may not be | ||||
| exported or re-exported in any form or on any media to Iran, North Sudan, | ||||
| Syria, Cuba, or North Korea, or to denied persons or entities mentioned on any | ||||
| US maintained blocked list. | ||||
| 
 | ||||
| ## Overview | ||||
| 
 | ||||
| The implementation follows the | ||||
| [JSON Web Encryption](http://dx.doi.org/10.17487/RFC7516) (RFC 7516), | ||||
| [JSON Web Signature](http://dx.doi.org/10.17487/RFC7515) (RFC 7515), and | ||||
| [JSON Web Token](http://dx.doi.org/10.17487/RFC7519) (RFC 7519). | ||||
| Tables of supported algorithms are shown below. The library supports both | ||||
| the compact and full serialization formats, and has optional support for | ||||
| multiple recipients. It also comes with a small command-line utility | ||||
| ([`jose-util`](https://github.com/square/go-jose/tree/v2/jose-util)) | ||||
| for dealing with JOSE messages in a shell. | ||||
| 
 | ||||
| **Note**: We use a forked version of the `encoding/json` package from the Go | ||||
| standard library which uses case-sensitive matching for member names (instead | ||||
| of [case-insensitive matching](https://www.ietf.org/mail-archive/web/json/current/msg03763.html)). | ||||
| This is to avoid differences in interpretation of messages between go-jose and | ||||
| libraries in other languages. | ||||
| 
 | ||||
| ### Versions | ||||
| 
 | ||||
| We use [gopkg.in](https://gopkg.in) for versioning. | ||||
| 
 | ||||
| [Version 2](https://gopkg.in/square/go-jose.v2) | ||||
| ([branch](https://github.com/square/go-jose/tree/v2), | ||||
| [doc](https://godoc.org/gopkg.in/square/go-jose.v2)) is the current version: | ||||
| 
 | ||||
|     import "gopkg.in/square/go-jose.v2" | ||||
| 
 | ||||
| The old `v1` branch ([go-jose.v1](https://gopkg.in/square/go-jose.v1)) will | ||||
| still receive backported bug fixes and security fixes, but otherwise | ||||
| development is frozen. All new feature development takes place on the `v2` | ||||
| branch. Version 2 also contains additional sub-packages such as the | ||||
| [jwt](https://godoc.org/gopkg.in/square/go-jose.v2/jwt) implementation | ||||
| contributed by [@shaxbee](https://github.com/shaxbee). | ||||
| 
 | ||||
| ### Supported algorithms | ||||
| 
 | ||||
| See below for a table of supported algorithms. Algorithm identifiers match | ||||
| the names in the [JSON Web Algorithms](http://dx.doi.org/10.17487/RFC7518) | ||||
| standard where possible. The Godoc reference has a list of constants. | ||||
| 
 | ||||
|  Key encryption             | Algorithm identifier(s) | ||||
|  :------------------------- | :------------------------------ | ||||
|  RSA-PKCS#1v1.5             | RSA1_5 | ||||
|  RSA-OAEP                   | RSA-OAEP, RSA-OAEP-256 | ||||
|  AES key wrap               | A128KW, A192KW, A256KW | ||||
|  AES-GCM key wrap           | A128GCMKW, A192GCMKW, A256GCMKW | ||||
|  ECDH-ES + AES key wrap     | ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW | ||||
|  ECDH-ES (direct)           | ECDH-ES<sup>1</sup> | ||||
|  Direct encryption          | dir<sup>1</sup> | ||||
| 
 | ||||
| <sup>1. Not supported in multi-recipient mode</sup> | ||||
| 
 | ||||
|  Signing / MAC              | Algorithm identifier(s) | ||||
|  :------------------------- | :------------------------------ | ||||
|  RSASSA-PKCS#1v1.5          | RS256, RS384, RS512 | ||||
|  RSASSA-PSS                 | PS256, PS384, PS512 | ||||
|  HMAC                       | HS256, HS384, HS512 | ||||
|  ECDSA                      | ES256, ES384, ES512 | ||||
|  Ed25519                    | EdDSA<sup>2</sup> | ||||
| 
 | ||||
| <sup>2. Only available in version 2 of the package</sup> | ||||
| 
 | ||||
|  Content encryption         | Algorithm identifier(s) | ||||
|  :------------------------- | :------------------------------ | ||||
|  AES-CBC+HMAC               | A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 | ||||
|  AES-GCM                    | A128GCM, A192GCM, A256GCM  | ||||
| 
 | ||||
|  Compression                | Algorithm identifiers(s) | ||||
|  :------------------------- | ------------------------------- | ||||
|  DEFLATE (RFC 1951)         | DEF | ||||
| 
 | ||||
| ### Supported key types | ||||
| 
 | ||||
| See below for a table of supported key types. These are understood by the | ||||
| library, and can be passed to corresponding functions such as `NewEncrypter` or | ||||
| `NewSigner`. Each of these keys can also be wrapped in a JWK if desired, which | ||||
| allows attaching a key id. | ||||
| 
 | ||||
|  Algorithm(s)               | Corresponding types | ||||
|  :------------------------- | ------------------------------- | ||||
|  RSA                        | *[rsa.PublicKey](http://golang.org/pkg/crypto/rsa/#PublicKey), *[rsa.PrivateKey](http://golang.org/pkg/crypto/rsa/#PrivateKey) | ||||
|  ECDH, ECDSA                | *[ecdsa.PublicKey](http://golang.org/pkg/crypto/ecdsa/#PublicKey), *[ecdsa.PrivateKey](http://golang.org/pkg/crypto/ecdsa/#PrivateKey) | ||||
|  EdDSA<sup>1</sup>          | [ed25519.PublicKey](https://godoc.org/golang.org/x/crypto/ed25519#PublicKey), [ed25519.PrivateKey](https://godoc.org/golang.org/x/crypto/ed25519#PrivateKey) | ||||
|  AES, HMAC                  | []byte | ||||
| 
 | ||||
| <sup>1. Only available in version 2 of the package</sup> | ||||
| 
 | ||||
| ## Examples | ||||
| 
 | ||||
| [](https://godoc.org/gopkg.in/square/go-jose.v1) | ||||
| [](https://godoc.org/gopkg.in/square/go-jose.v2) | ||||
| 
 | ||||
| Examples can be found in the Godoc | ||||
| reference for this package. The | ||||
| [`jose-util`](https://github.com/square/go-jose/tree/v2/jose-util) | ||||
| subdirectory also contains a small command-line utility which might be useful | ||||
| as an example. | ||||
|  | @ -1,592 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2014 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package jose | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto" | ||||
| 	"crypto/aes" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/rsa" | ||||
| 	"crypto/sha1" | ||||
| 	"crypto/sha256" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"math/big" | ||||
| 
 | ||||
| 	"golang.org/x/crypto/ed25519" | ||||
| 	josecipher "gopkg.in/square/go-jose.v2/cipher" | ||||
| 	"gopkg.in/square/go-jose.v2/json" | ||||
| ) | ||||
| 
 | ||||
| // A generic RSA-based encrypter/verifier
 | ||||
| type rsaEncrypterVerifier struct { | ||||
| 	publicKey *rsa.PublicKey | ||||
| } | ||||
| 
 | ||||
| // A generic RSA-based decrypter/signer
 | ||||
| type rsaDecrypterSigner struct { | ||||
| 	privateKey *rsa.PrivateKey | ||||
| } | ||||
| 
 | ||||
| // A generic EC-based encrypter/verifier
 | ||||
| type ecEncrypterVerifier struct { | ||||
| 	publicKey *ecdsa.PublicKey | ||||
| } | ||||
| 
 | ||||
| type edEncrypterVerifier struct { | ||||
| 	publicKey ed25519.PublicKey | ||||
| } | ||||
| 
 | ||||
| // A key generator for ECDH-ES
 | ||||
| type ecKeyGenerator struct { | ||||
| 	size      int | ||||
| 	algID     string | ||||
| 	publicKey *ecdsa.PublicKey | ||||
| } | ||||
| 
 | ||||
| // A generic EC-based decrypter/signer
 | ||||
| type ecDecrypterSigner struct { | ||||
| 	privateKey *ecdsa.PrivateKey | ||||
| } | ||||
| 
 | ||||
| type edDecrypterSigner struct { | ||||
| 	privateKey ed25519.PrivateKey | ||||
| } | ||||
| 
 | ||||
| // newRSARecipient creates recipientKeyInfo based on the given key.
 | ||||
| func newRSARecipient(keyAlg KeyAlgorithm, publicKey *rsa.PublicKey) (recipientKeyInfo, error) { | ||||
| 	// Verify that key management algorithm is supported by this encrypter
 | ||||
| 	switch keyAlg { | ||||
| 	case RSA1_5, RSA_OAEP, RSA_OAEP_256: | ||||
| 	default: | ||||
| 		return recipientKeyInfo{}, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	if publicKey == nil { | ||||
| 		return recipientKeyInfo{}, errors.New("invalid public key") | ||||
| 	} | ||||
| 
 | ||||
| 	return recipientKeyInfo{ | ||||
| 		keyAlg: keyAlg, | ||||
| 		keyEncrypter: &rsaEncrypterVerifier{ | ||||
| 			publicKey: publicKey, | ||||
| 		}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // newRSASigner creates a recipientSigInfo based on the given key.
 | ||||
| func newRSASigner(sigAlg SignatureAlgorithm, privateKey *rsa.PrivateKey) (recipientSigInfo, error) { | ||||
| 	// Verify that key management algorithm is supported by this encrypter
 | ||||
| 	switch sigAlg { | ||||
| 	case RS256, RS384, RS512, PS256, PS384, PS512: | ||||
| 	default: | ||||
| 		return recipientSigInfo{}, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	if privateKey == nil { | ||||
| 		return recipientSigInfo{}, errors.New("invalid private key") | ||||
| 	} | ||||
| 
 | ||||
| 	return recipientSigInfo{ | ||||
| 		sigAlg: sigAlg, | ||||
| 		publicKey: staticPublicKey(&JSONWebKey{ | ||||
| 			Key: privateKey.Public(), | ||||
| 		}), | ||||
| 		signer: &rsaDecrypterSigner{ | ||||
| 			privateKey: privateKey, | ||||
| 		}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func newEd25519Signer(sigAlg SignatureAlgorithm, privateKey ed25519.PrivateKey) (recipientSigInfo, error) { | ||||
| 	if sigAlg != EdDSA { | ||||
| 		return recipientSigInfo{}, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	if privateKey == nil { | ||||
| 		return recipientSigInfo{}, errors.New("invalid private key") | ||||
| 	} | ||||
| 	return recipientSigInfo{ | ||||
| 		sigAlg: sigAlg, | ||||
| 		publicKey: staticPublicKey(&JSONWebKey{ | ||||
| 			Key: privateKey.Public(), | ||||
| 		}), | ||||
| 		signer: &edDecrypterSigner{ | ||||
| 			privateKey: privateKey, | ||||
| 		}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // newECDHRecipient creates recipientKeyInfo based on the given key.
 | ||||
| func newECDHRecipient(keyAlg KeyAlgorithm, publicKey *ecdsa.PublicKey) (recipientKeyInfo, error) { | ||||
| 	// Verify that key management algorithm is supported by this encrypter
 | ||||
| 	switch keyAlg { | ||||
| 	case ECDH_ES, ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW: | ||||
| 	default: | ||||
| 		return recipientKeyInfo{}, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	if publicKey == nil || !publicKey.Curve.IsOnCurve(publicKey.X, publicKey.Y) { | ||||
| 		return recipientKeyInfo{}, errors.New("invalid public key") | ||||
| 	} | ||||
| 
 | ||||
| 	return recipientKeyInfo{ | ||||
| 		keyAlg: keyAlg, | ||||
| 		keyEncrypter: &ecEncrypterVerifier{ | ||||
| 			publicKey: publicKey, | ||||
| 		}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // newECDSASigner creates a recipientSigInfo based on the given key.
 | ||||
| func newECDSASigner(sigAlg SignatureAlgorithm, privateKey *ecdsa.PrivateKey) (recipientSigInfo, error) { | ||||
| 	// Verify that key management algorithm is supported by this encrypter
 | ||||
| 	switch sigAlg { | ||||
| 	case ES256, ES384, ES512: | ||||
| 	default: | ||||
| 		return recipientSigInfo{}, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	if privateKey == nil { | ||||
| 		return recipientSigInfo{}, errors.New("invalid private key") | ||||
| 	} | ||||
| 
 | ||||
| 	return recipientSigInfo{ | ||||
| 		sigAlg: sigAlg, | ||||
| 		publicKey: staticPublicKey(&JSONWebKey{ | ||||
| 			Key: privateKey.Public(), | ||||
| 		}), | ||||
| 		signer: &ecDecrypterSigner{ | ||||
| 			privateKey: privateKey, | ||||
| 		}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // Encrypt the given payload and update the object.
 | ||||
| func (ctx rsaEncrypterVerifier) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) { | ||||
| 	encryptedKey, err := ctx.encrypt(cek, alg) | ||||
| 	if err != nil { | ||||
| 		return recipientInfo{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	return recipientInfo{ | ||||
| 		encryptedKey: encryptedKey, | ||||
| 		header:       &rawHeader{}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // Encrypt the given payload. Based on the key encryption algorithm,
 | ||||
| // this will either use RSA-PKCS1v1.5 or RSA-OAEP (with SHA-1 or SHA-256).
 | ||||
| func (ctx rsaEncrypterVerifier) encrypt(cek []byte, alg KeyAlgorithm) ([]byte, error) { | ||||
| 	switch alg { | ||||
| 	case RSA1_5: | ||||
| 		return rsa.EncryptPKCS1v15(RandReader, ctx.publicKey, cek) | ||||
| 	case RSA_OAEP: | ||||
| 		return rsa.EncryptOAEP(sha1.New(), RandReader, ctx.publicKey, cek, []byte{}) | ||||
| 	case RSA_OAEP_256: | ||||
| 		return rsa.EncryptOAEP(sha256.New(), RandReader, ctx.publicKey, cek, []byte{}) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil, ErrUnsupportedAlgorithm | ||||
| } | ||||
| 
 | ||||
| // Decrypt the given payload and return the content encryption key.
 | ||||
| func (ctx rsaDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) { | ||||
| 	return ctx.decrypt(recipient.encryptedKey, headers.getAlgorithm(), generator) | ||||
| } | ||||
| 
 | ||||
| // Decrypt the given payload. Based on the key encryption algorithm,
 | ||||
| // this will either use RSA-PKCS1v1.5 or RSA-OAEP (with SHA-1 or SHA-256).
 | ||||
| func (ctx rsaDecrypterSigner) decrypt(jek []byte, alg KeyAlgorithm, generator keyGenerator) ([]byte, error) { | ||||
| 	// Note: The random reader on decrypt operations is only used for blinding,
 | ||||
| 	// so stubbing is meanlingless (hence the direct use of rand.Reader).
 | ||||
| 	switch alg { | ||||
| 	case RSA1_5: | ||||
| 		defer func() { | ||||
| 			// DecryptPKCS1v15SessionKey sometimes panics on an invalid payload
 | ||||
| 			// because of an index out of bounds error, which we want to ignore.
 | ||||
| 			// This has been fixed in Go 1.3.1 (released 2014/08/13), the recover()
 | ||||
| 			// only exists for preventing crashes with unpatched versions.
 | ||||
| 			// See: https://groups.google.com/forum/#!topic/golang-dev/7ihX6Y6kx9k
 | ||||
| 			// See: https://code.google.com/p/go/source/detail?r=58ee390ff31602edb66af41ed10901ec95904d33
 | ||||
| 			_ = recover() | ||||
| 		}() | ||||
| 
 | ||||
| 		// Perform some input validation.
 | ||||
| 		keyBytes := ctx.privateKey.PublicKey.N.BitLen() / 8 | ||||
| 		if keyBytes != len(jek) { | ||||
| 			// Input size is incorrect, the encrypted payload should always match
 | ||||
| 			// the size of the public modulus (e.g. using a 2048 bit key will
 | ||||
| 			// produce 256 bytes of output). Reject this since it's invalid input.
 | ||||
| 			return nil, ErrCryptoFailure | ||||
| 		} | ||||
| 
 | ||||
| 		cek, _, err := generator.genKey() | ||||
| 		if err != nil { | ||||
| 			return nil, ErrCryptoFailure | ||||
| 		} | ||||
| 
 | ||||
| 		// When decrypting an RSA-PKCS1v1.5 payload, we must take precautions to
 | ||||
| 		// prevent chosen-ciphertext attacks as described in RFC 3218, "Preventing
 | ||||
| 		// the Million Message Attack on Cryptographic Message Syntax". We are
 | ||||
| 		// therefore deliberately ignoring errors here.
 | ||||
| 		_ = rsa.DecryptPKCS1v15SessionKey(rand.Reader, ctx.privateKey, jek, cek) | ||||
| 
 | ||||
| 		return cek, nil | ||||
| 	case RSA_OAEP: | ||||
| 		// Use rand.Reader for RSA blinding
 | ||||
| 		return rsa.DecryptOAEP(sha1.New(), rand.Reader, ctx.privateKey, jek, []byte{}) | ||||
| 	case RSA_OAEP_256: | ||||
| 		// Use rand.Reader for RSA blinding
 | ||||
| 		return rsa.DecryptOAEP(sha256.New(), rand.Reader, ctx.privateKey, jek, []byte{}) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil, ErrUnsupportedAlgorithm | ||||
| } | ||||
| 
 | ||||
| // Sign the given payload
 | ||||
| func (ctx rsaDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) { | ||||
| 	var hash crypto.Hash | ||||
| 
 | ||||
| 	switch alg { | ||||
| 	case RS256, PS256: | ||||
| 		hash = crypto.SHA256 | ||||
| 	case RS384, PS384: | ||||
| 		hash = crypto.SHA384 | ||||
| 	case RS512, PS512: | ||||
| 		hash = crypto.SHA512 | ||||
| 	default: | ||||
| 		return Signature{}, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	hasher := hash.New() | ||||
| 
 | ||||
| 	// According to documentation, Write() on hash never fails
 | ||||
| 	_, _ = hasher.Write(payload) | ||||
| 	hashed := hasher.Sum(nil) | ||||
| 
 | ||||
| 	var out []byte | ||||
| 	var err error | ||||
| 
 | ||||
| 	switch alg { | ||||
| 	case RS256, RS384, RS512: | ||||
| 		out, err = rsa.SignPKCS1v15(RandReader, ctx.privateKey, hash, hashed) | ||||
| 	case PS256, PS384, PS512: | ||||
| 		out, err = rsa.SignPSS(RandReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{ | ||||
| 			SaltLength: rsa.PSSSaltLengthEqualsHash, | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return Signature{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	return Signature{ | ||||
| 		Signature: out, | ||||
| 		protected: &rawHeader{}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // Verify the given payload
 | ||||
| func (ctx rsaEncrypterVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error { | ||||
| 	var hash crypto.Hash | ||||
| 
 | ||||
| 	switch alg { | ||||
| 	case RS256, PS256: | ||||
| 		hash = crypto.SHA256 | ||||
| 	case RS384, PS384: | ||||
| 		hash = crypto.SHA384 | ||||
| 	case RS512, PS512: | ||||
| 		hash = crypto.SHA512 | ||||
| 	default: | ||||
| 		return ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	hasher := hash.New() | ||||
| 
 | ||||
| 	// According to documentation, Write() on hash never fails
 | ||||
| 	_, _ = hasher.Write(payload) | ||||
| 	hashed := hasher.Sum(nil) | ||||
| 
 | ||||
| 	switch alg { | ||||
| 	case RS256, RS384, RS512: | ||||
| 		return rsa.VerifyPKCS1v15(ctx.publicKey, hash, hashed, signature) | ||||
| 	case PS256, PS384, PS512: | ||||
| 		return rsa.VerifyPSS(ctx.publicKey, hash, hashed, signature, nil) | ||||
| 	} | ||||
| 
 | ||||
| 	return ErrUnsupportedAlgorithm | ||||
| } | ||||
| 
 | ||||
| // Encrypt the given payload and update the object.
 | ||||
| func (ctx ecEncrypterVerifier) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) { | ||||
| 	switch alg { | ||||
| 	case ECDH_ES: | ||||
| 		// ECDH-ES mode doesn't wrap a key, the shared secret is used directly as the key.
 | ||||
| 		return recipientInfo{ | ||||
| 			header: &rawHeader{}, | ||||
| 		}, nil | ||||
| 	case ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW: | ||||
| 	default: | ||||
| 		return recipientInfo{}, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	generator := ecKeyGenerator{ | ||||
| 		algID:     string(alg), | ||||
| 		publicKey: ctx.publicKey, | ||||
| 	} | ||||
| 
 | ||||
| 	switch alg { | ||||
| 	case ECDH_ES_A128KW: | ||||
| 		generator.size = 16 | ||||
| 	case ECDH_ES_A192KW: | ||||
| 		generator.size = 24 | ||||
| 	case ECDH_ES_A256KW: | ||||
| 		generator.size = 32 | ||||
| 	} | ||||
| 
 | ||||
| 	kek, header, err := generator.genKey() | ||||
| 	if err != nil { | ||||
| 		return recipientInfo{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	block, err := aes.NewCipher(kek) | ||||
| 	if err != nil { | ||||
| 		return recipientInfo{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	jek, err := josecipher.KeyWrap(block, cek) | ||||
| 	if err != nil { | ||||
| 		return recipientInfo{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	return recipientInfo{ | ||||
| 		encryptedKey: jek, | ||||
| 		header:       &header, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // Get key size for EC key generator
 | ||||
| func (ctx ecKeyGenerator) keySize() int { | ||||
| 	return ctx.size | ||||
| } | ||||
| 
 | ||||
| // Get a content encryption key for ECDH-ES
 | ||||
| func (ctx ecKeyGenerator) genKey() ([]byte, rawHeader, error) { | ||||
| 	priv, err := ecdsa.GenerateKey(ctx.publicKey.Curve, RandReader) | ||||
| 	if err != nil { | ||||
| 		return nil, rawHeader{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	out := josecipher.DeriveECDHES(ctx.algID, []byte{}, []byte{}, priv, ctx.publicKey, ctx.size) | ||||
| 
 | ||||
| 	b, err := json.Marshal(&JSONWebKey{ | ||||
| 		Key: &priv.PublicKey, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	headers := rawHeader{ | ||||
| 		headerEPK: makeRawMessage(b), | ||||
| 	} | ||||
| 
 | ||||
| 	return out, headers, nil | ||||
| } | ||||
| 
 | ||||
| // Decrypt the given payload and return the content encryption key.
 | ||||
| func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) { | ||||
| 	epk, err := headers.getEPK() | ||||
| 	if err != nil { | ||||
| 		return nil, errors.New("square/go-jose: invalid epk header") | ||||
| 	} | ||||
| 	if epk == nil { | ||||
| 		return nil, errors.New("square/go-jose: missing epk header") | ||||
| 	} | ||||
| 
 | ||||
| 	publicKey, ok := epk.Key.(*ecdsa.PublicKey) | ||||
| 	if publicKey == nil || !ok { | ||||
| 		return nil, errors.New("square/go-jose: invalid epk header") | ||||
| 	} | ||||
| 
 | ||||
| 	if !ctx.privateKey.Curve.IsOnCurve(publicKey.X, publicKey.Y) { | ||||
| 		return nil, errors.New("square/go-jose: invalid public key in epk header") | ||||
| 	} | ||||
| 
 | ||||
| 	apuData, err := headers.getAPU() | ||||
| 	if err != nil { | ||||
| 		return nil, errors.New("square/go-jose: invalid apu header") | ||||
| 	} | ||||
| 	apvData, err := headers.getAPV() | ||||
| 	if err != nil { | ||||
| 		return nil, errors.New("square/go-jose: invalid apv header") | ||||
| 	} | ||||
| 
 | ||||
| 	deriveKey := func(algID string, size int) []byte { | ||||
| 		return josecipher.DeriveECDHES(algID, apuData.bytes(), apvData.bytes(), ctx.privateKey, publicKey, size) | ||||
| 	} | ||||
| 
 | ||||
| 	var keySize int | ||||
| 
 | ||||
| 	algorithm := headers.getAlgorithm() | ||||
| 	switch algorithm { | ||||
| 	case ECDH_ES: | ||||
| 		// ECDH-ES uses direct key agreement, no key unwrapping necessary.
 | ||||
| 		return deriveKey(string(headers.getEncryption()), generator.keySize()), nil | ||||
| 	case ECDH_ES_A128KW: | ||||
| 		keySize = 16 | ||||
| 	case ECDH_ES_A192KW: | ||||
| 		keySize = 24 | ||||
| 	case ECDH_ES_A256KW: | ||||
| 		keySize = 32 | ||||
| 	default: | ||||
| 		return nil, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	key := deriveKey(string(algorithm), keySize) | ||||
| 	block, err := aes.NewCipher(key) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return josecipher.KeyUnwrap(block, recipient.encryptedKey) | ||||
| } | ||||
| 
 | ||||
| func (ctx edDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) { | ||||
| 	if alg != EdDSA { | ||||
| 		return Signature{}, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	sig, err := ctx.privateKey.Sign(RandReader, payload, crypto.Hash(0)) | ||||
| 	if err != nil { | ||||
| 		return Signature{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	return Signature{ | ||||
| 		Signature: sig, | ||||
| 		protected: &rawHeader{}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (ctx edEncrypterVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error { | ||||
| 	if alg != EdDSA { | ||||
| 		return ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 	ok := ed25519.Verify(ctx.publicKey, payload, signature) | ||||
| 	if !ok { | ||||
| 		return errors.New("square/go-jose: ed25519 signature failed to verify") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Sign the given payload
 | ||||
| func (ctx ecDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) { | ||||
| 	var expectedBitSize int | ||||
| 	var hash crypto.Hash | ||||
| 
 | ||||
| 	switch alg { | ||||
| 	case ES256: | ||||
| 		expectedBitSize = 256 | ||||
| 		hash = crypto.SHA256 | ||||
| 	case ES384: | ||||
| 		expectedBitSize = 384 | ||||
| 		hash = crypto.SHA384 | ||||
| 	case ES512: | ||||
| 		expectedBitSize = 521 | ||||
| 		hash = crypto.SHA512 | ||||
| 	} | ||||
| 
 | ||||
| 	curveBits := ctx.privateKey.Curve.Params().BitSize | ||||
| 	if expectedBitSize != curveBits { | ||||
| 		return Signature{}, fmt.Errorf("square/go-jose: expected %d bit key, got %d bits instead", expectedBitSize, curveBits) | ||||
| 	} | ||||
| 
 | ||||
| 	hasher := hash.New() | ||||
| 
 | ||||
| 	// According to documentation, Write() on hash never fails
 | ||||
| 	_, _ = hasher.Write(payload) | ||||
| 	hashed := hasher.Sum(nil) | ||||
| 
 | ||||
| 	r, s, err := ecdsa.Sign(RandReader, ctx.privateKey, hashed) | ||||
| 	if err != nil { | ||||
| 		return Signature{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	keyBytes := curveBits / 8 | ||||
| 	if curveBits%8 > 0 { | ||||
| 		keyBytes++ | ||||
| 	} | ||||
| 
 | ||||
| 	// We serialize the outputs (r and s) into big-endian byte arrays and pad
 | ||||
| 	// them with zeros on the left to make sure the sizes work out. Both arrays
 | ||||
| 	// must be keyBytes long, and the output must be 2*keyBytes long.
 | ||||
| 	rBytes := r.Bytes() | ||||
| 	rBytesPadded := make([]byte, keyBytes) | ||||
| 	copy(rBytesPadded[keyBytes-len(rBytes):], rBytes) | ||||
| 
 | ||||
| 	sBytes := s.Bytes() | ||||
| 	sBytesPadded := make([]byte, keyBytes) | ||||
| 	copy(sBytesPadded[keyBytes-len(sBytes):], sBytes) | ||||
| 
 | ||||
| 	out := append(rBytesPadded, sBytesPadded...) | ||||
| 
 | ||||
| 	return Signature{ | ||||
| 		Signature: out, | ||||
| 		protected: &rawHeader{}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // Verify the given payload
 | ||||
| func (ctx ecEncrypterVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error { | ||||
| 	var keySize int | ||||
| 	var hash crypto.Hash | ||||
| 
 | ||||
| 	switch alg { | ||||
| 	case ES256: | ||||
| 		keySize = 32 | ||||
| 		hash = crypto.SHA256 | ||||
| 	case ES384: | ||||
| 		keySize = 48 | ||||
| 		hash = crypto.SHA384 | ||||
| 	case ES512: | ||||
| 		keySize = 66 | ||||
| 		hash = crypto.SHA512 | ||||
| 	default: | ||||
| 		return ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	if len(signature) != 2*keySize { | ||||
| 		return fmt.Errorf("square/go-jose: invalid signature size, have %d bytes, wanted %d", len(signature), 2*keySize) | ||||
| 	} | ||||
| 
 | ||||
| 	hasher := hash.New() | ||||
| 
 | ||||
| 	// According to documentation, Write() on hash never fails
 | ||||
| 	_, _ = hasher.Write(payload) | ||||
| 	hashed := hasher.Sum(nil) | ||||
| 
 | ||||
| 	r := big.NewInt(0).SetBytes(signature[:keySize]) | ||||
| 	s := big.NewInt(0).SetBytes(signature[keySize:]) | ||||
| 
 | ||||
| 	match := ecdsa.Verify(ctx.publicKey, hashed, r, s) | ||||
| 	if !match { | ||||
| 		return errors.New("square/go-jose: ecdsa signature failed to verify") | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
|  | @ -1,196 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2014 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package josecipher | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/cipher" | ||||
| 	"crypto/hmac" | ||||
| 	"crypto/sha256" | ||||
| 	"crypto/sha512" | ||||
| 	"crypto/subtle" | ||||
| 	"encoding/binary" | ||||
| 	"errors" | ||||
| 	"hash" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	nonceBytes = 16 | ||||
| ) | ||||
| 
 | ||||
| // NewCBCHMAC instantiates a new AEAD based on CBC+HMAC.
 | ||||
| func NewCBCHMAC(key []byte, newBlockCipher func([]byte) (cipher.Block, error)) (cipher.AEAD, error) { | ||||
| 	keySize := len(key) / 2 | ||||
| 	integrityKey := key[:keySize] | ||||
| 	encryptionKey := key[keySize:] | ||||
| 
 | ||||
| 	blockCipher, err := newBlockCipher(encryptionKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	var hash func() hash.Hash | ||||
| 	switch keySize { | ||||
| 	case 16: | ||||
| 		hash = sha256.New | ||||
| 	case 24: | ||||
| 		hash = sha512.New384 | ||||
| 	case 32: | ||||
| 		hash = sha512.New | ||||
| 	} | ||||
| 
 | ||||
| 	return &cbcAEAD{ | ||||
| 		hash:         hash, | ||||
| 		blockCipher:  blockCipher, | ||||
| 		authtagBytes: keySize, | ||||
| 		integrityKey: integrityKey, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // An AEAD based on CBC+HMAC
 | ||||
| type cbcAEAD struct { | ||||
| 	hash         func() hash.Hash | ||||
| 	authtagBytes int | ||||
| 	integrityKey []byte | ||||
| 	blockCipher  cipher.Block | ||||
| } | ||||
| 
 | ||||
| func (ctx *cbcAEAD) NonceSize() int { | ||||
| 	return nonceBytes | ||||
| } | ||||
| 
 | ||||
| func (ctx *cbcAEAD) Overhead() int { | ||||
| 	// Maximum overhead is block size (for padding) plus auth tag length, where
 | ||||
| 	// the length of the auth tag is equivalent to the key size.
 | ||||
| 	return ctx.blockCipher.BlockSize() + ctx.authtagBytes | ||||
| } | ||||
| 
 | ||||
| // Seal encrypts and authenticates the plaintext.
 | ||||
| func (ctx *cbcAEAD) Seal(dst, nonce, plaintext, data []byte) []byte { | ||||
| 	// Output buffer -- must take care not to mangle plaintext input.
 | ||||
| 	ciphertext := make([]byte, uint64(len(plaintext))+uint64(ctx.Overhead()))[:len(plaintext)] | ||||
| 	copy(ciphertext, plaintext) | ||||
| 	ciphertext = padBuffer(ciphertext, ctx.blockCipher.BlockSize()) | ||||
| 
 | ||||
| 	cbc := cipher.NewCBCEncrypter(ctx.blockCipher, nonce) | ||||
| 
 | ||||
| 	cbc.CryptBlocks(ciphertext, ciphertext) | ||||
| 	authtag := ctx.computeAuthTag(data, nonce, ciphertext) | ||||
| 
 | ||||
| 	ret, out := resize(dst, uint64(len(dst))+uint64(len(ciphertext))+uint64(len(authtag))) | ||||
| 	copy(out, ciphertext) | ||||
| 	copy(out[len(ciphertext):], authtag) | ||||
| 
 | ||||
| 	return ret | ||||
| } | ||||
| 
 | ||||
| // Open decrypts and authenticates the ciphertext.
 | ||||
| func (ctx *cbcAEAD) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { | ||||
| 	if len(ciphertext) < ctx.authtagBytes { | ||||
| 		return nil, errors.New("square/go-jose: invalid ciphertext (too short)") | ||||
| 	} | ||||
| 
 | ||||
| 	offset := len(ciphertext) - ctx.authtagBytes | ||||
| 	expectedTag := ctx.computeAuthTag(data, nonce, ciphertext[:offset]) | ||||
| 	match := subtle.ConstantTimeCompare(expectedTag, ciphertext[offset:]) | ||||
| 	if match != 1 { | ||||
| 		return nil, errors.New("square/go-jose: invalid ciphertext (auth tag mismatch)") | ||||
| 	} | ||||
| 
 | ||||
| 	cbc := cipher.NewCBCDecrypter(ctx.blockCipher, nonce) | ||||
| 
 | ||||
| 	// Make copy of ciphertext buffer, don't want to modify in place
 | ||||
| 	buffer := append([]byte{}, []byte(ciphertext[:offset])...) | ||||
| 
 | ||||
| 	if len(buffer)%ctx.blockCipher.BlockSize() > 0 { | ||||
| 		return nil, errors.New("square/go-jose: invalid ciphertext (invalid length)") | ||||
| 	} | ||||
| 
 | ||||
| 	cbc.CryptBlocks(buffer, buffer) | ||||
| 
 | ||||
| 	// Remove padding
 | ||||
| 	plaintext, err := unpadBuffer(buffer, ctx.blockCipher.BlockSize()) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	ret, out := resize(dst, uint64(len(dst))+uint64(len(plaintext))) | ||||
| 	copy(out, plaintext) | ||||
| 
 | ||||
| 	return ret, nil | ||||
| } | ||||
| 
 | ||||
| // Compute an authentication tag
 | ||||
| func (ctx *cbcAEAD) computeAuthTag(aad, nonce, ciphertext []byte) []byte { | ||||
| 	buffer := make([]byte, uint64(len(aad))+uint64(len(nonce))+uint64(len(ciphertext))+8) | ||||
| 	n := 0 | ||||
| 	n += copy(buffer, aad) | ||||
| 	n += copy(buffer[n:], nonce) | ||||
| 	n += copy(buffer[n:], ciphertext) | ||||
| 	binary.BigEndian.PutUint64(buffer[n:], uint64(len(aad))*8) | ||||
| 
 | ||||
| 	// According to documentation, Write() on hash.Hash never fails.
 | ||||
| 	hmac := hmac.New(ctx.hash, ctx.integrityKey) | ||||
| 	_, _ = hmac.Write(buffer) | ||||
| 
 | ||||
| 	return hmac.Sum(nil)[:ctx.authtagBytes] | ||||
| } | ||||
| 
 | ||||
| // resize ensures that the given slice has a capacity of at least n bytes.
 | ||||
| // If the capacity of the slice is less than n, a new slice is allocated
 | ||||
| // and the existing data will be copied.
 | ||||
| func resize(in []byte, n uint64) (head, tail []byte) { | ||||
| 	if uint64(cap(in)) >= n { | ||||
| 		head = in[:n] | ||||
| 	} else { | ||||
| 		head = make([]byte, n) | ||||
| 		copy(head, in) | ||||
| 	} | ||||
| 
 | ||||
| 	tail = head[len(in):] | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Apply padding
 | ||||
| func padBuffer(buffer []byte, blockSize int) []byte { | ||||
| 	missing := blockSize - (len(buffer) % blockSize) | ||||
| 	ret, out := resize(buffer, uint64(len(buffer))+uint64(missing)) | ||||
| 	padding := bytes.Repeat([]byte{byte(missing)}, missing) | ||||
| 	copy(out, padding) | ||||
| 	return ret | ||||
| } | ||||
| 
 | ||||
| // Remove padding
 | ||||
| func unpadBuffer(buffer []byte, blockSize int) ([]byte, error) { | ||||
| 	if len(buffer)%blockSize != 0 { | ||||
| 		return nil, errors.New("square/go-jose: invalid padding") | ||||
| 	} | ||||
| 
 | ||||
| 	last := buffer[len(buffer)-1] | ||||
| 	count := int(last) | ||||
| 
 | ||||
| 	if count == 0 || count > blockSize || count > len(buffer) { | ||||
| 		return nil, errors.New("square/go-jose: invalid padding") | ||||
| 	} | ||||
| 
 | ||||
| 	padding := bytes.Repeat([]byte{last}, count) | ||||
| 	if !bytes.HasSuffix(buffer, padding) { | ||||
| 		return nil, errors.New("square/go-jose: invalid padding") | ||||
| 	} | ||||
| 
 | ||||
| 	return buffer[:len(buffer)-count], nil | ||||
| } | ||||
|  | @ -1,75 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2014 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package josecipher | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto" | ||||
| 	"encoding/binary" | ||||
| 	"hash" | ||||
| 	"io" | ||||
| ) | ||||
| 
 | ||||
| type concatKDF struct { | ||||
| 	z, info []byte | ||||
| 	i       uint32 | ||||
| 	cache   []byte | ||||
| 	hasher  hash.Hash | ||||
| } | ||||
| 
 | ||||
| // NewConcatKDF builds a KDF reader based on the given inputs.
 | ||||
| func NewConcatKDF(hash crypto.Hash, z, algID, ptyUInfo, ptyVInfo, supPubInfo, supPrivInfo []byte) io.Reader { | ||||
| 	buffer := make([]byte, uint64(len(algID))+uint64(len(ptyUInfo))+uint64(len(ptyVInfo))+uint64(len(supPubInfo))+uint64(len(supPrivInfo))) | ||||
| 	n := 0 | ||||
| 	n += copy(buffer, algID) | ||||
| 	n += copy(buffer[n:], ptyUInfo) | ||||
| 	n += copy(buffer[n:], ptyVInfo) | ||||
| 	n += copy(buffer[n:], supPubInfo) | ||||
| 	copy(buffer[n:], supPrivInfo) | ||||
| 
 | ||||
| 	hasher := hash.New() | ||||
| 
 | ||||
| 	return &concatKDF{ | ||||
| 		z:      z, | ||||
| 		info:   buffer, | ||||
| 		hasher: hasher, | ||||
| 		cache:  []byte{}, | ||||
| 		i:      1, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (ctx *concatKDF) Read(out []byte) (int, error) { | ||||
| 	copied := copy(out, ctx.cache) | ||||
| 	ctx.cache = ctx.cache[copied:] | ||||
| 
 | ||||
| 	for copied < len(out) { | ||||
| 		ctx.hasher.Reset() | ||||
| 
 | ||||
| 		// Write on a hash.Hash never fails
 | ||||
| 		_ = binary.Write(ctx.hasher, binary.BigEndian, ctx.i) | ||||
| 		_, _ = ctx.hasher.Write(ctx.z) | ||||
| 		_, _ = ctx.hasher.Write(ctx.info) | ||||
| 
 | ||||
| 		hash := ctx.hasher.Sum(nil) | ||||
| 		chunkCopied := copy(out[copied:], hash) | ||||
| 		copied += chunkCopied | ||||
| 		ctx.cache = hash[chunkCopied:] | ||||
| 
 | ||||
| 		ctx.i++ | ||||
| 	} | ||||
| 
 | ||||
| 	return copied, nil | ||||
| } | ||||
|  | @ -1,86 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2014 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package josecipher | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/elliptic" | ||||
| 	"encoding/binary" | ||||
| ) | ||||
| 
 | ||||
| // DeriveECDHES derives a shared encryption key using ECDH/ConcatKDF as described in JWE/JWA.
 | ||||
| // It is an error to call this function with a private/public key that are not on the same
 | ||||
| // curve. Callers must ensure that the keys are valid before calling this function. Output
 | ||||
| // size may be at most 1<<16 bytes (64 KiB).
 | ||||
| func DeriveECDHES(alg string, apuData, apvData []byte, priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey, size int) []byte { | ||||
| 	if size > 1<<16 { | ||||
| 		panic("ECDH-ES output size too large, must be less than or equal to 1<<16") | ||||
| 	} | ||||
| 
 | ||||
| 	// algId, partyUInfo, partyVInfo inputs must be prefixed with the length
 | ||||
| 	algID := lengthPrefixed([]byte(alg)) | ||||
| 	ptyUInfo := lengthPrefixed(apuData) | ||||
| 	ptyVInfo := lengthPrefixed(apvData) | ||||
| 
 | ||||
| 	// suppPubInfo is the encoded length of the output size in bits
 | ||||
| 	supPubInfo := make([]byte, 4) | ||||
| 	binary.BigEndian.PutUint32(supPubInfo, uint32(size)*8) | ||||
| 
 | ||||
| 	if !priv.PublicKey.Curve.IsOnCurve(pub.X, pub.Y) { | ||||
| 		panic("public key not on same curve as private key") | ||||
| 	} | ||||
| 
 | ||||
| 	z, _ := priv.Curve.ScalarMult(pub.X, pub.Y, priv.D.Bytes()) | ||||
| 	zBytes := z.Bytes() | ||||
| 
 | ||||
| 	// Note that calling z.Bytes() on a big.Int may strip leading zero bytes from
 | ||||
| 	// the returned byte array. This can lead to a problem where zBytes will be
 | ||||
| 	// shorter than expected which breaks the key derivation. Therefore we must pad
 | ||||
| 	// to the full length of the expected coordinate here before calling the KDF.
 | ||||
| 	octSize := dSize(priv.Curve) | ||||
| 	if len(zBytes) != octSize { | ||||
| 		zBytes = append(bytes.Repeat([]byte{0}, octSize-len(zBytes)), zBytes...) | ||||
| 	} | ||||
| 
 | ||||
| 	reader := NewConcatKDF(crypto.SHA256, zBytes, algID, ptyUInfo, ptyVInfo, supPubInfo, []byte{}) | ||||
| 	key := make([]byte, size) | ||||
| 
 | ||||
| 	// Read on the KDF will never fail
 | ||||
| 	_, _ = reader.Read(key) | ||||
| 
 | ||||
| 	return key | ||||
| } | ||||
| 
 | ||||
| // dSize returns the size in octets for a coordinate on a elliptic curve.
 | ||||
| func dSize(curve elliptic.Curve) int { | ||||
| 	order := curve.Params().P | ||||
| 	bitLen := order.BitLen() | ||||
| 	size := bitLen / 8 | ||||
| 	if bitLen%8 != 0 { | ||||
| 		size++ | ||||
| 	} | ||||
| 	return size | ||||
| } | ||||
| 
 | ||||
| func lengthPrefixed(data []byte) []byte { | ||||
| 	out := make([]byte, len(data)+4) | ||||
| 	binary.BigEndian.PutUint32(out, uint32(len(data))) | ||||
| 	copy(out[4:], data) | ||||
| 	return out | ||||
| } | ||||
|  | @ -1,109 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2014 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package josecipher | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/cipher" | ||||
| 	"crypto/subtle" | ||||
| 	"encoding/binary" | ||||
| 	"errors" | ||||
| ) | ||||
| 
 | ||||
| var defaultIV = []byte{0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6} | ||||
| 
 | ||||
| // KeyWrap implements NIST key wrapping; it wraps a content encryption key (cek) with the given block cipher.
 | ||||
| func KeyWrap(block cipher.Block, cek []byte) ([]byte, error) { | ||||
| 	if len(cek)%8 != 0 { | ||||
| 		return nil, errors.New("square/go-jose: key wrap input must be 8 byte blocks") | ||||
| 	} | ||||
| 
 | ||||
| 	n := len(cek) / 8 | ||||
| 	r := make([][]byte, n) | ||||
| 
 | ||||
| 	for i := range r { | ||||
| 		r[i] = make([]byte, 8) | ||||
| 		copy(r[i], cek[i*8:]) | ||||
| 	} | ||||
| 
 | ||||
| 	buffer := make([]byte, 16) | ||||
| 	tBytes := make([]byte, 8) | ||||
| 	copy(buffer, defaultIV) | ||||
| 
 | ||||
| 	for t := 0; t < 6*n; t++ { | ||||
| 		copy(buffer[8:], r[t%n]) | ||||
| 
 | ||||
| 		block.Encrypt(buffer, buffer) | ||||
| 
 | ||||
| 		binary.BigEndian.PutUint64(tBytes, uint64(t+1)) | ||||
| 
 | ||||
| 		for i := 0; i < 8; i++ { | ||||
| 			buffer[i] = buffer[i] ^ tBytes[i] | ||||
| 		} | ||||
| 		copy(r[t%n], buffer[8:]) | ||||
| 	} | ||||
| 
 | ||||
| 	out := make([]byte, (n+1)*8) | ||||
| 	copy(out, buffer[:8]) | ||||
| 	for i := range r { | ||||
| 		copy(out[(i+1)*8:], r[i]) | ||||
| 	} | ||||
| 
 | ||||
| 	return out, nil | ||||
| } | ||||
| 
 | ||||
| // KeyUnwrap implements NIST key unwrapping; it unwraps a content encryption key (cek) with the given block cipher.
 | ||||
| func KeyUnwrap(block cipher.Block, ciphertext []byte) ([]byte, error) { | ||||
| 	if len(ciphertext)%8 != 0 { | ||||
| 		return nil, errors.New("square/go-jose: key wrap input must be 8 byte blocks") | ||||
| 	} | ||||
| 
 | ||||
| 	n := (len(ciphertext) / 8) - 1 | ||||
| 	r := make([][]byte, n) | ||||
| 
 | ||||
| 	for i := range r { | ||||
| 		r[i] = make([]byte, 8) | ||||
| 		copy(r[i], ciphertext[(i+1)*8:]) | ||||
| 	} | ||||
| 
 | ||||
| 	buffer := make([]byte, 16) | ||||
| 	tBytes := make([]byte, 8) | ||||
| 	copy(buffer[:8], ciphertext[:8]) | ||||
| 
 | ||||
| 	for t := 6*n - 1; t >= 0; t-- { | ||||
| 		binary.BigEndian.PutUint64(tBytes, uint64(t+1)) | ||||
| 
 | ||||
| 		for i := 0; i < 8; i++ { | ||||
| 			buffer[i] = buffer[i] ^ tBytes[i] | ||||
| 		} | ||||
| 		copy(buffer[8:], r[t%n]) | ||||
| 
 | ||||
| 		block.Decrypt(buffer, buffer) | ||||
| 
 | ||||
| 		copy(r[t%n], buffer[8:]) | ||||
| 	} | ||||
| 
 | ||||
| 	if subtle.ConstantTimeCompare(buffer[:8], defaultIV) == 0 { | ||||
| 		return nil, errors.New("square/go-jose: failed to unwrap key") | ||||
| 	} | ||||
| 
 | ||||
| 	out := make([]byte, n*8) | ||||
| 	for i := range r { | ||||
| 		copy(out[i*8:], r[i]) | ||||
| 	} | ||||
| 
 | ||||
| 	return out, nil | ||||
| } | ||||
|  | @ -1,542 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2014 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package jose | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/rsa" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 
 | ||||
| 	"gopkg.in/square/go-jose.v2/json" | ||||
| ) | ||||
| 
 | ||||
| // Encrypter represents an encrypter which produces an encrypted JWE object.
 | ||||
| type Encrypter interface { | ||||
| 	Encrypt(plaintext []byte) (*JSONWebEncryption, error) | ||||
| 	EncryptWithAuthData(plaintext []byte, aad []byte) (*JSONWebEncryption, error) | ||||
| 	Options() EncrypterOptions | ||||
| } | ||||
| 
 | ||||
| // A generic content cipher
 | ||||
| type contentCipher interface { | ||||
| 	keySize() int | ||||
| 	encrypt(cek []byte, aad, plaintext []byte) (*aeadParts, error) | ||||
| 	decrypt(cek []byte, aad []byte, parts *aeadParts) ([]byte, error) | ||||
| } | ||||
| 
 | ||||
| // A key generator (for generating/getting a CEK)
 | ||||
| type keyGenerator interface { | ||||
| 	keySize() int | ||||
| 	genKey() ([]byte, rawHeader, error) | ||||
| } | ||||
| 
 | ||||
| // A generic key encrypter
 | ||||
| type keyEncrypter interface { | ||||
| 	encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) // Encrypt a key
 | ||||
| } | ||||
| 
 | ||||
| // A generic key decrypter
 | ||||
| type keyDecrypter interface { | ||||
| 	decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) // Decrypt a key
 | ||||
| } | ||||
| 
 | ||||
| // A generic encrypter based on the given key encrypter and content cipher.
 | ||||
| type genericEncrypter struct { | ||||
| 	contentAlg     ContentEncryption | ||||
| 	compressionAlg CompressionAlgorithm | ||||
| 	cipher         contentCipher | ||||
| 	recipients     []recipientKeyInfo | ||||
| 	keyGenerator   keyGenerator | ||||
| 	extraHeaders   map[HeaderKey]interface{} | ||||
| } | ||||
| 
 | ||||
| type recipientKeyInfo struct { | ||||
| 	keyID        string | ||||
| 	keyAlg       KeyAlgorithm | ||||
| 	keyEncrypter keyEncrypter | ||||
| } | ||||
| 
 | ||||
| // EncrypterOptions represents options that can be set on new encrypters.
 | ||||
| type EncrypterOptions struct { | ||||
| 	Compression CompressionAlgorithm | ||||
| 
 | ||||
| 	// Optional map of additional keys to be inserted into the protected header
 | ||||
| 	// of a JWS object. Some specifications which make use of JWS like to insert
 | ||||
| 	// additional values here. All values must be JSON-serializable.
 | ||||
| 	ExtraHeaders map[HeaderKey]interface{} | ||||
| } | ||||
| 
 | ||||
| // WithHeader adds an arbitrary value to the ExtraHeaders map, initializing it
 | ||||
| // if necessary. It returns itself and so can be used in a fluent style.
 | ||||
| func (eo *EncrypterOptions) WithHeader(k HeaderKey, v interface{}) *EncrypterOptions { | ||||
| 	if eo.ExtraHeaders == nil { | ||||
| 		eo.ExtraHeaders = map[HeaderKey]interface{}{} | ||||
| 	} | ||||
| 	eo.ExtraHeaders[k] = v | ||||
| 	return eo | ||||
| } | ||||
| 
 | ||||
| // WithContentType adds a content type ("cty") header and returns the updated
 | ||||
| // EncrypterOptions.
 | ||||
| func (eo *EncrypterOptions) WithContentType(contentType ContentType) *EncrypterOptions { | ||||
| 	return eo.WithHeader(HeaderContentType, contentType) | ||||
| } | ||||
| 
 | ||||
| // WithType adds a type ("typ") header and returns the updated EncrypterOptions.
 | ||||
| func (eo *EncrypterOptions) WithType(typ ContentType) *EncrypterOptions { | ||||
| 	return eo.WithHeader(HeaderType, typ) | ||||
| } | ||||
| 
 | ||||
| // Recipient represents an algorithm/key to encrypt messages to.
 | ||||
| //
 | ||||
| // PBES2Count and PBES2Salt correspond with the  "p2c" and "p2s" headers used
 | ||||
| // on the password-based encryption algorithms PBES2-HS256+A128KW,
 | ||||
| // PBES2-HS384+A192KW, and PBES2-HS512+A256KW. If they are not provided a safe
 | ||||
| // default of 100000 will be used for the count and a 128-bit random salt will
 | ||||
| // be generated.
 | ||||
| type Recipient struct { | ||||
| 	Algorithm  KeyAlgorithm | ||||
| 	Key        interface{} | ||||
| 	KeyID      string | ||||
| 	PBES2Count int | ||||
| 	PBES2Salt  []byte | ||||
| } | ||||
| 
 | ||||
| // NewEncrypter creates an appropriate encrypter based on the key type
 | ||||
| func NewEncrypter(enc ContentEncryption, rcpt Recipient, opts *EncrypterOptions) (Encrypter, error) { | ||||
| 	encrypter := &genericEncrypter{ | ||||
| 		contentAlg: enc, | ||||
| 		recipients: []recipientKeyInfo{}, | ||||
| 		cipher:     getContentCipher(enc), | ||||
| 	} | ||||
| 	if opts != nil { | ||||
| 		encrypter.compressionAlg = opts.Compression | ||||
| 		encrypter.extraHeaders = opts.ExtraHeaders | ||||
| 	} | ||||
| 
 | ||||
| 	if encrypter.cipher == nil { | ||||
| 		return nil, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	var keyID string | ||||
| 	var rawKey interface{} | ||||
| 	switch encryptionKey := rcpt.Key.(type) { | ||||
| 	case JSONWebKey: | ||||
| 		keyID, rawKey = encryptionKey.KeyID, encryptionKey.Key | ||||
| 	case *JSONWebKey: | ||||
| 		keyID, rawKey = encryptionKey.KeyID, encryptionKey.Key | ||||
| 	case OpaqueKeyEncrypter: | ||||
| 		keyID, rawKey = encryptionKey.KeyID(), encryptionKey | ||||
| 	default: | ||||
| 		rawKey = encryptionKey | ||||
| 	} | ||||
| 
 | ||||
| 	switch rcpt.Algorithm { | ||||
| 	case DIRECT: | ||||
| 		// Direct encryption mode must be treated differently
 | ||||
| 		if reflect.TypeOf(rawKey) != reflect.TypeOf([]byte{}) { | ||||
| 			return nil, ErrUnsupportedKeyType | ||||
| 		} | ||||
| 		if encrypter.cipher.keySize() != len(rawKey.([]byte)) { | ||||
| 			return nil, ErrInvalidKeySize | ||||
| 		} | ||||
| 		encrypter.keyGenerator = staticKeyGenerator{ | ||||
| 			key: rawKey.([]byte), | ||||
| 		} | ||||
| 		recipientInfo, _ := newSymmetricRecipient(rcpt.Algorithm, rawKey.([]byte)) | ||||
| 		recipientInfo.keyID = keyID | ||||
| 		if rcpt.KeyID != "" { | ||||
| 			recipientInfo.keyID = rcpt.KeyID | ||||
| 		} | ||||
| 		encrypter.recipients = []recipientKeyInfo{recipientInfo} | ||||
| 		return encrypter, nil | ||||
| 	case ECDH_ES: | ||||
| 		// ECDH-ES (w/o key wrapping) is similar to DIRECT mode
 | ||||
| 		typeOf := reflect.TypeOf(rawKey) | ||||
| 		if typeOf != reflect.TypeOf(&ecdsa.PublicKey{}) { | ||||
| 			return nil, ErrUnsupportedKeyType | ||||
| 		} | ||||
| 		encrypter.keyGenerator = ecKeyGenerator{ | ||||
| 			size:      encrypter.cipher.keySize(), | ||||
| 			algID:     string(enc), | ||||
| 			publicKey: rawKey.(*ecdsa.PublicKey), | ||||
| 		} | ||||
| 		recipientInfo, _ := newECDHRecipient(rcpt.Algorithm, rawKey.(*ecdsa.PublicKey)) | ||||
| 		recipientInfo.keyID = keyID | ||||
| 		if rcpt.KeyID != "" { | ||||
| 			recipientInfo.keyID = rcpt.KeyID | ||||
| 		} | ||||
| 		encrypter.recipients = []recipientKeyInfo{recipientInfo} | ||||
| 		return encrypter, nil | ||||
| 	default: | ||||
| 		// Can just add a standard recipient
 | ||||
| 		encrypter.keyGenerator = randomKeyGenerator{ | ||||
| 			size: encrypter.cipher.keySize(), | ||||
| 		} | ||||
| 		err := encrypter.addRecipient(rcpt) | ||||
| 		return encrypter, err | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // NewMultiEncrypter creates a multi-encrypter based on the given parameters
 | ||||
| func NewMultiEncrypter(enc ContentEncryption, rcpts []Recipient, opts *EncrypterOptions) (Encrypter, error) { | ||||
| 	cipher := getContentCipher(enc) | ||||
| 
 | ||||
| 	if cipher == nil { | ||||
| 		return nil, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 	if rcpts == nil || len(rcpts) == 0 { | ||||
| 		return nil, fmt.Errorf("square/go-jose: recipients is nil or empty") | ||||
| 	} | ||||
| 
 | ||||
| 	encrypter := &genericEncrypter{ | ||||
| 		contentAlg: enc, | ||||
| 		recipients: []recipientKeyInfo{}, | ||||
| 		cipher:     cipher, | ||||
| 		keyGenerator: randomKeyGenerator{ | ||||
| 			size: cipher.keySize(), | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	if opts != nil { | ||||
| 		encrypter.compressionAlg = opts.Compression | ||||
| 		encrypter.extraHeaders = opts.ExtraHeaders | ||||
| 	} | ||||
| 
 | ||||
| 	for _, recipient := range rcpts { | ||||
| 		err := encrypter.addRecipient(recipient) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return encrypter, nil | ||||
| } | ||||
| 
 | ||||
| func (ctx *genericEncrypter) addRecipient(recipient Recipient) (err error) { | ||||
| 	var recipientInfo recipientKeyInfo | ||||
| 
 | ||||
| 	switch recipient.Algorithm { | ||||
| 	case DIRECT, ECDH_ES: | ||||
| 		return fmt.Errorf("square/go-jose: key algorithm '%s' not supported in multi-recipient mode", recipient.Algorithm) | ||||
| 	} | ||||
| 
 | ||||
| 	recipientInfo, err = makeJWERecipient(recipient.Algorithm, recipient.Key) | ||||
| 	if recipient.KeyID != "" { | ||||
| 		recipientInfo.keyID = recipient.KeyID | ||||
| 	} | ||||
| 
 | ||||
| 	switch recipient.Algorithm { | ||||
| 	case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW: | ||||
| 		if sr, ok := recipientInfo.keyEncrypter.(*symmetricKeyCipher); ok { | ||||
| 			sr.p2c = recipient.PBES2Count | ||||
| 			sr.p2s = recipient.PBES2Salt | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if err == nil { | ||||
| 		ctx.recipients = append(ctx.recipients, recipientInfo) | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func makeJWERecipient(alg KeyAlgorithm, encryptionKey interface{}) (recipientKeyInfo, error) { | ||||
| 	switch encryptionKey := encryptionKey.(type) { | ||||
| 	case *rsa.PublicKey: | ||||
| 		return newRSARecipient(alg, encryptionKey) | ||||
| 	case *ecdsa.PublicKey: | ||||
| 		return newECDHRecipient(alg, encryptionKey) | ||||
| 	case []byte: | ||||
| 		return newSymmetricRecipient(alg, encryptionKey) | ||||
| 	case string: | ||||
| 		return newSymmetricRecipient(alg, []byte(encryptionKey)) | ||||
| 	case *JSONWebKey: | ||||
| 		recipient, err := makeJWERecipient(alg, encryptionKey.Key) | ||||
| 		recipient.keyID = encryptionKey.KeyID | ||||
| 		return recipient, err | ||||
| 	} | ||||
| 	if encrypter, ok := encryptionKey.(OpaqueKeyEncrypter); ok { | ||||
| 		return newOpaqueKeyEncrypter(alg, encrypter) | ||||
| 	} | ||||
| 	return recipientKeyInfo{}, ErrUnsupportedKeyType | ||||
| } | ||||
| 
 | ||||
| // newDecrypter creates an appropriate decrypter based on the key type
 | ||||
| func newDecrypter(decryptionKey interface{}) (keyDecrypter, error) { | ||||
| 	switch decryptionKey := decryptionKey.(type) { | ||||
| 	case *rsa.PrivateKey: | ||||
| 		return &rsaDecrypterSigner{ | ||||
| 			privateKey: decryptionKey, | ||||
| 		}, nil | ||||
| 	case *ecdsa.PrivateKey: | ||||
| 		return &ecDecrypterSigner{ | ||||
| 			privateKey: decryptionKey, | ||||
| 		}, nil | ||||
| 	case []byte: | ||||
| 		return &symmetricKeyCipher{ | ||||
| 			key: decryptionKey, | ||||
| 		}, nil | ||||
| 	case string: | ||||
| 		return &symmetricKeyCipher{ | ||||
| 			key: []byte(decryptionKey), | ||||
| 		}, nil | ||||
| 	case JSONWebKey: | ||||
| 		return newDecrypter(decryptionKey.Key) | ||||
| 	case *JSONWebKey: | ||||
| 		return newDecrypter(decryptionKey.Key) | ||||
| 	} | ||||
| 	if okd, ok := decryptionKey.(OpaqueKeyDecrypter); ok { | ||||
| 		return &opaqueKeyDecrypter{decrypter: okd}, nil | ||||
| 	} | ||||
| 	return nil, ErrUnsupportedKeyType | ||||
| } | ||||
| 
 | ||||
| // Implementation of encrypt method producing a JWE object.
 | ||||
| func (ctx *genericEncrypter) Encrypt(plaintext []byte) (*JSONWebEncryption, error) { | ||||
| 	return ctx.EncryptWithAuthData(plaintext, nil) | ||||
| } | ||||
| 
 | ||||
| // Implementation of encrypt method producing a JWE object.
 | ||||
| func (ctx *genericEncrypter) EncryptWithAuthData(plaintext, aad []byte) (*JSONWebEncryption, error) { | ||||
| 	obj := &JSONWebEncryption{} | ||||
| 	obj.aad = aad | ||||
| 
 | ||||
| 	obj.protected = &rawHeader{} | ||||
| 	err := obj.protected.set(headerEncryption, ctx.contentAlg) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	obj.recipients = make([]recipientInfo, len(ctx.recipients)) | ||||
| 
 | ||||
| 	if len(ctx.recipients) == 0 { | ||||
| 		return nil, fmt.Errorf("square/go-jose: no recipients to encrypt to") | ||||
| 	} | ||||
| 
 | ||||
| 	cek, headers, err := ctx.keyGenerator.genKey() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	obj.protected.merge(&headers) | ||||
| 
 | ||||
| 	for i, info := range ctx.recipients { | ||||
| 		recipient, err := info.keyEncrypter.encryptKey(cek, info.keyAlg) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		err = recipient.header.set(headerAlgorithm, info.keyAlg) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		if info.keyID != "" { | ||||
| 			err = recipient.header.set(headerKeyID, info.keyID) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 		obj.recipients[i] = recipient | ||||
| 	} | ||||
| 
 | ||||
| 	if len(ctx.recipients) == 1 { | ||||
| 		// Move per-recipient headers into main protected header if there's
 | ||||
| 		// only a single recipient.
 | ||||
| 		obj.protected.merge(obj.recipients[0].header) | ||||
| 		obj.recipients[0].header = nil | ||||
| 	} | ||||
| 
 | ||||
| 	if ctx.compressionAlg != NONE { | ||||
| 		plaintext, err = compress(ctx.compressionAlg, plaintext) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		err = obj.protected.set(headerCompression, ctx.compressionAlg) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for k, v := range ctx.extraHeaders { | ||||
| 		b, err := json.Marshal(v) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		(*obj.protected)[k] = makeRawMessage(b) | ||||
| 	} | ||||
| 
 | ||||
| 	authData := obj.computeAuthData() | ||||
| 	parts, err := ctx.cipher.encrypt(cek, authData, plaintext) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	obj.iv = parts.iv | ||||
| 	obj.ciphertext = parts.ciphertext | ||||
| 	obj.tag = parts.tag | ||||
| 
 | ||||
| 	return obj, nil | ||||
| } | ||||
| 
 | ||||
| func (ctx *genericEncrypter) Options() EncrypterOptions { | ||||
| 	return EncrypterOptions{ | ||||
| 		Compression:  ctx.compressionAlg, | ||||
| 		ExtraHeaders: ctx.extraHeaders, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Decrypt and validate the object and return the plaintext. Note that this
 | ||||
| // function does not support multi-recipient, if you desire multi-recipient
 | ||||
| // decryption use DecryptMulti instead.
 | ||||
| func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) { | ||||
| 	headers := obj.mergedHeaders(nil) | ||||
| 
 | ||||
| 	if len(obj.recipients) > 1 { | ||||
| 		return nil, errors.New("square/go-jose: too many recipients in payload; expecting only one") | ||||
| 	} | ||||
| 
 | ||||
| 	critical, err := headers.getCritical() | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("square/go-jose: invalid crit header") | ||||
| 	} | ||||
| 
 | ||||
| 	if len(critical) > 0 { | ||||
| 		return nil, fmt.Errorf("square/go-jose: unsupported crit header") | ||||
| 	} | ||||
| 
 | ||||
| 	decrypter, err := newDecrypter(decryptionKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	cipher := getContentCipher(headers.getEncryption()) | ||||
| 	if cipher == nil { | ||||
| 		return nil, fmt.Errorf("square/go-jose: unsupported enc value '%s'", string(headers.getEncryption())) | ||||
| 	} | ||||
| 
 | ||||
| 	generator := randomKeyGenerator{ | ||||
| 		size: cipher.keySize(), | ||||
| 	} | ||||
| 
 | ||||
| 	parts := &aeadParts{ | ||||
| 		iv:         obj.iv, | ||||
| 		ciphertext: obj.ciphertext, | ||||
| 		tag:        obj.tag, | ||||
| 	} | ||||
| 
 | ||||
| 	authData := obj.computeAuthData() | ||||
| 
 | ||||
| 	var plaintext []byte | ||||
| 	recipient := obj.recipients[0] | ||||
| 	recipientHeaders := obj.mergedHeaders(&recipient) | ||||
| 
 | ||||
| 	cek, err := decrypter.decryptKey(recipientHeaders, &recipient, generator) | ||||
| 	if err == nil { | ||||
| 		// Found a valid CEK -- let's try to decrypt.
 | ||||
| 		plaintext, err = cipher.decrypt(cek, authData, parts) | ||||
| 	} | ||||
| 
 | ||||
| 	if plaintext == nil { | ||||
| 		return nil, ErrCryptoFailure | ||||
| 	} | ||||
| 
 | ||||
| 	// The "zip" header parameter may only be present in the protected header.
 | ||||
| 	if comp := obj.protected.getCompression(); comp != "" { | ||||
| 		plaintext, err = decompress(comp, plaintext) | ||||
| 	} | ||||
| 
 | ||||
| 	return plaintext, err | ||||
| } | ||||
| 
 | ||||
| // DecryptMulti decrypts and validates the object and returns the plaintexts,
 | ||||
| // with support for multiple recipients. It returns the index of the recipient
 | ||||
| // for which the decryption was successful, the merged headers for that recipient,
 | ||||
| // and the plaintext.
 | ||||
| func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Header, []byte, error) { | ||||
| 	globalHeaders := obj.mergedHeaders(nil) | ||||
| 
 | ||||
| 	critical, err := globalHeaders.getCritical() | ||||
| 	if err != nil { | ||||
| 		return -1, Header{}, nil, fmt.Errorf("square/go-jose: invalid crit header") | ||||
| 	} | ||||
| 
 | ||||
| 	if len(critical) > 0 { | ||||
| 		return -1, Header{}, nil, fmt.Errorf("square/go-jose: unsupported crit header") | ||||
| 	} | ||||
| 
 | ||||
| 	decrypter, err := newDecrypter(decryptionKey) | ||||
| 	if err != nil { | ||||
| 		return -1, Header{}, nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	encryption := globalHeaders.getEncryption() | ||||
| 	cipher := getContentCipher(encryption) | ||||
| 	if cipher == nil { | ||||
| 		return -1, Header{}, nil, fmt.Errorf("square/go-jose: unsupported enc value '%s'", string(encryption)) | ||||
| 	} | ||||
| 
 | ||||
| 	generator := randomKeyGenerator{ | ||||
| 		size: cipher.keySize(), | ||||
| 	} | ||||
| 
 | ||||
| 	parts := &aeadParts{ | ||||
| 		iv:         obj.iv, | ||||
| 		ciphertext: obj.ciphertext, | ||||
| 		tag:        obj.tag, | ||||
| 	} | ||||
| 
 | ||||
| 	authData := obj.computeAuthData() | ||||
| 
 | ||||
| 	index := -1 | ||||
| 	var plaintext []byte | ||||
| 	var headers rawHeader | ||||
| 
 | ||||
| 	for i, recipient := range obj.recipients { | ||||
| 		recipientHeaders := obj.mergedHeaders(&recipient) | ||||
| 
 | ||||
| 		cek, err := decrypter.decryptKey(recipientHeaders, &recipient, generator) | ||||
| 		if err == nil { | ||||
| 			// Found a valid CEK -- let's try to decrypt.
 | ||||
| 			plaintext, err = cipher.decrypt(cek, authData, parts) | ||||
| 			if err == nil { | ||||
| 				index = i | ||||
| 				headers = recipientHeaders | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if plaintext == nil || err != nil { | ||||
| 		return -1, Header{}, nil, ErrCryptoFailure | ||||
| 	} | ||||
| 
 | ||||
| 	// The "zip" header parameter may only be present in the protected header.
 | ||||
| 	if comp := obj.protected.getCompression(); comp != "" { | ||||
| 		plaintext, err = decompress(comp, plaintext) | ||||
| 	} | ||||
| 
 | ||||
| 	sanitized, err := headers.sanitized() | ||||
| 	if err != nil { | ||||
| 		return -1, Header{}, nil, fmt.Errorf("square/go-jose: failed to sanitize header: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return index, sanitized, plaintext, err | ||||
| } | ||||
|  | @ -1,27 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2014 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| /* | ||||
| 
 | ||||
| Package jose aims to provide an implementation of the Javascript Object Signing | ||||
| and Encryption set of standards. It implements encryption and signing based on | ||||
| the JSON Web Encryption and JSON Web Signature standards, with optional JSON | ||||
| Web Token support available in a sub-package. The library supports both the | ||||
| compact and full serialization formats, and has optional support for multiple | ||||
| recipients. | ||||
| 
 | ||||
| */ | ||||
| package jose | ||||
|  | @ -1,185 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2014 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package jose | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"compress/flate" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/binary" | ||||
| 	"io" | ||||
| 	"math/big" | ||||
| 	"strings" | ||||
| 	"unicode" | ||||
| 
 | ||||
| 	"gopkg.in/square/go-jose.v2/json" | ||||
| ) | ||||
| 
 | ||||
| // Helper function to serialize known-good objects.
 | ||||
| // Precondition: value is not a nil pointer.
 | ||||
| func mustSerializeJSON(value interface{}) []byte { | ||||
| 	out, err := json.Marshal(value) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	// We never want to serialize the top-level value "null," since it's not a
 | ||||
| 	// valid JOSE message. But if a caller passes in a nil pointer to this method,
 | ||||
| 	// MarshalJSON will happily serialize it as the top-level value "null". If
 | ||||
| 	// that value is then embedded in another operation, for instance by being
 | ||||
| 	// base64-encoded and fed as input to a signing algorithm
 | ||||
| 	// (https://github.com/square/go-jose/issues/22), the result will be
 | ||||
| 	// incorrect. Because this method is intended for known-good objects, and a nil
 | ||||
| 	// pointer is not a known-good object, we are free to panic in this case.
 | ||||
| 	// Note: It's not possible to directly check whether the data pointed at by an
 | ||||
| 	// interface is a nil pointer, so we do this hacky workaround.
 | ||||
| 	// https://groups.google.com/forum/#!topic/golang-nuts/wnH302gBa4I
 | ||||
| 	if string(out) == "null" { | ||||
| 		panic("Tried to serialize a nil pointer.") | ||||
| 	} | ||||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // Strip all newlines and whitespace
 | ||||
| func stripWhitespace(data string) string { | ||||
| 	buf := strings.Builder{} | ||||
| 	buf.Grow(len(data)) | ||||
| 	for _, r := range data { | ||||
| 		if !unicode.IsSpace(r) { | ||||
| 			buf.WriteRune(r) | ||||
| 		} | ||||
| 	} | ||||
| 	return buf.String() | ||||
| } | ||||
| 
 | ||||
| // Perform compression based on algorithm
 | ||||
| func compress(algorithm CompressionAlgorithm, input []byte) ([]byte, error) { | ||||
| 	switch algorithm { | ||||
| 	case DEFLATE: | ||||
| 		return deflate(input) | ||||
| 	default: | ||||
| 		return nil, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Perform decompression based on algorithm
 | ||||
| func decompress(algorithm CompressionAlgorithm, input []byte) ([]byte, error) { | ||||
| 	switch algorithm { | ||||
| 	case DEFLATE: | ||||
| 		return inflate(input) | ||||
| 	default: | ||||
| 		return nil, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Compress with DEFLATE
 | ||||
| func deflate(input []byte) ([]byte, error) { | ||||
| 	output := new(bytes.Buffer) | ||||
| 
 | ||||
| 	// Writing to byte buffer, err is always nil
 | ||||
| 	writer, _ := flate.NewWriter(output, 1) | ||||
| 	_, _ = io.Copy(writer, bytes.NewBuffer(input)) | ||||
| 
 | ||||
| 	err := writer.Close() | ||||
| 	return output.Bytes(), err | ||||
| } | ||||
| 
 | ||||
| // Decompress with DEFLATE
 | ||||
| func inflate(input []byte) ([]byte, error) { | ||||
| 	output := new(bytes.Buffer) | ||||
| 	reader := flate.NewReader(bytes.NewBuffer(input)) | ||||
| 
 | ||||
| 	_, err := io.Copy(output, reader) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	err = reader.Close() | ||||
| 	return output.Bytes(), err | ||||
| } | ||||
| 
 | ||||
| // byteBuffer represents a slice of bytes that can be serialized to url-safe base64.
 | ||||
| type byteBuffer struct { | ||||
| 	data []byte | ||||
| } | ||||
| 
 | ||||
| func newBuffer(data []byte) *byteBuffer { | ||||
| 	if data == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return &byteBuffer{ | ||||
| 		data: data, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func newFixedSizeBuffer(data []byte, length int) *byteBuffer { | ||||
| 	if len(data) > length { | ||||
| 		panic("square/go-jose: invalid call to newFixedSizeBuffer (len(data) > length)") | ||||
| 	} | ||||
| 	pad := make([]byte, length-len(data)) | ||||
| 	return newBuffer(append(pad, data...)) | ||||
| } | ||||
| 
 | ||||
| func newBufferFromInt(num uint64) *byteBuffer { | ||||
| 	data := make([]byte, 8) | ||||
| 	binary.BigEndian.PutUint64(data, num) | ||||
| 	return newBuffer(bytes.TrimLeft(data, "\x00")) | ||||
| } | ||||
| 
 | ||||
| func (b *byteBuffer) MarshalJSON() ([]byte, error) { | ||||
| 	return json.Marshal(b.base64()) | ||||
| } | ||||
| 
 | ||||
| func (b *byteBuffer) UnmarshalJSON(data []byte) error { | ||||
| 	var encoded string | ||||
| 	err := json.Unmarshal(data, &encoded) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if encoded == "" { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	decoded, err := base64.RawURLEncoding.DecodeString(encoded) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	*b = *newBuffer(decoded) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (b *byteBuffer) base64() string { | ||||
| 	return base64.RawURLEncoding.EncodeToString(b.data) | ||||
| } | ||||
| 
 | ||||
| func (b *byteBuffer) bytes() []byte { | ||||
| 	// Handling nil here allows us to transparently handle nil slices when serializing.
 | ||||
| 	if b == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return b.data | ||||
| } | ||||
| 
 | ||||
| func (b byteBuffer) bigInt() *big.Int { | ||||
| 	return new(big.Int).SetBytes(b.data) | ||||
| } | ||||
| 
 | ||||
| func (b byteBuffer) toInt() int { | ||||
| 	return int(b.bigInt().Int64()) | ||||
| } | ||||
|  | @ -1,27 +0,0 @@ | |||
| Copyright (c) 2012 The Go Authors. All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are | ||||
| met: | ||||
| 
 | ||||
|    * Redistributions of source code must retain the above copyright | ||||
| notice, this list of conditions and the following disclaimer. | ||||
|    * Redistributions in binary form must reproduce the above | ||||
| copyright notice, this list of conditions and the following disclaimer | ||||
| in the documentation and/or other materials provided with the | ||||
| distribution. | ||||
|    * Neither the name of Google Inc. nor the names of its | ||||
| contributors may be used to endorse or promote products derived from | ||||
| this software without specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | @ -1,13 +0,0 @@ | |||
| # Safe JSON | ||||
| 
 | ||||
| This repository contains a fork of the `encoding/json` package from Go 1.6. | ||||
| 
 | ||||
| The following changes were made: | ||||
| 
 | ||||
| * Object deserialization uses case-sensitive member name matching instead of | ||||
|   [case-insensitive matching](https://www.ietf.org/mail-archive/web/json/current/msg03763.html). | ||||
|   This is to avoid differences in the interpretation of JOSE messages between | ||||
|   go-jose and libraries written in other languages. | ||||
| * When deserializing a JSON object, we check for duplicate keys and reject the | ||||
|   input whenever we detect a duplicate. Rather than trying to work with malformed | ||||
|   data, we prefer to reject it right away. | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -1,141 +0,0 @@ | |||
| // Copyright 2010 The Go Authors.  All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package json | ||||
| 
 | ||||
| import "bytes" | ||||
| 
 | ||||
| // Compact appends to dst the JSON-encoded src with
 | ||||
| // insignificant space characters elided.
 | ||||
| func Compact(dst *bytes.Buffer, src []byte) error { | ||||
| 	return compact(dst, src, false) | ||||
| } | ||||
| 
 | ||||
| func compact(dst *bytes.Buffer, src []byte, escape bool) error { | ||||
| 	origLen := dst.Len() | ||||
| 	var scan scanner | ||||
| 	scan.reset() | ||||
| 	start := 0 | ||||
| 	for i, c := range src { | ||||
| 		if escape && (c == '<' || c == '>' || c == '&') { | ||||
| 			if start < i { | ||||
| 				dst.Write(src[start:i]) | ||||
| 			} | ||||
| 			dst.WriteString(`\u00`) | ||||
| 			dst.WriteByte(hex[c>>4]) | ||||
| 			dst.WriteByte(hex[c&0xF]) | ||||
| 			start = i + 1 | ||||
| 		} | ||||
| 		// Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
 | ||||
| 		if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 { | ||||
| 			if start < i { | ||||
| 				dst.Write(src[start:i]) | ||||
| 			} | ||||
| 			dst.WriteString(`\u202`) | ||||
| 			dst.WriteByte(hex[src[i+2]&0xF]) | ||||
| 			start = i + 3 | ||||
| 		} | ||||
| 		v := scan.step(&scan, c) | ||||
| 		if v >= scanSkipSpace { | ||||
| 			if v == scanError { | ||||
| 				break | ||||
| 			} | ||||
| 			if start < i { | ||||
| 				dst.Write(src[start:i]) | ||||
| 			} | ||||
| 			start = i + 1 | ||||
| 		} | ||||
| 	} | ||||
| 	if scan.eof() == scanError { | ||||
| 		dst.Truncate(origLen) | ||||
| 		return scan.err | ||||
| 	} | ||||
| 	if start < len(src) { | ||||
| 		dst.Write(src[start:]) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func newline(dst *bytes.Buffer, prefix, indent string, depth int) { | ||||
| 	dst.WriteByte('\n') | ||||
| 	dst.WriteString(prefix) | ||||
| 	for i := 0; i < depth; i++ { | ||||
| 		dst.WriteString(indent) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Indent appends to dst an indented form of the JSON-encoded src.
 | ||||
| // Each element in a JSON object or array begins on a new,
 | ||||
| // indented line beginning with prefix followed by one or more
 | ||||
| // copies of indent according to the indentation nesting.
 | ||||
| // The data appended to dst does not begin with the prefix nor
 | ||||
| // any indentation, to make it easier to embed inside other formatted JSON data.
 | ||||
| // Although leading space characters (space, tab, carriage return, newline)
 | ||||
| // at the beginning of src are dropped, trailing space characters
 | ||||
| // at the end of src are preserved and copied to dst.
 | ||||
| // For example, if src has no trailing spaces, neither will dst;
 | ||||
| // if src ends in a trailing newline, so will dst.
 | ||||
| func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error { | ||||
| 	origLen := dst.Len() | ||||
| 	var scan scanner | ||||
| 	scan.reset() | ||||
| 	needIndent := false | ||||
| 	depth := 0 | ||||
| 	for _, c := range src { | ||||
| 		scan.bytes++ | ||||
| 		v := scan.step(&scan, c) | ||||
| 		if v == scanSkipSpace { | ||||
| 			continue | ||||
| 		} | ||||
| 		if v == scanError { | ||||
| 			break | ||||
| 		} | ||||
| 		if needIndent && v != scanEndObject && v != scanEndArray { | ||||
| 			needIndent = false | ||||
| 			depth++ | ||||
| 			newline(dst, prefix, indent, depth) | ||||
| 		} | ||||
| 
 | ||||
| 		// Emit semantically uninteresting bytes
 | ||||
| 		// (in particular, punctuation in strings) unmodified.
 | ||||
| 		if v == scanContinue { | ||||
| 			dst.WriteByte(c) | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// Add spacing around real punctuation.
 | ||||
| 		switch c { | ||||
| 		case '{', '[': | ||||
| 			// delay indent so that empty object and array are formatted as {} and [].
 | ||||
| 			needIndent = true | ||||
| 			dst.WriteByte(c) | ||||
| 
 | ||||
| 		case ',': | ||||
| 			dst.WriteByte(c) | ||||
| 			newline(dst, prefix, indent, depth) | ||||
| 
 | ||||
| 		case ':': | ||||
| 			dst.WriteByte(c) | ||||
| 			dst.WriteByte(' ') | ||||
| 
 | ||||
| 		case '}', ']': | ||||
| 			if needIndent { | ||||
| 				// suppress indent in empty object/array
 | ||||
| 				needIndent = false | ||||
| 			} else { | ||||
| 				depth-- | ||||
| 				newline(dst, prefix, indent, depth) | ||||
| 			} | ||||
| 			dst.WriteByte(c) | ||||
| 
 | ||||
| 		default: | ||||
| 			dst.WriteByte(c) | ||||
| 		} | ||||
| 	} | ||||
| 	if scan.eof() == scanError { | ||||
| 		dst.Truncate(origLen) | ||||
| 		return scan.err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | @ -1,623 +0,0 @@ | |||
| // Copyright 2010 The Go Authors.  All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package json | ||||
| 
 | ||||
| // JSON value parser state machine.
 | ||||
| // Just about at the limit of what is reasonable to write by hand.
 | ||||
| // Some parts are a bit tedious, but overall it nicely factors out the
 | ||||
| // otherwise common code from the multiple scanning functions
 | ||||
| // in this package (Compact, Indent, checkValid, nextValue, etc).
 | ||||
| //
 | ||||
| // This file starts with two simple examples using the scanner
 | ||||
| // before diving into the scanner itself.
 | ||||
| 
 | ||||
| import "strconv" | ||||
| 
 | ||||
| // checkValid verifies that data is valid JSON-encoded data.
 | ||||
| // scan is passed in for use by checkValid to avoid an allocation.
 | ||||
| func checkValid(data []byte, scan *scanner) error { | ||||
| 	scan.reset() | ||||
| 	for _, c := range data { | ||||
| 		scan.bytes++ | ||||
| 		if scan.step(scan, c) == scanError { | ||||
| 			return scan.err | ||||
| 		} | ||||
| 	} | ||||
| 	if scan.eof() == scanError { | ||||
| 		return scan.err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // nextValue splits data after the next whole JSON value,
 | ||||
| // returning that value and the bytes that follow it as separate slices.
 | ||||
| // scan is passed in for use by nextValue to avoid an allocation.
 | ||||
| func nextValue(data []byte, scan *scanner) (value, rest []byte, err error) { | ||||
| 	scan.reset() | ||||
| 	for i, c := range data { | ||||
| 		v := scan.step(scan, c) | ||||
| 		if v >= scanEndObject { | ||||
| 			switch v { | ||||
| 			// probe the scanner with a space to determine whether we will
 | ||||
| 			// get scanEnd on the next character. Otherwise, if the next character
 | ||||
| 			// is not a space, scanEndTop allocates a needless error.
 | ||||
| 			case scanEndObject, scanEndArray: | ||||
| 				if scan.step(scan, ' ') == scanEnd { | ||||
| 					return data[:i+1], data[i+1:], nil | ||||
| 				} | ||||
| 			case scanError: | ||||
| 				return nil, nil, scan.err | ||||
| 			case scanEnd: | ||||
| 				return data[:i], data[i:], nil | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if scan.eof() == scanError { | ||||
| 		return nil, nil, scan.err | ||||
| 	} | ||||
| 	return data, nil, nil | ||||
| } | ||||
| 
 | ||||
| // A SyntaxError is a description of a JSON syntax error.
 | ||||
| type SyntaxError struct { | ||||
| 	msg    string // description of error
 | ||||
| 	Offset int64  // error occurred after reading Offset bytes
 | ||||
| } | ||||
| 
 | ||||
| func (e *SyntaxError) Error() string { return e.msg } | ||||
| 
 | ||||
| // A scanner is a JSON scanning state machine.
 | ||||
| // Callers call scan.reset() and then pass bytes in one at a time
 | ||||
| // by calling scan.step(&scan, c) for each byte.
 | ||||
| // The return value, referred to as an opcode, tells the
 | ||||
| // caller about significant parsing events like beginning
 | ||||
| // and ending literals, objects, and arrays, so that the
 | ||||
| // caller can follow along if it wishes.
 | ||||
| // The return value scanEnd indicates that a single top-level
 | ||||
| // JSON value has been completed, *before* the byte that
 | ||||
| // just got passed in.  (The indication must be delayed in order
 | ||||
| // to recognize the end of numbers: is 123 a whole value or
 | ||||
| // the beginning of 12345e+6?).
 | ||||
| type scanner struct { | ||||
| 	// The step is a func to be called to execute the next transition.
 | ||||
| 	// Also tried using an integer constant and a single func
 | ||||
| 	// with a switch, but using the func directly was 10% faster
 | ||||
| 	// on a 64-bit Mac Mini, and it's nicer to read.
 | ||||
| 	step func(*scanner, byte) int | ||||
| 
 | ||||
| 	// Reached end of top-level value.
 | ||||
| 	endTop bool | ||||
| 
 | ||||
| 	// Stack of what we're in the middle of - array values, object keys, object values.
 | ||||
| 	parseState []int | ||||
| 
 | ||||
| 	// Error that happened, if any.
 | ||||
| 	err error | ||||
| 
 | ||||
| 	// 1-byte redo (see undo method)
 | ||||
| 	redo      bool | ||||
| 	redoCode  int | ||||
| 	redoState func(*scanner, byte) int | ||||
| 
 | ||||
| 	// total bytes consumed, updated by decoder.Decode
 | ||||
| 	bytes int64 | ||||
| } | ||||
| 
 | ||||
| // These values are returned by the state transition functions
 | ||||
| // assigned to scanner.state and the method scanner.eof.
 | ||||
| // They give details about the current state of the scan that
 | ||||
| // callers might be interested to know about.
 | ||||
| // It is okay to ignore the return value of any particular
 | ||||
| // call to scanner.state: if one call returns scanError,
 | ||||
| // every subsequent call will return scanError too.
 | ||||
| const ( | ||||
| 	// Continue.
 | ||||
| 	scanContinue     = iota // uninteresting byte
 | ||||
| 	scanBeginLiteral        // end implied by next result != scanContinue
 | ||||
| 	scanBeginObject         // begin object
 | ||||
| 	scanObjectKey           // just finished object key (string)
 | ||||
| 	scanObjectValue         // just finished non-last object value
 | ||||
| 	scanEndObject           // end object (implies scanObjectValue if possible)
 | ||||
| 	scanBeginArray          // begin array
 | ||||
| 	scanArrayValue          // just finished array value
 | ||||
| 	scanEndArray            // end array (implies scanArrayValue if possible)
 | ||||
| 	scanSkipSpace           // space byte; can skip; known to be last "continue" result
 | ||||
| 
 | ||||
| 	// Stop.
 | ||||
| 	scanEnd   // top-level value ended *before* this byte; known to be first "stop" result
 | ||||
| 	scanError // hit an error, scanner.err.
 | ||||
| ) | ||||
| 
 | ||||
| // These values are stored in the parseState stack.
 | ||||
| // They give the current state of a composite value
 | ||||
| // being scanned.  If the parser is inside a nested value
 | ||||
| // the parseState describes the nested state, outermost at entry 0.
 | ||||
| const ( | ||||
| 	parseObjectKey   = iota // parsing object key (before colon)
 | ||||
| 	parseObjectValue        // parsing object value (after colon)
 | ||||
| 	parseArrayValue         // parsing array value
 | ||||
| ) | ||||
| 
 | ||||
| // reset prepares the scanner for use.
 | ||||
| // It must be called before calling s.step.
 | ||||
| func (s *scanner) reset() { | ||||
| 	s.step = stateBeginValue | ||||
| 	s.parseState = s.parseState[0:0] | ||||
| 	s.err = nil | ||||
| 	s.redo = false | ||||
| 	s.endTop = false | ||||
| } | ||||
| 
 | ||||
| // eof tells the scanner that the end of input has been reached.
 | ||||
| // It returns a scan status just as s.step does.
 | ||||
| func (s *scanner) eof() int { | ||||
| 	if s.err != nil { | ||||
| 		return scanError | ||||
| 	} | ||||
| 	if s.endTop { | ||||
| 		return scanEnd | ||||
| 	} | ||||
| 	s.step(s, ' ') | ||||
| 	if s.endTop { | ||||
| 		return scanEnd | ||||
| 	} | ||||
| 	if s.err == nil { | ||||
| 		s.err = &SyntaxError{"unexpected end of JSON input", s.bytes} | ||||
| 	} | ||||
| 	return scanError | ||||
| } | ||||
| 
 | ||||
| // pushParseState pushes a new parse state p onto the parse stack.
 | ||||
| func (s *scanner) pushParseState(p int) { | ||||
| 	s.parseState = append(s.parseState, p) | ||||
| } | ||||
| 
 | ||||
| // popParseState pops a parse state (already obtained) off the stack
 | ||||
| // and updates s.step accordingly.
 | ||||
| func (s *scanner) popParseState() { | ||||
| 	n := len(s.parseState) - 1 | ||||
| 	s.parseState = s.parseState[0:n] | ||||
| 	s.redo = false | ||||
| 	if n == 0 { | ||||
| 		s.step = stateEndTop | ||||
| 		s.endTop = true | ||||
| 	} else { | ||||
| 		s.step = stateEndValue | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func isSpace(c byte) bool { | ||||
| 	return c == ' ' || c == '\t' || c == '\r' || c == '\n' | ||||
| } | ||||
| 
 | ||||
| // stateBeginValueOrEmpty is the state after reading `[`.
 | ||||
| func stateBeginValueOrEmpty(s *scanner, c byte) int { | ||||
| 	if c <= ' ' && isSpace(c) { | ||||
| 		return scanSkipSpace | ||||
| 	} | ||||
| 	if c == ']' { | ||||
| 		return stateEndValue(s, c) | ||||
| 	} | ||||
| 	return stateBeginValue(s, c) | ||||
| } | ||||
| 
 | ||||
| // stateBeginValue is the state at the beginning of the input.
 | ||||
| func stateBeginValue(s *scanner, c byte) int { | ||||
| 	if c <= ' ' && isSpace(c) { | ||||
| 		return scanSkipSpace | ||||
| 	} | ||||
| 	switch c { | ||||
| 	case '{': | ||||
| 		s.step = stateBeginStringOrEmpty | ||||
| 		s.pushParseState(parseObjectKey) | ||||
| 		return scanBeginObject | ||||
| 	case '[': | ||||
| 		s.step = stateBeginValueOrEmpty | ||||
| 		s.pushParseState(parseArrayValue) | ||||
| 		return scanBeginArray | ||||
| 	case '"': | ||||
| 		s.step = stateInString | ||||
| 		return scanBeginLiteral | ||||
| 	case '-': | ||||
| 		s.step = stateNeg | ||||
| 		return scanBeginLiteral | ||||
| 	case '0': // beginning of 0.123
 | ||||
| 		s.step = state0 | ||||
| 		return scanBeginLiteral | ||||
| 	case 't': // beginning of true
 | ||||
| 		s.step = stateT | ||||
| 		return scanBeginLiteral | ||||
| 	case 'f': // beginning of false
 | ||||
| 		s.step = stateF | ||||
| 		return scanBeginLiteral | ||||
| 	case 'n': // beginning of null
 | ||||
| 		s.step = stateN | ||||
| 		return scanBeginLiteral | ||||
| 	} | ||||
| 	if '1' <= c && c <= '9' { // beginning of 1234.5
 | ||||
| 		s.step = state1 | ||||
| 		return scanBeginLiteral | ||||
| 	} | ||||
| 	return s.error(c, "looking for beginning of value") | ||||
| } | ||||
| 
 | ||||
| // stateBeginStringOrEmpty is the state after reading `{`.
 | ||||
| func stateBeginStringOrEmpty(s *scanner, c byte) int { | ||||
| 	if c <= ' ' && isSpace(c) { | ||||
| 		return scanSkipSpace | ||||
| 	} | ||||
| 	if c == '}' { | ||||
| 		n := len(s.parseState) | ||||
| 		s.parseState[n-1] = parseObjectValue | ||||
| 		return stateEndValue(s, c) | ||||
| 	} | ||||
| 	return stateBeginString(s, c) | ||||
| } | ||||
| 
 | ||||
| // stateBeginString is the state after reading `{"key": value,`.
 | ||||
| func stateBeginString(s *scanner, c byte) int { | ||||
| 	if c <= ' ' && isSpace(c) { | ||||
| 		return scanSkipSpace | ||||
| 	} | ||||
| 	if c == '"' { | ||||
| 		s.step = stateInString | ||||
| 		return scanBeginLiteral | ||||
| 	} | ||||
| 	return s.error(c, "looking for beginning of object key string") | ||||
| } | ||||
| 
 | ||||
| // stateEndValue is the state after completing a value,
 | ||||
| // such as after reading `{}` or `true` or `["x"`.
 | ||||
| func stateEndValue(s *scanner, c byte) int { | ||||
| 	n := len(s.parseState) | ||||
| 	if n == 0 { | ||||
| 		// Completed top-level before the current byte.
 | ||||
| 		s.step = stateEndTop | ||||
| 		s.endTop = true | ||||
| 		return stateEndTop(s, c) | ||||
| 	} | ||||
| 	if c <= ' ' && isSpace(c) { | ||||
| 		s.step = stateEndValue | ||||
| 		return scanSkipSpace | ||||
| 	} | ||||
| 	ps := s.parseState[n-1] | ||||
| 	switch ps { | ||||
| 	case parseObjectKey: | ||||
| 		if c == ':' { | ||||
| 			s.parseState[n-1] = parseObjectValue | ||||
| 			s.step = stateBeginValue | ||||
| 			return scanObjectKey | ||||
| 		} | ||||
| 		return s.error(c, "after object key") | ||||
| 	case parseObjectValue: | ||||
| 		if c == ',' { | ||||
| 			s.parseState[n-1] = parseObjectKey | ||||
| 			s.step = stateBeginString | ||||
| 			return scanObjectValue | ||||
| 		} | ||||
| 		if c == '}' { | ||||
| 			s.popParseState() | ||||
| 			return scanEndObject | ||||
| 		} | ||||
| 		return s.error(c, "after object key:value pair") | ||||
| 	case parseArrayValue: | ||||
| 		if c == ',' { | ||||
| 			s.step = stateBeginValue | ||||
| 			return scanArrayValue | ||||
| 		} | ||||
| 		if c == ']' { | ||||
| 			s.popParseState() | ||||
| 			return scanEndArray | ||||
| 		} | ||||
| 		return s.error(c, "after array element") | ||||
| 	} | ||||
| 	return s.error(c, "") | ||||
| } | ||||
| 
 | ||||
| // stateEndTop is the state after finishing the top-level value,
 | ||||
| // such as after reading `{}` or `[1,2,3]`.
 | ||||
| // Only space characters should be seen now.
 | ||||
| func stateEndTop(s *scanner, c byte) int { | ||||
| 	if c != ' ' && c != '\t' && c != '\r' && c != '\n' { | ||||
| 		// Complain about non-space byte on next call.
 | ||||
| 		s.error(c, "after top-level value") | ||||
| 	} | ||||
| 	return scanEnd | ||||
| } | ||||
| 
 | ||||
| // stateInString is the state after reading `"`.
 | ||||
| func stateInString(s *scanner, c byte) int { | ||||
| 	if c == '"' { | ||||
| 		s.step = stateEndValue | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	if c == '\\' { | ||||
| 		s.step = stateInStringEsc | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	if c < 0x20 { | ||||
| 		return s.error(c, "in string literal") | ||||
| 	} | ||||
| 	return scanContinue | ||||
| } | ||||
| 
 | ||||
| // stateInStringEsc is the state after reading `"\` during a quoted string.
 | ||||
| func stateInStringEsc(s *scanner, c byte) int { | ||||
| 	switch c { | ||||
| 	case 'b', 'f', 'n', 'r', 't', '\\', '/', '"': | ||||
| 		s.step = stateInString | ||||
| 		return scanContinue | ||||
| 	case 'u': | ||||
| 		s.step = stateInStringEscU | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return s.error(c, "in string escape code") | ||||
| } | ||||
| 
 | ||||
| // stateInStringEscU is the state after reading `"\u` during a quoted string.
 | ||||
| func stateInStringEscU(s *scanner, c byte) int { | ||||
| 	if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { | ||||
| 		s.step = stateInStringEscU1 | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	// numbers
 | ||||
| 	return s.error(c, "in \\u hexadecimal character escape") | ||||
| } | ||||
| 
 | ||||
| // stateInStringEscU1 is the state after reading `"\u1` during a quoted string.
 | ||||
| func stateInStringEscU1(s *scanner, c byte) int { | ||||
| 	if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { | ||||
| 		s.step = stateInStringEscU12 | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	// numbers
 | ||||
| 	return s.error(c, "in \\u hexadecimal character escape") | ||||
| } | ||||
| 
 | ||||
| // stateInStringEscU12 is the state after reading `"\u12` during a quoted string.
 | ||||
| func stateInStringEscU12(s *scanner, c byte) int { | ||||
| 	if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { | ||||
| 		s.step = stateInStringEscU123 | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	// numbers
 | ||||
| 	return s.error(c, "in \\u hexadecimal character escape") | ||||
| } | ||||
| 
 | ||||
| // stateInStringEscU123 is the state after reading `"\u123` during a quoted string.
 | ||||
| func stateInStringEscU123(s *scanner, c byte) int { | ||||
| 	if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { | ||||
| 		s.step = stateInString | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	// numbers
 | ||||
| 	return s.error(c, "in \\u hexadecimal character escape") | ||||
| } | ||||
| 
 | ||||
| // stateNeg is the state after reading `-` during a number.
 | ||||
| func stateNeg(s *scanner, c byte) int { | ||||
| 	if c == '0' { | ||||
| 		s.step = state0 | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	if '1' <= c && c <= '9' { | ||||
| 		s.step = state1 | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return s.error(c, "in numeric literal") | ||||
| } | ||||
| 
 | ||||
| // state1 is the state after reading a non-zero integer during a number,
 | ||||
| // such as after reading `1` or `100` but not `0`.
 | ||||
| func state1(s *scanner, c byte) int { | ||||
| 	if '0' <= c && c <= '9' { | ||||
| 		s.step = state1 | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return state0(s, c) | ||||
| } | ||||
| 
 | ||||
| // state0 is the state after reading `0` during a number.
 | ||||
| func state0(s *scanner, c byte) int { | ||||
| 	if c == '.' { | ||||
| 		s.step = stateDot | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	if c == 'e' || c == 'E' { | ||||
| 		s.step = stateE | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return stateEndValue(s, c) | ||||
| } | ||||
| 
 | ||||
| // stateDot is the state after reading the integer and decimal point in a number,
 | ||||
| // such as after reading `1.`.
 | ||||
| func stateDot(s *scanner, c byte) int { | ||||
| 	if '0' <= c && c <= '9' { | ||||
| 		s.step = stateDot0 | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return s.error(c, "after decimal point in numeric literal") | ||||
| } | ||||
| 
 | ||||
| // stateDot0 is the state after reading the integer, decimal point, and subsequent
 | ||||
| // digits of a number, such as after reading `3.14`.
 | ||||
| func stateDot0(s *scanner, c byte) int { | ||||
| 	if '0' <= c && c <= '9' { | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	if c == 'e' || c == 'E' { | ||||
| 		s.step = stateE | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return stateEndValue(s, c) | ||||
| } | ||||
| 
 | ||||
| // stateE is the state after reading the mantissa and e in a number,
 | ||||
| // such as after reading `314e` or `0.314e`.
 | ||||
| func stateE(s *scanner, c byte) int { | ||||
| 	if c == '+' || c == '-' { | ||||
| 		s.step = stateESign | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return stateESign(s, c) | ||||
| } | ||||
| 
 | ||||
| // stateESign is the state after reading the mantissa, e, and sign in a number,
 | ||||
| // such as after reading `314e-` or `0.314e+`.
 | ||||
| func stateESign(s *scanner, c byte) int { | ||||
| 	if '0' <= c && c <= '9' { | ||||
| 		s.step = stateE0 | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return s.error(c, "in exponent of numeric literal") | ||||
| } | ||||
| 
 | ||||
| // stateE0 is the state after reading the mantissa, e, optional sign,
 | ||||
| // and at least one digit of the exponent in a number,
 | ||||
| // such as after reading `314e-2` or `0.314e+1` or `3.14e0`.
 | ||||
| func stateE0(s *scanner, c byte) int { | ||||
| 	if '0' <= c && c <= '9' { | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return stateEndValue(s, c) | ||||
| } | ||||
| 
 | ||||
| // stateT is the state after reading `t`.
 | ||||
| func stateT(s *scanner, c byte) int { | ||||
| 	if c == 'r' { | ||||
| 		s.step = stateTr | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return s.error(c, "in literal true (expecting 'r')") | ||||
| } | ||||
| 
 | ||||
| // stateTr is the state after reading `tr`.
 | ||||
| func stateTr(s *scanner, c byte) int { | ||||
| 	if c == 'u' { | ||||
| 		s.step = stateTru | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return s.error(c, "in literal true (expecting 'u')") | ||||
| } | ||||
| 
 | ||||
| // stateTru is the state after reading `tru`.
 | ||||
| func stateTru(s *scanner, c byte) int { | ||||
| 	if c == 'e' { | ||||
| 		s.step = stateEndValue | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return s.error(c, "in literal true (expecting 'e')") | ||||
| } | ||||
| 
 | ||||
| // stateF is the state after reading `f`.
 | ||||
| func stateF(s *scanner, c byte) int { | ||||
| 	if c == 'a' { | ||||
| 		s.step = stateFa | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return s.error(c, "in literal false (expecting 'a')") | ||||
| } | ||||
| 
 | ||||
| // stateFa is the state after reading `fa`.
 | ||||
| func stateFa(s *scanner, c byte) int { | ||||
| 	if c == 'l' { | ||||
| 		s.step = stateFal | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return s.error(c, "in literal false (expecting 'l')") | ||||
| } | ||||
| 
 | ||||
| // stateFal is the state after reading `fal`.
 | ||||
| func stateFal(s *scanner, c byte) int { | ||||
| 	if c == 's' { | ||||
| 		s.step = stateFals | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return s.error(c, "in literal false (expecting 's')") | ||||
| } | ||||
| 
 | ||||
| // stateFals is the state after reading `fals`.
 | ||||
| func stateFals(s *scanner, c byte) int { | ||||
| 	if c == 'e' { | ||||
| 		s.step = stateEndValue | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return s.error(c, "in literal false (expecting 'e')") | ||||
| } | ||||
| 
 | ||||
| // stateN is the state after reading `n`.
 | ||||
| func stateN(s *scanner, c byte) int { | ||||
| 	if c == 'u' { | ||||
| 		s.step = stateNu | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return s.error(c, "in literal null (expecting 'u')") | ||||
| } | ||||
| 
 | ||||
| // stateNu is the state after reading `nu`.
 | ||||
| func stateNu(s *scanner, c byte) int { | ||||
| 	if c == 'l' { | ||||
| 		s.step = stateNul | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return s.error(c, "in literal null (expecting 'l')") | ||||
| } | ||||
| 
 | ||||
| // stateNul is the state after reading `nul`.
 | ||||
| func stateNul(s *scanner, c byte) int { | ||||
| 	if c == 'l' { | ||||
| 		s.step = stateEndValue | ||||
| 		return scanContinue | ||||
| 	} | ||||
| 	return s.error(c, "in literal null (expecting 'l')") | ||||
| } | ||||
| 
 | ||||
| // stateError is the state after reaching a syntax error,
 | ||||
| // such as after reading `[1}` or `5.1.2`.
 | ||||
| func stateError(s *scanner, c byte) int { | ||||
| 	return scanError | ||||
| } | ||||
| 
 | ||||
| // error records an error and switches to the error state.
 | ||||
| func (s *scanner) error(c byte, context string) int { | ||||
| 	s.step = stateError | ||||
| 	s.err = &SyntaxError{"invalid character " + quoteChar(c) + " " + context, s.bytes} | ||||
| 	return scanError | ||||
| } | ||||
| 
 | ||||
| // quoteChar formats c as a quoted character literal
 | ||||
| func quoteChar(c byte) string { | ||||
| 	// special cases - different from quoted strings
 | ||||
| 	if c == '\'' { | ||||
| 		return `'\''` | ||||
| 	} | ||||
| 	if c == '"' { | ||||
| 		return `'"'` | ||||
| 	} | ||||
| 
 | ||||
| 	// use quoted string with different quotation marks
 | ||||
| 	s := strconv.Quote(string(c)) | ||||
| 	return "'" + s[1:len(s)-1] + "'" | ||||
| } | ||||
| 
 | ||||
| // undo causes the scanner to return scanCode from the next state transition.
 | ||||
| // This gives callers a simple 1-byte undo mechanism.
 | ||||
| func (s *scanner) undo(scanCode int) { | ||||
| 	if s.redo { | ||||
| 		panic("json: invalid use of scanner") | ||||
| 	} | ||||
| 	s.redoCode = scanCode | ||||
| 	s.redoState = s.step | ||||
| 	s.step = stateRedo | ||||
| 	s.redo = true | ||||
| } | ||||
| 
 | ||||
| // stateRedo helps implement the scanner's 1-byte undo.
 | ||||
| func stateRedo(s *scanner, c byte) int { | ||||
| 	s.redo = false | ||||
| 	s.step = s.redoState | ||||
| 	return s.redoCode | ||||
| } | ||||
|  | @ -1,485 +0,0 @@ | |||
| // Copyright 2010 The Go Authors.  All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package json | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| ) | ||||
| 
 | ||||
| // A Decoder reads and decodes JSON objects from an input stream.
 | ||||
| type Decoder struct { | ||||
| 	r     io.Reader | ||||
| 	buf   []byte | ||||
| 	d     decodeState | ||||
| 	scanp int // start of unread data in buf
 | ||||
| 	scan  scanner | ||||
| 	err   error | ||||
| 
 | ||||
| 	tokenState int | ||||
| 	tokenStack []int | ||||
| } | ||||
| 
 | ||||
| // NewDecoder returns a new decoder that reads from r.
 | ||||
| //
 | ||||
| // The decoder introduces its own buffering and may
 | ||||
| // read data from r beyond the JSON values requested.
 | ||||
| func NewDecoder(r io.Reader) *Decoder { | ||||
| 	return &Decoder{r: r} | ||||
| } | ||||
| 
 | ||||
| // Deprecated: Use `SetNumberType` instead
 | ||||
| // UseNumber causes the Decoder to unmarshal a number into an interface{} as a
 | ||||
| // Number instead of as a float64.
 | ||||
| func (dec *Decoder) UseNumber() { dec.d.numberType = UnmarshalJSONNumber } | ||||
| 
 | ||||
| // SetNumberType causes the Decoder to unmarshal a number into an interface{} as a
 | ||||
| // Number, float64 or int64 depending on `t` enum value.
 | ||||
| func (dec *Decoder) SetNumberType(t NumberUnmarshalType) { dec.d.numberType = t } | ||||
| 
 | ||||
| // Decode reads the next JSON-encoded value from its
 | ||||
| // input and stores it in the value pointed to by v.
 | ||||
| //
 | ||||
| // See the documentation for Unmarshal for details about
 | ||||
| // the conversion of JSON into a Go value.
 | ||||
| func (dec *Decoder) Decode(v interface{}) error { | ||||
| 	if dec.err != nil { | ||||
| 		return dec.err | ||||
| 	} | ||||
| 
 | ||||
| 	if err := dec.tokenPrepareForDecode(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if !dec.tokenValueAllowed() { | ||||
| 		return &SyntaxError{msg: "not at beginning of value"} | ||||
| 	} | ||||
| 
 | ||||
| 	// Read whole value into buffer.
 | ||||
| 	n, err := dec.readValue() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	dec.d.init(dec.buf[dec.scanp : dec.scanp+n]) | ||||
| 	dec.scanp += n | ||||
| 
 | ||||
| 	// Don't save err from unmarshal into dec.err:
 | ||||
| 	// the connection is still usable since we read a complete JSON
 | ||||
| 	// object from it before the error happened.
 | ||||
| 	err = dec.d.unmarshal(v) | ||||
| 
 | ||||
| 	// fixup token streaming state
 | ||||
| 	dec.tokenValueEnd() | ||||
| 
 | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // Buffered returns a reader of the data remaining in the Decoder's
 | ||||
| // buffer. The reader is valid until the next call to Decode.
 | ||||
| func (dec *Decoder) Buffered() io.Reader { | ||||
| 	return bytes.NewReader(dec.buf[dec.scanp:]) | ||||
| } | ||||
| 
 | ||||
| // readValue reads a JSON value into dec.buf.
 | ||||
| // It returns the length of the encoding.
 | ||||
| func (dec *Decoder) readValue() (int, error) { | ||||
| 	dec.scan.reset() | ||||
| 
 | ||||
| 	scanp := dec.scanp | ||||
| 	var err error | ||||
| Input: | ||||
| 	for { | ||||
| 		// Look in the buffer for a new value.
 | ||||
| 		for i, c := range dec.buf[scanp:] { | ||||
| 			dec.scan.bytes++ | ||||
| 			v := dec.scan.step(&dec.scan, c) | ||||
| 			if v == scanEnd { | ||||
| 				scanp += i | ||||
| 				break Input | ||||
| 			} | ||||
| 			// scanEnd is delayed one byte.
 | ||||
| 			// We might block trying to get that byte from src,
 | ||||
| 			// so instead invent a space byte.
 | ||||
| 			if (v == scanEndObject || v == scanEndArray) && dec.scan.step(&dec.scan, ' ') == scanEnd { | ||||
| 				scanp += i + 1 | ||||
| 				break Input | ||||
| 			} | ||||
| 			if v == scanError { | ||||
| 				dec.err = dec.scan.err | ||||
| 				return 0, dec.scan.err | ||||
| 			} | ||||
| 		} | ||||
| 		scanp = len(dec.buf) | ||||
| 
 | ||||
| 		// Did the last read have an error?
 | ||||
| 		// Delayed until now to allow buffer scan.
 | ||||
| 		if err != nil { | ||||
| 			if err == io.EOF { | ||||
| 				if dec.scan.step(&dec.scan, ' ') == scanEnd { | ||||
| 					break Input | ||||
| 				} | ||||
| 				if nonSpace(dec.buf) { | ||||
| 					err = io.ErrUnexpectedEOF | ||||
| 				} | ||||
| 			} | ||||
| 			dec.err = err | ||||
| 			return 0, err | ||||
| 		} | ||||
| 
 | ||||
| 		n := scanp - dec.scanp | ||||
| 		err = dec.refill() | ||||
| 		scanp = dec.scanp + n | ||||
| 	} | ||||
| 	return scanp - dec.scanp, nil | ||||
| } | ||||
| 
 | ||||
| func (dec *Decoder) refill() error { | ||||
| 	// Make room to read more into the buffer.
 | ||||
| 	// First slide down data already consumed.
 | ||||
| 	if dec.scanp > 0 { | ||||
| 		n := copy(dec.buf, dec.buf[dec.scanp:]) | ||||
| 		dec.buf = dec.buf[:n] | ||||
| 		dec.scanp = 0 | ||||
| 	} | ||||
| 
 | ||||
| 	// Grow buffer if not large enough.
 | ||||
| 	const minRead = 512 | ||||
| 	if cap(dec.buf)-len(dec.buf) < minRead { | ||||
| 		newBuf := make([]byte, len(dec.buf), 2*cap(dec.buf)+minRead) | ||||
| 		copy(newBuf, dec.buf) | ||||
| 		dec.buf = newBuf | ||||
| 	} | ||||
| 
 | ||||
| 	// Read.  Delay error for next iteration (after scan).
 | ||||
| 	n, err := dec.r.Read(dec.buf[len(dec.buf):cap(dec.buf)]) | ||||
| 	dec.buf = dec.buf[0 : len(dec.buf)+n] | ||||
| 
 | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func nonSpace(b []byte) bool { | ||||
| 	for _, c := range b { | ||||
| 		if !isSpace(c) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // An Encoder writes JSON objects to an output stream.
 | ||||
| type Encoder struct { | ||||
| 	w   io.Writer | ||||
| 	err error | ||||
| } | ||||
| 
 | ||||
| // NewEncoder returns a new encoder that writes to w.
 | ||||
| func NewEncoder(w io.Writer) *Encoder { | ||||
| 	return &Encoder{w: w} | ||||
| } | ||||
| 
 | ||||
| // Encode writes the JSON encoding of v to the stream,
 | ||||
| // followed by a newline character.
 | ||||
| //
 | ||||
| // See the documentation for Marshal for details about the
 | ||||
| // conversion of Go values to JSON.
 | ||||
| func (enc *Encoder) Encode(v interface{}) error { | ||||
| 	if enc.err != nil { | ||||
| 		return enc.err | ||||
| 	} | ||||
| 	e := newEncodeState() | ||||
| 	err := e.marshal(v) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Terminate each value with a newline.
 | ||||
| 	// This makes the output look a little nicer
 | ||||
| 	// when debugging, and some kind of space
 | ||||
| 	// is required if the encoded value was a number,
 | ||||
| 	// so that the reader knows there aren't more
 | ||||
| 	// digits coming.
 | ||||
| 	e.WriteByte('\n') | ||||
| 
 | ||||
| 	if _, err = enc.w.Write(e.Bytes()); err != nil { | ||||
| 		enc.err = err | ||||
| 	} | ||||
| 	encodeStatePool.Put(e) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // RawMessage is a raw encoded JSON object.
 | ||||
| // It implements Marshaler and Unmarshaler and can
 | ||||
| // be used to delay JSON decoding or precompute a JSON encoding.
 | ||||
| type RawMessage []byte | ||||
| 
 | ||||
| // MarshalJSON returns *m as the JSON encoding of m.
 | ||||
| func (m *RawMessage) MarshalJSON() ([]byte, error) { | ||||
| 	return *m, nil | ||||
| } | ||||
| 
 | ||||
| // UnmarshalJSON sets *m to a copy of data.
 | ||||
| func (m *RawMessage) UnmarshalJSON(data []byte) error { | ||||
| 	if m == nil { | ||||
| 		return errors.New("json.RawMessage: UnmarshalJSON on nil pointer") | ||||
| 	} | ||||
| 	*m = append((*m)[0:0], data...) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| var _ Marshaler = (*RawMessage)(nil) | ||||
| var _ Unmarshaler = (*RawMessage)(nil) | ||||
| 
 | ||||
| // A Token holds a value of one of these types:
 | ||||
| //
 | ||||
| //	Delim, for the four JSON delimiters [ ] { }
 | ||||
| //	bool, for JSON booleans
 | ||||
| //	float64, for JSON numbers
 | ||||
| //	Number, for JSON numbers
 | ||||
| //	string, for JSON string literals
 | ||||
| //	nil, for JSON null
 | ||||
| //
 | ||||
| type Token interface{} | ||||
| 
 | ||||
| const ( | ||||
| 	tokenTopValue = iota | ||||
| 	tokenArrayStart | ||||
| 	tokenArrayValue | ||||
| 	tokenArrayComma | ||||
| 	tokenObjectStart | ||||
| 	tokenObjectKey | ||||
| 	tokenObjectColon | ||||
| 	tokenObjectValue | ||||
| 	tokenObjectComma | ||||
| ) | ||||
| 
 | ||||
| // advance tokenstate from a separator state to a value state
 | ||||
| func (dec *Decoder) tokenPrepareForDecode() error { | ||||
| 	// Note: Not calling peek before switch, to avoid
 | ||||
| 	// putting peek into the standard Decode path.
 | ||||
| 	// peek is only called when using the Token API.
 | ||||
| 	switch dec.tokenState { | ||||
| 	case tokenArrayComma: | ||||
| 		c, err := dec.peek() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if c != ',' { | ||||
| 			return &SyntaxError{"expected comma after array element", 0} | ||||
| 		} | ||||
| 		dec.scanp++ | ||||
| 		dec.tokenState = tokenArrayValue | ||||
| 	case tokenObjectColon: | ||||
| 		c, err := dec.peek() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if c != ':' { | ||||
| 			return &SyntaxError{"expected colon after object key", 0} | ||||
| 		} | ||||
| 		dec.scanp++ | ||||
| 		dec.tokenState = tokenObjectValue | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (dec *Decoder) tokenValueAllowed() bool { | ||||
| 	switch dec.tokenState { | ||||
| 	case tokenTopValue, tokenArrayStart, tokenArrayValue, tokenObjectValue: | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func (dec *Decoder) tokenValueEnd() { | ||||
| 	switch dec.tokenState { | ||||
| 	case tokenArrayStart, tokenArrayValue: | ||||
| 		dec.tokenState = tokenArrayComma | ||||
| 	case tokenObjectValue: | ||||
| 		dec.tokenState = tokenObjectComma | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // A Delim is a JSON array or object delimiter, one of [ ] { or }.
 | ||||
| type Delim rune | ||||
| 
 | ||||
| func (d Delim) String() string { | ||||
| 	return string(d) | ||||
| } | ||||
| 
 | ||||
| // Token returns the next JSON token in the input stream.
 | ||||
| // At the end of the input stream, Token returns nil, io.EOF.
 | ||||
| //
 | ||||
| // Token guarantees that the delimiters [ ] { } it returns are
 | ||||
| // properly nested and matched: if Token encounters an unexpected
 | ||||
| // delimiter in the input, it will return an error.
 | ||||
| //
 | ||||
| // The input stream consists of basic JSON values—bool, string,
 | ||||
| // number, and null—along with delimiters [ ] { } of type Delim
 | ||||
| // to mark the start and end of arrays and objects.
 | ||||
| // Commas and colons are elided.
 | ||||
| func (dec *Decoder) Token() (Token, error) { | ||||
| 	for { | ||||
| 		c, err := dec.peek() | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		switch c { | ||||
| 		case '[': | ||||
| 			if !dec.tokenValueAllowed() { | ||||
| 				return dec.tokenError(c) | ||||
| 			} | ||||
| 			dec.scanp++ | ||||
| 			dec.tokenStack = append(dec.tokenStack, dec.tokenState) | ||||
| 			dec.tokenState = tokenArrayStart | ||||
| 			return Delim('['), nil | ||||
| 
 | ||||
| 		case ']': | ||||
| 			if dec.tokenState != tokenArrayStart && dec.tokenState != tokenArrayComma { | ||||
| 				return dec.tokenError(c) | ||||
| 			} | ||||
| 			dec.scanp++ | ||||
| 			dec.tokenState = dec.tokenStack[len(dec.tokenStack)-1] | ||||
| 			dec.tokenStack = dec.tokenStack[:len(dec.tokenStack)-1] | ||||
| 			dec.tokenValueEnd() | ||||
| 			return Delim(']'), nil | ||||
| 
 | ||||
| 		case '{': | ||||
| 			if !dec.tokenValueAllowed() { | ||||
| 				return dec.tokenError(c) | ||||
| 			} | ||||
| 			dec.scanp++ | ||||
| 			dec.tokenStack = append(dec.tokenStack, dec.tokenState) | ||||
| 			dec.tokenState = tokenObjectStart | ||||
| 			return Delim('{'), nil | ||||
| 
 | ||||
| 		case '}': | ||||
| 			if dec.tokenState != tokenObjectStart && dec.tokenState != tokenObjectComma { | ||||
| 				return dec.tokenError(c) | ||||
| 			} | ||||
| 			dec.scanp++ | ||||
| 			dec.tokenState = dec.tokenStack[len(dec.tokenStack)-1] | ||||
| 			dec.tokenStack = dec.tokenStack[:len(dec.tokenStack)-1] | ||||
| 			dec.tokenValueEnd() | ||||
| 			return Delim('}'), nil | ||||
| 
 | ||||
| 		case ':': | ||||
| 			if dec.tokenState != tokenObjectColon { | ||||
| 				return dec.tokenError(c) | ||||
| 			} | ||||
| 			dec.scanp++ | ||||
| 			dec.tokenState = tokenObjectValue | ||||
| 			continue | ||||
| 
 | ||||
| 		case ',': | ||||
| 			if dec.tokenState == tokenArrayComma { | ||||
| 				dec.scanp++ | ||||
| 				dec.tokenState = tokenArrayValue | ||||
| 				continue | ||||
| 			} | ||||
| 			if dec.tokenState == tokenObjectComma { | ||||
| 				dec.scanp++ | ||||
| 				dec.tokenState = tokenObjectKey | ||||
| 				continue | ||||
| 			} | ||||
| 			return dec.tokenError(c) | ||||
| 
 | ||||
| 		case '"': | ||||
| 			if dec.tokenState == tokenObjectStart || dec.tokenState == tokenObjectKey { | ||||
| 				var x string | ||||
| 				old := dec.tokenState | ||||
| 				dec.tokenState = tokenTopValue | ||||
| 				err := dec.Decode(&x) | ||||
| 				dec.tokenState = old | ||||
| 				if err != nil { | ||||
| 					clearOffset(err) | ||||
| 					return nil, err | ||||
| 				} | ||||
| 				dec.tokenState = tokenObjectColon | ||||
| 				return x, nil | ||||
| 			} | ||||
| 			fallthrough | ||||
| 
 | ||||
| 		default: | ||||
| 			if !dec.tokenValueAllowed() { | ||||
| 				return dec.tokenError(c) | ||||
| 			} | ||||
| 			var x interface{} | ||||
| 			if err := dec.Decode(&x); err != nil { | ||||
| 				clearOffset(err) | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			return x, nil | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func clearOffset(err error) { | ||||
| 	if s, ok := err.(*SyntaxError); ok { | ||||
| 		s.Offset = 0 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (dec *Decoder) tokenError(c byte) (Token, error) { | ||||
| 	var context string | ||||
| 	switch dec.tokenState { | ||||
| 	case tokenTopValue: | ||||
| 		context = " looking for beginning of value" | ||||
| 	case tokenArrayStart, tokenArrayValue, tokenObjectValue: | ||||
| 		context = " looking for beginning of value" | ||||
| 	case tokenArrayComma: | ||||
| 		context = " after array element" | ||||
| 	case tokenObjectKey: | ||||
| 		context = " looking for beginning of object key string" | ||||
| 	case tokenObjectColon: | ||||
| 		context = " after object key" | ||||
| 	case tokenObjectComma: | ||||
| 		context = " after object key:value pair" | ||||
| 	} | ||||
| 	return nil, &SyntaxError{"invalid character " + quoteChar(c) + " " + context, 0} | ||||
| } | ||||
| 
 | ||||
| // More reports whether there is another element in the
 | ||||
| // current array or object being parsed.
 | ||||
| func (dec *Decoder) More() bool { | ||||
| 	c, err := dec.peek() | ||||
| 	return err == nil && c != ']' && c != '}' | ||||
| } | ||||
| 
 | ||||
| func (dec *Decoder) peek() (byte, error) { | ||||
| 	var err error | ||||
| 	for { | ||||
| 		for i := dec.scanp; i < len(dec.buf); i++ { | ||||
| 			c := dec.buf[i] | ||||
| 			if isSpace(c) { | ||||
| 				continue | ||||
| 			} | ||||
| 			dec.scanp = i | ||||
| 			return c, nil | ||||
| 		} | ||||
| 		// buffer has been scanned, now report any error
 | ||||
| 		if err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 		err = dec.refill() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| TODO | ||||
| 
 | ||||
| // EncodeToken writes the given JSON token to the stream.
 | ||||
| // It returns an error if the delimiters [ ] { } are not properly used.
 | ||||
| //
 | ||||
| // EncodeToken does not call Flush, because usually it is part of
 | ||||
| // a larger operation such as Encode, and those will call Flush when finished.
 | ||||
| // Callers that create an Encoder and then invoke EncodeToken directly,
 | ||||
| // without using Encode, need to call Flush when finished to ensure that
 | ||||
| // the JSON is written to the underlying writer.
 | ||||
| func (e *Encoder) EncodeToken(t Token) error  { | ||||
| 	... | ||||
| } | ||||
| 
 | ||||
| */ | ||||
|  | @ -1,44 +0,0 @@ | |||
| // Copyright 2011 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package json | ||||
| 
 | ||||
| import ( | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // tagOptions is the string following a comma in a struct field's "json"
 | ||||
| // tag, or the empty string. It does not include the leading comma.
 | ||||
| type tagOptions string | ||||
| 
 | ||||
| // parseTag splits a struct field's json tag into its name and
 | ||||
| // comma-separated options.
 | ||||
| func parseTag(tag string) (string, tagOptions) { | ||||
| 	if idx := strings.Index(tag, ","); idx != -1 { | ||||
| 		return tag[:idx], tagOptions(tag[idx+1:]) | ||||
| 	} | ||||
| 	return tag, tagOptions("") | ||||
| } | ||||
| 
 | ||||
| // Contains reports whether a comma-separated list of options
 | ||||
| // contains a particular substr flag. substr must be surrounded by a
 | ||||
| // string boundary or commas.
 | ||||
| func (o tagOptions) Contains(optionName string) bool { | ||||
| 	if len(o) == 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	s := string(o) | ||||
| 	for s != "" { | ||||
| 		var next string | ||||
| 		i := strings.Index(s, ",") | ||||
| 		if i >= 0 { | ||||
| 			s, next = s[:i], s[i+1:] | ||||
| 		} | ||||
| 		if s == optionName { | ||||
| 			return true | ||||
| 		} | ||||
| 		s = next | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | @ -1,294 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2014 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package jose | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"gopkg.in/square/go-jose.v2/json" | ||||
| ) | ||||
| 
 | ||||
| // rawJSONWebEncryption represents a raw JWE JSON object. Used for parsing/serializing.
 | ||||
| type rawJSONWebEncryption struct { | ||||
| 	Protected    *byteBuffer        `json:"protected,omitempty"` | ||||
| 	Unprotected  *rawHeader         `json:"unprotected,omitempty"` | ||||
| 	Header       *rawHeader         `json:"header,omitempty"` | ||||
| 	Recipients   []rawRecipientInfo `json:"recipients,omitempty"` | ||||
| 	Aad          *byteBuffer        `json:"aad,omitempty"` | ||||
| 	EncryptedKey *byteBuffer        `json:"encrypted_key,omitempty"` | ||||
| 	Iv           *byteBuffer        `json:"iv,omitempty"` | ||||
| 	Ciphertext   *byteBuffer        `json:"ciphertext,omitempty"` | ||||
| 	Tag          *byteBuffer        `json:"tag,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // rawRecipientInfo represents a raw JWE Per-Recipient header JSON object. Used for parsing/serializing.
 | ||||
| type rawRecipientInfo struct { | ||||
| 	Header       *rawHeader `json:"header,omitempty"` | ||||
| 	EncryptedKey string     `json:"encrypted_key,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // JSONWebEncryption represents an encrypted JWE object after parsing.
 | ||||
| type JSONWebEncryption struct { | ||||
| 	Header                   Header | ||||
| 	protected, unprotected   *rawHeader | ||||
| 	recipients               []recipientInfo | ||||
| 	aad, iv, ciphertext, tag []byte | ||||
| 	original                 *rawJSONWebEncryption | ||||
| } | ||||
| 
 | ||||
| // recipientInfo represents a raw JWE Per-Recipient header JSON object after parsing.
 | ||||
| type recipientInfo struct { | ||||
| 	header       *rawHeader | ||||
| 	encryptedKey []byte | ||||
| } | ||||
| 
 | ||||
| // GetAuthData retrieves the (optional) authenticated data attached to the object.
 | ||||
| func (obj JSONWebEncryption) GetAuthData() []byte { | ||||
| 	if obj.aad != nil { | ||||
| 		out := make([]byte, len(obj.aad)) | ||||
| 		copy(out, obj.aad) | ||||
| 		return out | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Get the merged header values
 | ||||
| func (obj JSONWebEncryption) mergedHeaders(recipient *recipientInfo) rawHeader { | ||||
| 	out := rawHeader{} | ||||
| 	out.merge(obj.protected) | ||||
| 	out.merge(obj.unprotected) | ||||
| 
 | ||||
| 	if recipient != nil { | ||||
| 		out.merge(recipient.header) | ||||
| 	} | ||||
| 
 | ||||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // Get the additional authenticated data from a JWE object.
 | ||||
| func (obj JSONWebEncryption) computeAuthData() []byte { | ||||
| 	var protected string | ||||
| 
 | ||||
| 	if obj.original != nil && obj.original.Protected != nil { | ||||
| 		protected = obj.original.Protected.base64() | ||||
| 	} else if obj.protected != nil { | ||||
| 		protected = base64.RawURLEncoding.EncodeToString(mustSerializeJSON((obj.protected))) | ||||
| 	} else { | ||||
| 		protected = "" | ||||
| 	} | ||||
| 
 | ||||
| 	output := []byte(protected) | ||||
| 	if obj.aad != nil { | ||||
| 		output = append(output, '.') | ||||
| 		output = append(output, []byte(base64.RawURLEncoding.EncodeToString(obj.aad))...) | ||||
| 	} | ||||
| 
 | ||||
| 	return output | ||||
| } | ||||
| 
 | ||||
| // ParseEncrypted parses an encrypted message in compact or full serialization format.
 | ||||
| func ParseEncrypted(input string) (*JSONWebEncryption, error) { | ||||
| 	input = stripWhitespace(input) | ||||
| 	if strings.HasPrefix(input, "{") { | ||||
| 		return parseEncryptedFull(input) | ||||
| 	} | ||||
| 
 | ||||
| 	return parseEncryptedCompact(input) | ||||
| } | ||||
| 
 | ||||
| // parseEncryptedFull parses a message in compact format.
 | ||||
| func parseEncryptedFull(input string) (*JSONWebEncryption, error) { | ||||
| 	var parsed rawJSONWebEncryption | ||||
| 	err := json.Unmarshal([]byte(input), &parsed) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return parsed.sanitized() | ||||
| } | ||||
| 
 | ||||
| // sanitized produces a cleaned-up JWE object from the raw JSON.
 | ||||
| func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) { | ||||
| 	obj := &JSONWebEncryption{ | ||||
| 		original:    parsed, | ||||
| 		unprotected: parsed.Unprotected, | ||||
| 	} | ||||
| 
 | ||||
| 	// Check that there is not a nonce in the unprotected headers
 | ||||
| 	if parsed.Unprotected != nil { | ||||
| 		if nonce := parsed.Unprotected.getNonce(); nonce != "" { | ||||
| 			return nil, ErrUnprotectedNonce | ||||
| 		} | ||||
| 	} | ||||
| 	if parsed.Header != nil { | ||||
| 		if nonce := parsed.Header.getNonce(); nonce != "" { | ||||
| 			return nil, ErrUnprotectedNonce | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 { | ||||
| 		err := json.Unmarshal(parsed.Protected.bytes(), &obj.protected) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("square/go-jose: invalid protected header: %s, %s", err, parsed.Protected.base64()) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Note: this must be called _after_ we parse the protected header,
 | ||||
| 	// otherwise fields from the protected header will not get picked up.
 | ||||
| 	var err error | ||||
| 	mergedHeaders := obj.mergedHeaders(nil) | ||||
| 	obj.Header, err = mergedHeaders.sanitized() | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("square/go-jose: cannot sanitize merged headers: %v (%v)", err, mergedHeaders) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(parsed.Recipients) == 0 { | ||||
| 		obj.recipients = []recipientInfo{ | ||||
| 			{ | ||||
| 				header:       parsed.Header, | ||||
| 				encryptedKey: parsed.EncryptedKey.bytes(), | ||||
| 			}, | ||||
| 		} | ||||
| 	} else { | ||||
| 		obj.recipients = make([]recipientInfo, len(parsed.Recipients)) | ||||
| 		for r := range parsed.Recipients { | ||||
| 			encryptedKey, err := base64.RawURLEncoding.DecodeString(parsed.Recipients[r].EncryptedKey) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 
 | ||||
| 			// Check that there is not a nonce in the unprotected header
 | ||||
| 			if parsed.Recipients[r].Header != nil && parsed.Recipients[r].Header.getNonce() != "" { | ||||
| 				return nil, ErrUnprotectedNonce | ||||
| 			} | ||||
| 
 | ||||
| 			obj.recipients[r].header = parsed.Recipients[r].Header | ||||
| 			obj.recipients[r].encryptedKey = encryptedKey | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for _, recipient := range obj.recipients { | ||||
| 		headers := obj.mergedHeaders(&recipient) | ||||
| 		if headers.getAlgorithm() == "" || headers.getEncryption() == "" { | ||||
| 			return nil, fmt.Errorf("square/go-jose: message is missing alg/enc headers") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	obj.iv = parsed.Iv.bytes() | ||||
| 	obj.ciphertext = parsed.Ciphertext.bytes() | ||||
| 	obj.tag = parsed.Tag.bytes() | ||||
| 	obj.aad = parsed.Aad.bytes() | ||||
| 
 | ||||
| 	return obj, nil | ||||
| } | ||||
| 
 | ||||
| // parseEncryptedCompact parses a message in compact format.
 | ||||
| func parseEncryptedCompact(input string) (*JSONWebEncryption, error) { | ||||
| 	parts := strings.Split(input, ".") | ||||
| 	if len(parts) != 5 { | ||||
| 		return nil, fmt.Errorf("square/go-jose: compact JWE format must have five parts") | ||||
| 	} | ||||
| 
 | ||||
| 	rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	encryptedKey, err := base64.RawURLEncoding.DecodeString(parts[1]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	iv, err := base64.RawURLEncoding.DecodeString(parts[2]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	ciphertext, err := base64.RawURLEncoding.DecodeString(parts[3]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	tag, err := base64.RawURLEncoding.DecodeString(parts[4]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	raw := &rawJSONWebEncryption{ | ||||
| 		Protected:    newBuffer(rawProtected), | ||||
| 		EncryptedKey: newBuffer(encryptedKey), | ||||
| 		Iv:           newBuffer(iv), | ||||
| 		Ciphertext:   newBuffer(ciphertext), | ||||
| 		Tag:          newBuffer(tag), | ||||
| 	} | ||||
| 
 | ||||
| 	return raw.sanitized() | ||||
| } | ||||
| 
 | ||||
| // CompactSerialize serializes an object using the compact serialization format.
 | ||||
| func (obj JSONWebEncryption) CompactSerialize() (string, error) { | ||||
| 	if len(obj.recipients) != 1 || obj.unprotected != nil || | ||||
| 		obj.protected == nil || obj.recipients[0].header != nil { | ||||
| 		return "", ErrNotSupported | ||||
| 	} | ||||
| 
 | ||||
| 	serializedProtected := mustSerializeJSON(obj.protected) | ||||
| 
 | ||||
| 	return fmt.Sprintf( | ||||
| 		"%s.%s.%s.%s.%s", | ||||
| 		base64.RawURLEncoding.EncodeToString(serializedProtected), | ||||
| 		base64.RawURLEncoding.EncodeToString(obj.recipients[0].encryptedKey), | ||||
| 		base64.RawURLEncoding.EncodeToString(obj.iv), | ||||
| 		base64.RawURLEncoding.EncodeToString(obj.ciphertext), | ||||
| 		base64.RawURLEncoding.EncodeToString(obj.tag)), nil | ||||
| } | ||||
| 
 | ||||
| // FullSerialize serializes an object using the full JSON serialization format.
 | ||||
| func (obj JSONWebEncryption) FullSerialize() string { | ||||
| 	raw := rawJSONWebEncryption{ | ||||
| 		Unprotected:  obj.unprotected, | ||||
| 		Iv:           newBuffer(obj.iv), | ||||
| 		Ciphertext:   newBuffer(obj.ciphertext), | ||||
| 		EncryptedKey: newBuffer(obj.recipients[0].encryptedKey), | ||||
| 		Tag:          newBuffer(obj.tag), | ||||
| 		Aad:          newBuffer(obj.aad), | ||||
| 		Recipients:   []rawRecipientInfo{}, | ||||
| 	} | ||||
| 
 | ||||
| 	if len(obj.recipients) > 1 { | ||||
| 		for _, recipient := range obj.recipients { | ||||
| 			info := rawRecipientInfo{ | ||||
| 				Header:       recipient.header, | ||||
| 				EncryptedKey: base64.RawURLEncoding.EncodeToString(recipient.encryptedKey), | ||||
| 			} | ||||
| 			raw.Recipients = append(raw.Recipients, info) | ||||
| 		} | ||||
| 	} else { | ||||
| 		// Use flattened serialization
 | ||||
| 		raw.Header = obj.recipients[0].header | ||||
| 		raw.EncryptedKey = newBuffer(obj.recipients[0].encryptedKey) | ||||
| 	} | ||||
| 
 | ||||
| 	if obj.protected != nil { | ||||
| 		raw.Protected = newBuffer(mustSerializeJSON(obj.protected)) | ||||
| 	} | ||||
| 
 | ||||
| 	return string(mustSerializeJSON(raw)) | ||||
| } | ||||
|  | @ -1,760 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2014 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package jose | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/elliptic" | ||||
| 	"crypto/rsa" | ||||
| 	"crypto/sha1" | ||||
| 	"crypto/sha256" | ||||
| 	"crypto/x509" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/hex" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"math/big" | ||||
| 	"net/url" | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"golang.org/x/crypto/ed25519" | ||||
| 
 | ||||
| 	"gopkg.in/square/go-jose.v2/json" | ||||
| ) | ||||
| 
 | ||||
| // rawJSONWebKey represents a public or private key in JWK format, used for parsing/serializing.
 | ||||
| type rawJSONWebKey struct { | ||||
| 	Use string      `json:"use,omitempty"` | ||||
| 	Kty string      `json:"kty,omitempty"` | ||||
| 	Kid string      `json:"kid,omitempty"` | ||||
| 	Crv string      `json:"crv,omitempty"` | ||||
| 	Alg string      `json:"alg,omitempty"` | ||||
| 	K   *byteBuffer `json:"k,omitempty"` | ||||
| 	X   *byteBuffer `json:"x,omitempty"` | ||||
| 	Y   *byteBuffer `json:"y,omitempty"` | ||||
| 	N   *byteBuffer `json:"n,omitempty"` | ||||
| 	E   *byteBuffer `json:"e,omitempty"` | ||||
| 	// -- Following fields are only used for private keys --
 | ||||
| 	// RSA uses D, P and Q, while ECDSA uses only D. Fields Dp, Dq, and Qi are
 | ||||
| 	// completely optional. Therefore for RSA/ECDSA, D != nil is a contract that
 | ||||
| 	// we have a private key whereas D == nil means we have only a public key.
 | ||||
| 	D  *byteBuffer `json:"d,omitempty"` | ||||
| 	P  *byteBuffer `json:"p,omitempty"` | ||||
| 	Q  *byteBuffer `json:"q,omitempty"` | ||||
| 	Dp *byteBuffer `json:"dp,omitempty"` | ||||
| 	Dq *byteBuffer `json:"dq,omitempty"` | ||||
| 	Qi *byteBuffer `json:"qi,omitempty"` | ||||
| 	// Certificates
 | ||||
| 	X5c       []string `json:"x5c,omitempty"` | ||||
| 	X5u       *url.URL `json:"x5u,omitempty"` | ||||
| 	X5tSHA1   string   `json:"x5t,omitempty"` | ||||
| 	X5tSHA256 string   `json:"x5t#S256,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // JSONWebKey represents a public or private key in JWK format.
 | ||||
| type JSONWebKey struct { | ||||
| 	// Cryptographic key, can be a symmetric or asymmetric key.
 | ||||
| 	Key interface{} | ||||
| 	// Key identifier, parsed from `kid` header.
 | ||||
| 	KeyID string | ||||
| 	// Key algorithm, parsed from `alg` header.
 | ||||
| 	Algorithm string | ||||
| 	// Key use, parsed from `use` header.
 | ||||
| 	Use string | ||||
| 
 | ||||
| 	// X.509 certificate chain, parsed from `x5c` header.
 | ||||
| 	Certificates []*x509.Certificate | ||||
| 	// X.509 certificate URL, parsed from `x5u` header.
 | ||||
| 	CertificatesURL *url.URL | ||||
| 	// X.509 certificate thumbprint (SHA-1), parsed from `x5t` header.
 | ||||
| 	CertificateThumbprintSHA1 []byte | ||||
| 	// X.509 certificate thumbprint (SHA-256), parsed from `x5t#S256` header.
 | ||||
| 	CertificateThumbprintSHA256 []byte | ||||
| } | ||||
| 
 | ||||
| // MarshalJSON serializes the given key to its JSON representation.
 | ||||
| func (k JSONWebKey) MarshalJSON() ([]byte, error) { | ||||
| 	var raw *rawJSONWebKey | ||||
| 	var err error | ||||
| 
 | ||||
| 	switch key := k.Key.(type) { | ||||
| 	case ed25519.PublicKey: | ||||
| 		raw = fromEdPublicKey(key) | ||||
| 	case *ecdsa.PublicKey: | ||||
| 		raw, err = fromEcPublicKey(key) | ||||
| 	case *rsa.PublicKey: | ||||
| 		raw = fromRsaPublicKey(key) | ||||
| 	case ed25519.PrivateKey: | ||||
| 		raw, err = fromEdPrivateKey(key) | ||||
| 	case *ecdsa.PrivateKey: | ||||
| 		raw, err = fromEcPrivateKey(key) | ||||
| 	case *rsa.PrivateKey: | ||||
| 		raw, err = fromRsaPrivateKey(key) | ||||
| 	case []byte: | ||||
| 		raw, err = fromSymmetricKey(key) | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("square/go-jose: unknown key type '%s'", reflect.TypeOf(key)) | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	raw.Kid = k.KeyID | ||||
| 	raw.Alg = k.Algorithm | ||||
| 	raw.Use = k.Use | ||||
| 
 | ||||
| 	for _, cert := range k.Certificates { | ||||
| 		raw.X5c = append(raw.X5c, base64.StdEncoding.EncodeToString(cert.Raw)) | ||||
| 	} | ||||
| 
 | ||||
| 	x5tSHA1Len := len(k.CertificateThumbprintSHA1) | ||||
| 	x5tSHA256Len := len(k.CertificateThumbprintSHA256) | ||||
| 	if x5tSHA1Len > 0 { | ||||
| 		if x5tSHA1Len != sha1.Size { | ||||
| 			return nil, fmt.Errorf("square/go-jose: invalid SHA-1 thumbprint (must be %d bytes, not %d)", sha1.Size, x5tSHA1Len) | ||||
| 		} | ||||
| 		raw.X5tSHA1 = base64.RawURLEncoding.EncodeToString(k.CertificateThumbprintSHA1) | ||||
| 	} | ||||
| 	if x5tSHA256Len > 0 { | ||||
| 		if x5tSHA256Len != sha256.Size { | ||||
| 			return nil, fmt.Errorf("square/go-jose: invalid SHA-256 thumbprint (must be %d bytes, not %d)", sha256.Size, x5tSHA256Len) | ||||
| 		} | ||||
| 		raw.X5tSHA256 = base64.RawURLEncoding.EncodeToString(k.CertificateThumbprintSHA256) | ||||
| 	} | ||||
| 
 | ||||
| 	// If cert chain is attached (as opposed to being behind a URL), check the
 | ||||
| 	// keys thumbprints to make sure they match what is expected. This is to
 | ||||
| 	// ensure we don't accidentally produce a JWK with semantically inconsistent
 | ||||
| 	// data in the headers.
 | ||||
| 	if len(k.Certificates) > 0 { | ||||
| 		expectedSHA1 := sha1.Sum(k.Certificates[0].Raw) | ||||
| 		expectedSHA256 := sha256.Sum256(k.Certificates[0].Raw) | ||||
| 
 | ||||
| 		if len(k.CertificateThumbprintSHA1) > 0 && !bytes.Equal(k.CertificateThumbprintSHA1, expectedSHA1[:]) { | ||||
| 			return nil, errors.New("square/go-jose: invalid SHA-1 thumbprint, does not match cert chain") | ||||
| 		} | ||||
| 		if len(k.CertificateThumbprintSHA256) > 0 && !bytes.Equal(k.CertificateThumbprintSHA256, expectedSHA256[:]) { | ||||
| 			return nil, errors.New("square/go-jose: invalid or SHA-256 thumbprint, does not match cert chain") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	raw.X5u = k.CertificatesURL | ||||
| 
 | ||||
| 	return json.Marshal(raw) | ||||
| } | ||||
| 
 | ||||
| // UnmarshalJSON reads a key from its JSON representation.
 | ||||
| func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { | ||||
| 	var raw rawJSONWebKey | ||||
| 	err = json.Unmarshal(data, &raw) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	certs, err := parseCertificateChain(raw.X5c) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("square/go-jose: failed to unmarshal x5c field: %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	var key interface{} | ||||
| 	var certPub interface{} | ||||
| 	var keyPub interface{} | ||||
| 
 | ||||
| 	if len(certs) > 0 { | ||||
| 		// We need to check that leaf public key matches the key embedded in this
 | ||||
| 		// JWK, as required by the standard (see RFC 7517, Section 4.7). Otherwise
 | ||||
| 		// the JWK parsed could be semantically invalid. Technically, should also
 | ||||
| 		// check key usage fields and other extensions on the cert here, but the
 | ||||
| 		// standard doesn't exactly explain how they're supposed to map from the
 | ||||
| 		// JWK representation to the X.509 extensions.
 | ||||
| 		certPub = certs[0].PublicKey | ||||
| 	} | ||||
| 
 | ||||
| 	switch raw.Kty { | ||||
| 	case "EC": | ||||
| 		if raw.D != nil { | ||||
| 			key, err = raw.ecPrivateKey() | ||||
| 			if err == nil { | ||||
| 				keyPub = key.(*ecdsa.PrivateKey).Public() | ||||
| 			} | ||||
| 		} else { | ||||
| 			key, err = raw.ecPublicKey() | ||||
| 			keyPub = key | ||||
| 		} | ||||
| 	case "RSA": | ||||
| 		if raw.D != nil { | ||||
| 			key, err = raw.rsaPrivateKey() | ||||
| 			if err == nil { | ||||
| 				keyPub = key.(*rsa.PrivateKey).Public() | ||||
| 			} | ||||
| 		} else { | ||||
| 			key, err = raw.rsaPublicKey() | ||||
| 			keyPub = key | ||||
| 		} | ||||
| 	case "oct": | ||||
| 		if certPub != nil { | ||||
| 			return errors.New("square/go-jose: invalid JWK, found 'oct' (symmetric) key with cert chain") | ||||
| 		} | ||||
| 		key, err = raw.symmetricKey() | ||||
| 	case "OKP": | ||||
| 		if raw.Crv == "Ed25519" && raw.X != nil { | ||||
| 			if raw.D != nil { | ||||
| 				key, err = raw.edPrivateKey() | ||||
| 				if err == nil { | ||||
| 					keyPub = key.(ed25519.PrivateKey).Public() | ||||
| 				} | ||||
| 			} else { | ||||
| 				key, err = raw.edPublicKey() | ||||
| 				keyPub = key | ||||
| 			} | ||||
| 		} else { | ||||
| 			err = fmt.Errorf("square/go-jose: unknown curve %s'", raw.Crv) | ||||
| 		} | ||||
| 	default: | ||||
| 		err = fmt.Errorf("square/go-jose: unknown json web key type '%s'", raw.Kty) | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if certPub != nil && keyPub != nil { | ||||
| 		if !reflect.DeepEqual(certPub, keyPub) { | ||||
| 			return errors.New("square/go-jose: invalid JWK, public keys in key and x5c fields do not match") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	*k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use, Certificates: certs} | ||||
| 
 | ||||
| 	k.CertificatesURL = raw.X5u | ||||
| 
 | ||||
| 	// x5t parameters are base64url-encoded SHA thumbprints
 | ||||
| 	// See RFC 7517, Section 4.8, https://tools.ietf.org/html/rfc7517#section-4.8
 | ||||
| 	x5tSHA1bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA1) | ||||
| 	if err != nil { | ||||
| 		return errors.New("square/go-jose: invalid JWK, x5t header has invalid encoding") | ||||
| 	} | ||||
| 
 | ||||
| 	// RFC 7517, Section 4.8 is ambiguous as to whether the digest output should be byte or hex,
 | ||||
| 	// for this reason, after base64 decoding, if the size is sha1.Size it's likely that the value is a byte encoded
 | ||||
| 	// checksum so we skip this. Otherwise if the checksum was hex encoded we expect a 40 byte sized array so we'll
 | ||||
| 	// try to hex decode it. When Marshalling this value we'll always use a base64 encoded version of byte format checksum.
 | ||||
| 	if len(x5tSHA1bytes) == 2*sha1.Size { | ||||
| 		hx, err := hex.DecodeString(string(x5tSHA1bytes)) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("square/go-jose: invalid JWK, unable to hex decode x5t: %v", err) | ||||
| 
 | ||||
| 		} | ||||
| 		x5tSHA1bytes = hx | ||||
| 	} | ||||
| 
 | ||||
| 	k.CertificateThumbprintSHA1 = x5tSHA1bytes | ||||
| 
 | ||||
| 	x5tSHA256bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA256) | ||||
| 	if err != nil { | ||||
| 		return errors.New("square/go-jose: invalid JWK, x5t#S256 header has invalid encoding") | ||||
| 	} | ||||
| 
 | ||||
| 	if len(x5tSHA256bytes) == 2*sha256.Size { | ||||
| 		hx256, err := hex.DecodeString(string(x5tSHA256bytes)) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("square/go-jose: invalid JWK, unable to hex decode x5t#S256: %v", err) | ||||
| 		} | ||||
| 		x5tSHA256bytes = hx256 | ||||
| 	} | ||||
| 
 | ||||
| 	k.CertificateThumbprintSHA256 = x5tSHA256bytes | ||||
| 
 | ||||
| 	x5tSHA1Len := len(k.CertificateThumbprintSHA1) | ||||
| 	x5tSHA256Len := len(k.CertificateThumbprintSHA256) | ||||
| 	if x5tSHA1Len > 0 && x5tSHA1Len != sha1.Size { | ||||
| 		return errors.New("square/go-jose: invalid JWK, x5t header is of incorrect size") | ||||
| 	} | ||||
| 	if x5tSHA256Len > 0 && x5tSHA256Len != sha256.Size { | ||||
| 		return errors.New("square/go-jose: invalid JWK, x5t#S256 header is of incorrect size") | ||||
| 	} | ||||
| 
 | ||||
| 	// If certificate chain *and* thumbprints are set, verify correctness.
 | ||||
| 	if len(k.Certificates) > 0 { | ||||
| 		leaf := k.Certificates[0] | ||||
| 		sha1sum := sha1.Sum(leaf.Raw) | ||||
| 		sha256sum := sha256.Sum256(leaf.Raw) | ||||
| 
 | ||||
| 		if len(k.CertificateThumbprintSHA1) > 0 && !bytes.Equal(sha1sum[:], k.CertificateThumbprintSHA1) { | ||||
| 			return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t value") | ||||
| 		} | ||||
| 
 | ||||
| 		if len(k.CertificateThumbprintSHA256) > 0 && !bytes.Equal(sha256sum[:], k.CertificateThumbprintSHA256) { | ||||
| 			return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t#S256 value") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // JSONWebKeySet represents a JWK Set object.
 | ||||
| type JSONWebKeySet struct { | ||||
| 	Keys []JSONWebKey `json:"keys"` | ||||
| } | ||||
| 
 | ||||
| // Key convenience method returns keys by key ID. Specification states
 | ||||
| // that a JWK Set "SHOULD" use distinct key IDs, but allows for some
 | ||||
| // cases where they are not distinct. Hence method returns a slice
 | ||||
| // of JSONWebKeys.
 | ||||
| func (s *JSONWebKeySet) Key(kid string) []JSONWebKey { | ||||
| 	var keys []JSONWebKey | ||||
| 	for _, key := range s.Keys { | ||||
| 		if key.KeyID == kid { | ||||
| 			keys = append(keys, key) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return keys | ||||
| } | ||||
| 
 | ||||
| const rsaThumbprintTemplate = `{"e":"%s","kty":"RSA","n":"%s"}` | ||||
| const ecThumbprintTemplate = `{"crv":"%s","kty":"EC","x":"%s","y":"%s"}` | ||||
| const edThumbprintTemplate = `{"crv":"%s","kty":"OKP","x":"%s"}` | ||||
| 
 | ||||
| func ecThumbprintInput(curve elliptic.Curve, x, y *big.Int) (string, error) { | ||||
| 	coordLength := curveSize(curve) | ||||
| 	crv, err := curveName(curve) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	if len(x.Bytes()) > coordLength || len(y.Bytes()) > coordLength { | ||||
| 		return "", errors.New("square/go-jose: invalid elliptic key (too large)") | ||||
| 	} | ||||
| 
 | ||||
| 	return fmt.Sprintf(ecThumbprintTemplate, crv, | ||||
| 		newFixedSizeBuffer(x.Bytes(), coordLength).base64(), | ||||
| 		newFixedSizeBuffer(y.Bytes(), coordLength).base64()), nil | ||||
| } | ||||
| 
 | ||||
| func rsaThumbprintInput(n *big.Int, e int) (string, error) { | ||||
| 	return fmt.Sprintf(rsaThumbprintTemplate, | ||||
| 		newBufferFromInt(uint64(e)).base64(), | ||||
| 		newBuffer(n.Bytes()).base64()), nil | ||||
| } | ||||
| 
 | ||||
| func edThumbprintInput(ed ed25519.PublicKey) (string, error) { | ||||
| 	crv := "Ed25519" | ||||
| 	if len(ed) > 32 { | ||||
| 		return "", errors.New("square/go-jose: invalid elliptic key (too large)") | ||||
| 	} | ||||
| 	return fmt.Sprintf(edThumbprintTemplate, crv, | ||||
| 		newFixedSizeBuffer(ed, 32).base64()), nil | ||||
| } | ||||
| 
 | ||||
| // Thumbprint computes the JWK Thumbprint of a key using the
 | ||||
| // indicated hash algorithm.
 | ||||
| func (k *JSONWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) { | ||||
| 	var input string | ||||
| 	var err error | ||||
| 	switch key := k.Key.(type) { | ||||
| 	case ed25519.PublicKey: | ||||
| 		input, err = edThumbprintInput(key) | ||||
| 	case *ecdsa.PublicKey: | ||||
| 		input, err = ecThumbprintInput(key.Curve, key.X, key.Y) | ||||
| 	case *ecdsa.PrivateKey: | ||||
| 		input, err = ecThumbprintInput(key.Curve, key.X, key.Y) | ||||
| 	case *rsa.PublicKey: | ||||
| 		input, err = rsaThumbprintInput(key.N, key.E) | ||||
| 	case *rsa.PrivateKey: | ||||
| 		input, err = rsaThumbprintInput(key.N, key.E) | ||||
| 	case ed25519.PrivateKey: | ||||
| 		input, err = edThumbprintInput(ed25519.PublicKey(key[32:])) | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("square/go-jose: unknown key type '%s'", reflect.TypeOf(key)) | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	h := hash.New() | ||||
| 	h.Write([]byte(input)) | ||||
| 	return h.Sum(nil), nil | ||||
| } | ||||
| 
 | ||||
| // IsPublic returns true if the JWK represents a public key (not symmetric, not private).
 | ||||
| func (k *JSONWebKey) IsPublic() bool { | ||||
| 	switch k.Key.(type) { | ||||
| 	case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey: | ||||
| 		return true | ||||
| 	default: | ||||
| 		return false | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Public creates JSONWebKey with corresponding public key if JWK represents asymmetric private key.
 | ||||
| func (k *JSONWebKey) Public() JSONWebKey { | ||||
| 	if k.IsPublic() { | ||||
| 		return *k | ||||
| 	} | ||||
| 	ret := *k | ||||
| 	switch key := k.Key.(type) { | ||||
| 	case *ecdsa.PrivateKey: | ||||
| 		ret.Key = key.Public() | ||||
| 	case *rsa.PrivateKey: | ||||
| 		ret.Key = key.Public() | ||||
| 	case ed25519.PrivateKey: | ||||
| 		ret.Key = key.Public() | ||||
| 	default: | ||||
| 		return JSONWebKey{} // returning invalid key
 | ||||
| 	} | ||||
| 	return ret | ||||
| } | ||||
| 
 | ||||
| // Valid checks that the key contains the expected parameters.
 | ||||
| func (k *JSONWebKey) Valid() bool { | ||||
| 	if k.Key == nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	switch key := k.Key.(type) { | ||||
| 	case *ecdsa.PublicKey: | ||||
| 		if key.Curve == nil || key.X == nil || key.Y == nil { | ||||
| 			return false | ||||
| 		} | ||||
| 	case *ecdsa.PrivateKey: | ||||
| 		if key.Curve == nil || key.X == nil || key.Y == nil || key.D == nil { | ||||
| 			return false | ||||
| 		} | ||||
| 	case *rsa.PublicKey: | ||||
| 		if key.N == nil || key.E == 0 { | ||||
| 			return false | ||||
| 		} | ||||
| 	case *rsa.PrivateKey: | ||||
| 		if key.N == nil || key.E == 0 || key.D == nil || len(key.Primes) < 2 { | ||||
| 			return false | ||||
| 		} | ||||
| 	case ed25519.PublicKey: | ||||
| 		if len(key) != 32 { | ||||
| 			return false | ||||
| 		} | ||||
| 	case ed25519.PrivateKey: | ||||
| 		if len(key) != 64 { | ||||
| 			return false | ||||
| 		} | ||||
| 	default: | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (key rawJSONWebKey) rsaPublicKey() (*rsa.PublicKey, error) { | ||||
| 	if key.N == nil || key.E == nil { | ||||
| 		return nil, fmt.Errorf("square/go-jose: invalid RSA key, missing n/e values") | ||||
| 	} | ||||
| 
 | ||||
| 	return &rsa.PublicKey{ | ||||
| 		N: key.N.bigInt(), | ||||
| 		E: key.E.toInt(), | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func fromEdPublicKey(pub ed25519.PublicKey) *rawJSONWebKey { | ||||
| 	return &rawJSONWebKey{ | ||||
| 		Kty: "OKP", | ||||
| 		Crv: "Ed25519", | ||||
| 		X:   newBuffer(pub), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func fromRsaPublicKey(pub *rsa.PublicKey) *rawJSONWebKey { | ||||
| 	return &rawJSONWebKey{ | ||||
| 		Kty: "RSA", | ||||
| 		N:   newBuffer(pub.N.Bytes()), | ||||
| 		E:   newBufferFromInt(uint64(pub.E)), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (key rawJSONWebKey) ecPublicKey() (*ecdsa.PublicKey, error) { | ||||
| 	var curve elliptic.Curve | ||||
| 	switch key.Crv { | ||||
| 	case "P-256": | ||||
| 		curve = elliptic.P256() | ||||
| 	case "P-384": | ||||
| 		curve = elliptic.P384() | ||||
| 	case "P-521": | ||||
| 		curve = elliptic.P521() | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("square/go-jose: unsupported elliptic curve '%s'", key.Crv) | ||||
| 	} | ||||
| 
 | ||||
| 	if key.X == nil || key.Y == nil { | ||||
| 		return nil, errors.New("square/go-jose: invalid EC key, missing x/y values") | ||||
| 	} | ||||
| 
 | ||||
| 	// The length of this octet string MUST be the full size of a coordinate for
 | ||||
| 	// the curve specified in the "crv" parameter.
 | ||||
| 	// https://tools.ietf.org/html/rfc7518#section-6.2.1.2
 | ||||
| 	if curveSize(curve) != len(key.X.data) { | ||||
| 		return nil, fmt.Errorf("square/go-jose: invalid EC public key, wrong length for x") | ||||
| 	} | ||||
| 
 | ||||
| 	if curveSize(curve) != len(key.Y.data) { | ||||
| 		return nil, fmt.Errorf("square/go-jose: invalid EC public key, wrong length for y") | ||||
| 	} | ||||
| 
 | ||||
| 	x := key.X.bigInt() | ||||
| 	y := key.Y.bigInt() | ||||
| 
 | ||||
| 	if !curve.IsOnCurve(x, y) { | ||||
| 		return nil, errors.New("square/go-jose: invalid EC key, X/Y are not on declared curve") | ||||
| 	} | ||||
| 
 | ||||
| 	return &ecdsa.PublicKey{ | ||||
| 		Curve: curve, | ||||
| 		X:     x, | ||||
| 		Y:     y, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func fromEcPublicKey(pub *ecdsa.PublicKey) (*rawJSONWebKey, error) { | ||||
| 	if pub == nil || pub.X == nil || pub.Y == nil { | ||||
| 		return nil, fmt.Errorf("square/go-jose: invalid EC key (nil, or X/Y missing)") | ||||
| 	} | ||||
| 
 | ||||
| 	name, err := curveName(pub.Curve) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	size := curveSize(pub.Curve) | ||||
| 
 | ||||
| 	xBytes := pub.X.Bytes() | ||||
| 	yBytes := pub.Y.Bytes() | ||||
| 
 | ||||
| 	if len(xBytes) > size || len(yBytes) > size { | ||||
| 		return nil, fmt.Errorf("square/go-jose: invalid EC key (X/Y too large)") | ||||
| 	} | ||||
| 
 | ||||
| 	key := &rawJSONWebKey{ | ||||
| 		Kty: "EC", | ||||
| 		Crv: name, | ||||
| 		X:   newFixedSizeBuffer(xBytes, size), | ||||
| 		Y:   newFixedSizeBuffer(yBytes, size), | ||||
| 	} | ||||
| 
 | ||||
| 	return key, nil | ||||
| } | ||||
| 
 | ||||
| func (key rawJSONWebKey) edPrivateKey() (ed25519.PrivateKey, error) { | ||||
| 	var missing []string | ||||
| 	switch { | ||||
| 	case key.D == nil: | ||||
| 		missing = append(missing, "D") | ||||
| 	case key.X == nil: | ||||
| 		missing = append(missing, "X") | ||||
| 	} | ||||
| 
 | ||||
| 	if len(missing) > 0 { | ||||
| 		return nil, fmt.Errorf("square/go-jose: invalid Ed25519 private key, missing %s value(s)", strings.Join(missing, ", ")) | ||||
| 	} | ||||
| 
 | ||||
| 	privateKey := make([]byte, ed25519.PrivateKeySize) | ||||
| 	copy(privateKey[0:32], key.D.bytes()) | ||||
| 	copy(privateKey[32:], key.X.bytes()) | ||||
| 	rv := ed25519.PrivateKey(privateKey) | ||||
| 	return rv, nil | ||||
| } | ||||
| 
 | ||||
| func (key rawJSONWebKey) edPublicKey() (ed25519.PublicKey, error) { | ||||
| 	if key.X == nil { | ||||
| 		return nil, fmt.Errorf("square/go-jose: invalid Ed key, missing x value") | ||||
| 	} | ||||
| 	publicKey := make([]byte, ed25519.PublicKeySize) | ||||
| 	copy(publicKey[0:32], key.X.bytes()) | ||||
| 	rv := ed25519.PublicKey(publicKey) | ||||
| 	return rv, nil | ||||
| } | ||||
| 
 | ||||
| func (key rawJSONWebKey) rsaPrivateKey() (*rsa.PrivateKey, error) { | ||||
| 	var missing []string | ||||
| 	switch { | ||||
| 	case key.N == nil: | ||||
| 		missing = append(missing, "N") | ||||
| 	case key.E == nil: | ||||
| 		missing = append(missing, "E") | ||||
| 	case key.D == nil: | ||||
| 		missing = append(missing, "D") | ||||
| 	case key.P == nil: | ||||
| 		missing = append(missing, "P") | ||||
| 	case key.Q == nil: | ||||
| 		missing = append(missing, "Q") | ||||
| 	} | ||||
| 
 | ||||
| 	if len(missing) > 0 { | ||||
| 		return nil, fmt.Errorf("square/go-jose: invalid RSA private key, missing %s value(s)", strings.Join(missing, ", ")) | ||||
| 	} | ||||
| 
 | ||||
| 	rv := &rsa.PrivateKey{ | ||||
| 		PublicKey: rsa.PublicKey{ | ||||
| 			N: key.N.bigInt(), | ||||
| 			E: key.E.toInt(), | ||||
| 		}, | ||||
| 		D: key.D.bigInt(), | ||||
| 		Primes: []*big.Int{ | ||||
| 			key.P.bigInt(), | ||||
| 			key.Q.bigInt(), | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	if key.Dp != nil { | ||||
| 		rv.Precomputed.Dp = key.Dp.bigInt() | ||||
| 	} | ||||
| 	if key.Dq != nil { | ||||
| 		rv.Precomputed.Dq = key.Dq.bigInt() | ||||
| 	} | ||||
| 	if key.Qi != nil { | ||||
| 		rv.Precomputed.Qinv = key.Qi.bigInt() | ||||
| 	} | ||||
| 
 | ||||
| 	err := rv.Validate() | ||||
| 	return rv, err | ||||
| } | ||||
| 
 | ||||
| func fromEdPrivateKey(ed ed25519.PrivateKey) (*rawJSONWebKey, error) { | ||||
| 	raw := fromEdPublicKey(ed25519.PublicKey(ed[32:])) | ||||
| 
 | ||||
| 	raw.D = newBuffer(ed[0:32]) | ||||
| 	return raw, nil | ||||
| } | ||||
| 
 | ||||
| func fromRsaPrivateKey(rsa *rsa.PrivateKey) (*rawJSONWebKey, error) { | ||||
| 	if len(rsa.Primes) != 2 { | ||||
| 		return nil, ErrUnsupportedKeyType | ||||
| 	} | ||||
| 
 | ||||
| 	raw := fromRsaPublicKey(&rsa.PublicKey) | ||||
| 
 | ||||
| 	raw.D = newBuffer(rsa.D.Bytes()) | ||||
| 	raw.P = newBuffer(rsa.Primes[0].Bytes()) | ||||
| 	raw.Q = newBuffer(rsa.Primes[1].Bytes()) | ||||
| 
 | ||||
| 	if rsa.Precomputed.Dp != nil { | ||||
| 		raw.Dp = newBuffer(rsa.Precomputed.Dp.Bytes()) | ||||
| 	} | ||||
| 	if rsa.Precomputed.Dq != nil { | ||||
| 		raw.Dq = newBuffer(rsa.Precomputed.Dq.Bytes()) | ||||
| 	} | ||||
| 	if rsa.Precomputed.Qinv != nil { | ||||
| 		raw.Qi = newBuffer(rsa.Precomputed.Qinv.Bytes()) | ||||
| 	} | ||||
| 
 | ||||
| 	return raw, nil | ||||
| } | ||||
| 
 | ||||
| func (key rawJSONWebKey) ecPrivateKey() (*ecdsa.PrivateKey, error) { | ||||
| 	var curve elliptic.Curve | ||||
| 	switch key.Crv { | ||||
| 	case "P-256": | ||||
| 		curve = elliptic.P256() | ||||
| 	case "P-384": | ||||
| 		curve = elliptic.P384() | ||||
| 	case "P-521": | ||||
| 		curve = elliptic.P521() | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("square/go-jose: unsupported elliptic curve '%s'", key.Crv) | ||||
| 	} | ||||
| 
 | ||||
| 	if key.X == nil || key.Y == nil || key.D == nil { | ||||
| 		return nil, fmt.Errorf("square/go-jose: invalid EC private key, missing x/y/d values") | ||||
| 	} | ||||
| 
 | ||||
| 	// The length of this octet string MUST be the full size of a coordinate for
 | ||||
| 	// the curve specified in the "crv" parameter.
 | ||||
| 	// https://tools.ietf.org/html/rfc7518#section-6.2.1.2
 | ||||
| 	if curveSize(curve) != len(key.X.data) { | ||||
| 		return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for x") | ||||
| 	} | ||||
| 
 | ||||
| 	if curveSize(curve) != len(key.Y.data) { | ||||
| 		return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for y") | ||||
| 	} | ||||
| 
 | ||||
| 	// https://tools.ietf.org/html/rfc7518#section-6.2.2.1
 | ||||
| 	if dSize(curve) != len(key.D.data) { | ||||
| 		return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for d") | ||||
| 	} | ||||
| 
 | ||||
| 	x := key.X.bigInt() | ||||
| 	y := key.Y.bigInt() | ||||
| 
 | ||||
| 	if !curve.IsOnCurve(x, y) { | ||||
| 		return nil, errors.New("square/go-jose: invalid EC key, X/Y are not on declared curve") | ||||
| 	} | ||||
| 
 | ||||
| 	return &ecdsa.PrivateKey{ | ||||
| 		PublicKey: ecdsa.PublicKey{ | ||||
| 			Curve: curve, | ||||
| 			X:     x, | ||||
| 			Y:     y, | ||||
| 		}, | ||||
| 		D: key.D.bigInt(), | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func fromEcPrivateKey(ec *ecdsa.PrivateKey) (*rawJSONWebKey, error) { | ||||
| 	raw, err := fromEcPublicKey(&ec.PublicKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if ec.D == nil { | ||||
| 		return nil, fmt.Errorf("square/go-jose: invalid EC private key") | ||||
| 	} | ||||
| 
 | ||||
| 	raw.D = newFixedSizeBuffer(ec.D.Bytes(), dSize(ec.PublicKey.Curve)) | ||||
| 
 | ||||
| 	return raw, nil | ||||
| } | ||||
| 
 | ||||
| // dSize returns the size in octets for the "d" member of an elliptic curve
 | ||||
| // private key.
 | ||||
| // The length of this octet string MUST be ceiling(log-base-2(n)/8)
 | ||||
| // octets (where n is the order of the curve).
 | ||||
| // https://tools.ietf.org/html/rfc7518#section-6.2.2.1
 | ||||
| func dSize(curve elliptic.Curve) int { | ||||
| 	order := curve.Params().P | ||||
| 	bitLen := order.BitLen() | ||||
| 	size := bitLen / 8 | ||||
| 	if bitLen%8 != 0 { | ||||
| 		size = size + 1 | ||||
| 	} | ||||
| 	return size | ||||
| } | ||||
| 
 | ||||
| func fromSymmetricKey(key []byte) (*rawJSONWebKey, error) { | ||||
| 	return &rawJSONWebKey{ | ||||
| 		Kty: "oct", | ||||
| 		K:   newBuffer(key), | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (key rawJSONWebKey) symmetricKey() ([]byte, error) { | ||||
| 	if key.K == nil { | ||||
| 		return nil, fmt.Errorf("square/go-jose: invalid OCT (symmetric) key, missing k value") | ||||
| 	} | ||||
| 	return key.K.bytes(), nil | ||||
| } | ||||
|  | @ -1,366 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2014 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package jose | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/base64" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"gopkg.in/square/go-jose.v2/json" | ||||
| ) | ||||
| 
 | ||||
| // rawJSONWebSignature represents a raw JWS JSON object. Used for parsing/serializing.
 | ||||
| type rawJSONWebSignature struct { | ||||
| 	Payload    *byteBuffer        `json:"payload,omitempty"` | ||||
| 	Signatures []rawSignatureInfo `json:"signatures,omitempty"` | ||||
| 	Protected  *byteBuffer        `json:"protected,omitempty"` | ||||
| 	Header     *rawHeader         `json:"header,omitempty"` | ||||
| 	Signature  *byteBuffer        `json:"signature,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // rawSignatureInfo represents a single JWS signature over the JWS payload and protected header.
 | ||||
| type rawSignatureInfo struct { | ||||
| 	Protected *byteBuffer `json:"protected,omitempty"` | ||||
| 	Header    *rawHeader  `json:"header,omitempty"` | ||||
| 	Signature *byteBuffer `json:"signature,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // JSONWebSignature represents a signed JWS object after parsing.
 | ||||
| type JSONWebSignature struct { | ||||
| 	payload []byte | ||||
| 	// Signatures attached to this object (may be more than one for multi-sig).
 | ||||
| 	// Be careful about accessing these directly, prefer to use Verify() or
 | ||||
| 	// VerifyMulti() to ensure that the data you're getting is verified.
 | ||||
| 	Signatures []Signature | ||||
| } | ||||
| 
 | ||||
| // Signature represents a single signature over the JWS payload and protected header.
 | ||||
| type Signature struct { | ||||
| 	// Merged header fields. Contains both protected and unprotected header
 | ||||
| 	// values. Prefer using Protected and Unprotected fields instead of this.
 | ||||
| 	// Values in this header may or may not have been signed and in general
 | ||||
| 	// should not be trusted.
 | ||||
| 	Header Header | ||||
| 
 | ||||
| 	// Protected header. Values in this header were signed and
 | ||||
| 	// will be verified as part of the signature verification process.
 | ||||
| 	Protected Header | ||||
| 
 | ||||
| 	// Unprotected header. Values in this header were not signed
 | ||||
| 	// and in general should not be trusted.
 | ||||
| 	Unprotected Header | ||||
| 
 | ||||
| 	// The actual signature value
 | ||||
| 	Signature []byte | ||||
| 
 | ||||
| 	protected *rawHeader | ||||
| 	header    *rawHeader | ||||
| 	original  *rawSignatureInfo | ||||
| } | ||||
| 
 | ||||
| // ParseSigned parses a signed message in compact or full serialization format.
 | ||||
| func ParseSigned(signature string) (*JSONWebSignature, error) { | ||||
| 	signature = stripWhitespace(signature) | ||||
| 	if strings.HasPrefix(signature, "{") { | ||||
| 		return parseSignedFull(signature) | ||||
| 	} | ||||
| 
 | ||||
| 	return parseSignedCompact(signature, nil) | ||||
| } | ||||
| 
 | ||||
| // ParseDetached parses a signed message in compact serialization format with detached payload.
 | ||||
| func ParseDetached(signature string, payload []byte) (*JSONWebSignature, error) { | ||||
| 	if payload == nil { | ||||
| 		return nil, errors.New("square/go-jose: nil payload") | ||||
| 	} | ||||
| 	return parseSignedCompact(stripWhitespace(signature), payload) | ||||
| } | ||||
| 
 | ||||
| // Get a header value
 | ||||
| func (sig Signature) mergedHeaders() rawHeader { | ||||
| 	out := rawHeader{} | ||||
| 	out.merge(sig.protected) | ||||
| 	out.merge(sig.header) | ||||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // Compute data to be signed
 | ||||
| func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) ([]byte, error) { | ||||
| 	var authData bytes.Buffer | ||||
| 
 | ||||
| 	protectedHeader := new(rawHeader) | ||||
| 
 | ||||
| 	if signature.original != nil && signature.original.Protected != nil { | ||||
| 		if err := json.Unmarshal(signature.original.Protected.bytes(), protectedHeader); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		authData.WriteString(signature.original.Protected.base64()) | ||||
| 	} else if signature.protected != nil { | ||||
| 		protectedHeader = signature.protected | ||||
| 		authData.WriteString(base64.RawURLEncoding.EncodeToString(mustSerializeJSON(protectedHeader))) | ||||
| 	} | ||||
| 
 | ||||
| 	needsBase64 := true | ||||
| 
 | ||||
| 	if protectedHeader != nil { | ||||
| 		var err error | ||||
| 		if needsBase64, err = protectedHeader.getB64(); err != nil { | ||||
| 			needsBase64 = true | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	authData.WriteByte('.') | ||||
| 
 | ||||
| 	if needsBase64 { | ||||
| 		authData.WriteString(base64.RawURLEncoding.EncodeToString(payload)) | ||||
| 	} else { | ||||
| 		authData.Write(payload) | ||||
| 	} | ||||
| 
 | ||||
| 	return authData.Bytes(), nil | ||||
| } | ||||
| 
 | ||||
| // parseSignedFull parses a message in full format.
 | ||||
| func parseSignedFull(input string) (*JSONWebSignature, error) { | ||||
| 	var parsed rawJSONWebSignature | ||||
| 	err := json.Unmarshal([]byte(input), &parsed) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return parsed.sanitized() | ||||
| } | ||||
| 
 | ||||
| // sanitized produces a cleaned-up JWS object from the raw JSON.
 | ||||
| func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) { | ||||
| 	if parsed.Payload == nil { | ||||
| 		return nil, fmt.Errorf("square/go-jose: missing payload in JWS message") | ||||
| 	} | ||||
| 
 | ||||
| 	obj := &JSONWebSignature{ | ||||
| 		payload:    parsed.Payload.bytes(), | ||||
| 		Signatures: make([]Signature, len(parsed.Signatures)), | ||||
| 	} | ||||
| 
 | ||||
| 	if len(parsed.Signatures) == 0 { | ||||
| 		// No signatures array, must be flattened serialization
 | ||||
| 		signature := Signature{} | ||||
| 		if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 { | ||||
| 			signature.protected = &rawHeader{} | ||||
| 			err := json.Unmarshal(parsed.Protected.bytes(), signature.protected) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// Check that there is not a nonce in the unprotected header
 | ||||
| 		if parsed.Header != nil && parsed.Header.getNonce() != "" { | ||||
| 			return nil, ErrUnprotectedNonce | ||||
| 		} | ||||
| 
 | ||||
| 		signature.header = parsed.Header | ||||
| 		signature.Signature = parsed.Signature.bytes() | ||||
| 		// Make a fake "original" rawSignatureInfo to store the unprocessed
 | ||||
| 		// Protected header. This is necessary because the Protected header can
 | ||||
| 		// contain arbitrary fields not registered as part of the spec. See
 | ||||
| 		// https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4
 | ||||
| 		// If we unmarshal Protected into a rawHeader with its explicit list of fields,
 | ||||
| 		// we cannot marshal losslessly. So we have to keep around the original bytes.
 | ||||
| 		// This is used in computeAuthData, which will first attempt to use
 | ||||
| 		// the original bytes of a protected header, and fall back on marshaling the
 | ||||
| 		// header struct only if those bytes are not available.
 | ||||
| 		signature.original = &rawSignatureInfo{ | ||||
| 			Protected: parsed.Protected, | ||||
| 			Header:    parsed.Header, | ||||
| 			Signature: parsed.Signature, | ||||
| 		} | ||||
| 
 | ||||
| 		var err error | ||||
| 		signature.Header, err = signature.mergedHeaders().sanitized() | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		if signature.header != nil { | ||||
| 			signature.Unprotected, err = signature.header.sanitized() | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if signature.protected != nil { | ||||
| 			signature.Protected, err = signature.protected.sanitized() | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded.
 | ||||
| 		jwk := signature.Header.JSONWebKey | ||||
| 		if jwk != nil && (!jwk.Valid() || !jwk.IsPublic()) { | ||||
| 			return nil, errors.New("square/go-jose: invalid embedded jwk, must be public key") | ||||
| 		} | ||||
| 
 | ||||
| 		obj.Signatures = append(obj.Signatures, signature) | ||||
| 	} | ||||
| 
 | ||||
| 	for i, sig := range parsed.Signatures { | ||||
| 		if sig.Protected != nil && len(sig.Protected.bytes()) > 0 { | ||||
| 			obj.Signatures[i].protected = &rawHeader{} | ||||
| 			err := json.Unmarshal(sig.Protected.bytes(), obj.Signatures[i].protected) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// Check that there is not a nonce in the unprotected header
 | ||||
| 		if sig.Header != nil && sig.Header.getNonce() != "" { | ||||
| 			return nil, ErrUnprotectedNonce | ||||
| 		} | ||||
| 
 | ||||
| 		var err error | ||||
| 		obj.Signatures[i].Header, err = obj.Signatures[i].mergedHeaders().sanitized() | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		if obj.Signatures[i].header != nil { | ||||
| 			obj.Signatures[i].Unprotected, err = obj.Signatures[i].header.sanitized() | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if obj.Signatures[i].protected != nil { | ||||
| 			obj.Signatures[i].Protected, err = obj.Signatures[i].protected.sanitized() | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		obj.Signatures[i].Signature = sig.Signature.bytes() | ||||
| 
 | ||||
| 		// As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded.
 | ||||
| 		jwk := obj.Signatures[i].Header.JSONWebKey | ||||
| 		if jwk != nil && (!jwk.Valid() || !jwk.IsPublic()) { | ||||
| 			return nil, errors.New("square/go-jose: invalid embedded jwk, must be public key") | ||||
| 		} | ||||
| 
 | ||||
| 		// Copy value of sig
 | ||||
| 		original := sig | ||||
| 
 | ||||
| 		obj.Signatures[i].header = sig.Header | ||||
| 		obj.Signatures[i].original = &original | ||||
| 	} | ||||
| 
 | ||||
| 	return obj, nil | ||||
| } | ||||
| 
 | ||||
| // parseSignedCompact parses a message in compact format.
 | ||||
| func parseSignedCompact(input string, payload []byte) (*JSONWebSignature, error) { | ||||
| 	parts := strings.Split(input, ".") | ||||
| 	if len(parts) != 3 { | ||||
| 		return nil, fmt.Errorf("square/go-jose: compact JWS format must have three parts") | ||||
| 	} | ||||
| 
 | ||||
| 	if parts[1] != "" && payload != nil { | ||||
| 		return nil, fmt.Errorf("square/go-jose: payload is not detached") | ||||
| 	} | ||||
| 
 | ||||
| 	rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if payload == nil { | ||||
| 		payload, err = base64.RawURLEncoding.DecodeString(parts[1]) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	signature, err := base64.RawURLEncoding.DecodeString(parts[2]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	raw := &rawJSONWebSignature{ | ||||
| 		Payload:   newBuffer(payload), | ||||
| 		Protected: newBuffer(rawProtected), | ||||
| 		Signature: newBuffer(signature), | ||||
| 	} | ||||
| 	return raw.sanitized() | ||||
| } | ||||
| 
 | ||||
| func (obj JSONWebSignature) compactSerialize(detached bool) (string, error) { | ||||
| 	if len(obj.Signatures) != 1 || obj.Signatures[0].header != nil || obj.Signatures[0].protected == nil { | ||||
| 		return "", ErrNotSupported | ||||
| 	} | ||||
| 
 | ||||
| 	serializedProtected := base64.RawURLEncoding.EncodeToString(mustSerializeJSON(obj.Signatures[0].protected)) | ||||
| 	payload := "" | ||||
| 	signature := base64.RawURLEncoding.EncodeToString(obj.Signatures[0].Signature) | ||||
| 
 | ||||
| 	if !detached { | ||||
| 		payload = base64.RawURLEncoding.EncodeToString(obj.payload) | ||||
| 	} | ||||
| 
 | ||||
| 	return fmt.Sprintf("%s.%s.%s", serializedProtected, payload, signature), nil | ||||
| } | ||||
| 
 | ||||
| // CompactSerialize serializes an object using the compact serialization format.
 | ||||
| func (obj JSONWebSignature) CompactSerialize() (string, error) { | ||||
| 	return obj.compactSerialize(false) | ||||
| } | ||||
| 
 | ||||
| // DetachedCompactSerialize serializes an object using the compact serialization format with detached payload.
 | ||||
| func (obj JSONWebSignature) DetachedCompactSerialize() (string, error) { | ||||
| 	return obj.compactSerialize(true) | ||||
| } | ||||
| 
 | ||||
| // FullSerialize serializes an object using the full JSON serialization format.
 | ||||
| func (obj JSONWebSignature) FullSerialize() string { | ||||
| 	raw := rawJSONWebSignature{ | ||||
| 		Payload: newBuffer(obj.payload), | ||||
| 	} | ||||
| 
 | ||||
| 	if len(obj.Signatures) == 1 { | ||||
| 		if obj.Signatures[0].protected != nil { | ||||
| 			serializedProtected := mustSerializeJSON(obj.Signatures[0].protected) | ||||
| 			raw.Protected = newBuffer(serializedProtected) | ||||
| 		} | ||||
| 		raw.Header = obj.Signatures[0].header | ||||
| 		raw.Signature = newBuffer(obj.Signatures[0].Signature) | ||||
| 	} else { | ||||
| 		raw.Signatures = make([]rawSignatureInfo, len(obj.Signatures)) | ||||
| 		for i, signature := range obj.Signatures { | ||||
| 			raw.Signatures[i] = rawSignatureInfo{ | ||||
| 				Header:    signature.header, | ||||
| 				Signature: newBuffer(signature.Signature), | ||||
| 			} | ||||
| 
 | ||||
| 			if signature.protected != nil { | ||||
| 				raw.Signatures[i].Protected = newBuffer(mustSerializeJSON(signature.protected)) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return string(mustSerializeJSON(raw)) | ||||
| } | ||||
|  | @ -1,334 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2016 Zbigniew Mandziejewicz | ||||
|  * Copyright 2016 Square, Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package jwt | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"reflect" | ||||
| 
 | ||||
| 	"gopkg.in/square/go-jose.v2/json" | ||||
| 
 | ||||
| 	"gopkg.in/square/go-jose.v2" | ||||
| ) | ||||
| 
 | ||||
| // Builder is a utility for making JSON Web Tokens. Calls can be chained, and
 | ||||
| // errors are accumulated until the final call to CompactSerialize/FullSerialize.
 | ||||
| type Builder interface { | ||||
| 	// Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
 | ||||
| 	// into single JSON object. If you are passing private claims, make sure to set
 | ||||
| 	// struct field tags to specify the name for the JSON key to be used when
 | ||||
| 	// serializing.
 | ||||
| 	Claims(i interface{}) Builder | ||||
| 	// Token builds a JSONWebToken from provided data.
 | ||||
| 	Token() (*JSONWebToken, error) | ||||
| 	// FullSerialize serializes a token using the full serialization format.
 | ||||
| 	FullSerialize() (string, error) | ||||
| 	// CompactSerialize serializes a token using the compact serialization format.
 | ||||
| 	CompactSerialize() (string, error) | ||||
| } | ||||
| 
 | ||||
| // NestedBuilder is a utility for making Signed-Then-Encrypted JSON Web Tokens.
 | ||||
| // Calls can be chained, and errors are accumulated until final call to
 | ||||
| // CompactSerialize/FullSerialize.
 | ||||
| type NestedBuilder interface { | ||||
| 	// Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
 | ||||
| 	// into single JSON object. If you are passing private claims, make sure to set
 | ||||
| 	// struct field tags to specify the name for the JSON key to be used when
 | ||||
| 	// serializing.
 | ||||
| 	Claims(i interface{}) NestedBuilder | ||||
| 	// Token builds a NestedJSONWebToken from provided data.
 | ||||
| 	Token() (*NestedJSONWebToken, error) | ||||
| 	// FullSerialize serializes a token using the full serialization format.
 | ||||
| 	FullSerialize() (string, error) | ||||
| 	// CompactSerialize serializes a token using the compact serialization format.
 | ||||
| 	CompactSerialize() (string, error) | ||||
| } | ||||
| 
 | ||||
| type builder struct { | ||||
| 	payload map[string]interface{} | ||||
| 	err     error | ||||
| } | ||||
| 
 | ||||
| type signedBuilder struct { | ||||
| 	builder | ||||
| 	sig jose.Signer | ||||
| } | ||||
| 
 | ||||
| type encryptedBuilder struct { | ||||
| 	builder | ||||
| 	enc jose.Encrypter | ||||
| } | ||||
| 
 | ||||
| type nestedBuilder struct { | ||||
| 	builder | ||||
| 	sig jose.Signer | ||||
| 	enc jose.Encrypter | ||||
| } | ||||
| 
 | ||||
| // Signed creates builder for signed tokens.
 | ||||
| func Signed(sig jose.Signer) Builder { | ||||
| 	return &signedBuilder{ | ||||
| 		sig: sig, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Encrypted creates builder for encrypted tokens.
 | ||||
| func Encrypted(enc jose.Encrypter) Builder { | ||||
| 	return &encryptedBuilder{ | ||||
| 		enc: enc, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SignedAndEncrypted creates builder for signed-then-encrypted tokens.
 | ||||
| // ErrInvalidContentType will be returned if encrypter doesn't have JWT content type.
 | ||||
| func SignedAndEncrypted(sig jose.Signer, enc jose.Encrypter) NestedBuilder { | ||||
| 	if contentType, _ := enc.Options().ExtraHeaders[jose.HeaderContentType].(jose.ContentType); contentType != "JWT" { | ||||
| 		return &nestedBuilder{ | ||||
| 			builder: builder{ | ||||
| 				err: ErrInvalidContentType, | ||||
| 			}, | ||||
| 		} | ||||
| 	} | ||||
| 	return &nestedBuilder{ | ||||
| 		sig: sig, | ||||
| 		enc: enc, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (b builder) claims(i interface{}) builder { | ||||
| 	if b.err != nil { | ||||
| 		return b | ||||
| 	} | ||||
| 
 | ||||
| 	m, ok := i.(map[string]interface{}) | ||||
| 	switch { | ||||
| 	case ok: | ||||
| 		return b.merge(m) | ||||
| 	case reflect.Indirect(reflect.ValueOf(i)).Kind() == reflect.Struct: | ||||
| 		m, err := normalize(i) | ||||
| 		if err != nil { | ||||
| 			return builder{ | ||||
| 				err: err, | ||||
| 			} | ||||
| 		} | ||||
| 		return b.merge(m) | ||||
| 	default: | ||||
| 		return builder{ | ||||
| 			err: ErrInvalidClaims, | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func normalize(i interface{}) (map[string]interface{}, error) { | ||||
| 	m := make(map[string]interface{}) | ||||
| 
 | ||||
| 	raw, err := json.Marshal(i) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	d := json.NewDecoder(bytes.NewReader(raw)) | ||||
| 	d.SetNumberType(json.UnmarshalJSONNumber) | ||||
| 
 | ||||
| 	if err := d.Decode(&m); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return m, nil | ||||
| } | ||||
| 
 | ||||
| func (b *builder) merge(m map[string]interface{}) builder { | ||||
| 	p := make(map[string]interface{}) | ||||
| 	for k, v := range b.payload { | ||||
| 		p[k] = v | ||||
| 	} | ||||
| 	for k, v := range m { | ||||
| 		p[k] = v | ||||
| 	} | ||||
| 
 | ||||
| 	return builder{ | ||||
| 		payload: p, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (b *builder) token(p func(interface{}) ([]byte, error), h []jose.Header) (*JSONWebToken, error) { | ||||
| 	return &JSONWebToken{ | ||||
| 		payload: p, | ||||
| 		Headers: h, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (b *signedBuilder) Claims(i interface{}) Builder { | ||||
| 	return &signedBuilder{ | ||||
| 		builder: b.builder.claims(i), | ||||
| 		sig:     b.sig, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (b *signedBuilder) Token() (*JSONWebToken, error) { | ||||
| 	sig, err := b.sign() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	h := make([]jose.Header, len(sig.Signatures)) | ||||
| 	for i, v := range sig.Signatures { | ||||
| 		h[i] = v.Header | ||||
| 	} | ||||
| 
 | ||||
| 	return b.builder.token(sig.Verify, h) | ||||
| } | ||||
| 
 | ||||
| func (b *signedBuilder) CompactSerialize() (string, error) { | ||||
| 	sig, err := b.sign() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	return sig.CompactSerialize() | ||||
| } | ||||
| 
 | ||||
| func (b *signedBuilder) FullSerialize() (string, error) { | ||||
| 	sig, err := b.sign() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	return sig.FullSerialize(), nil | ||||
| } | ||||
| 
 | ||||
| func (b *signedBuilder) sign() (*jose.JSONWebSignature, error) { | ||||
| 	if b.err != nil { | ||||
| 		return nil, b.err | ||||
| 	} | ||||
| 
 | ||||
| 	p, err := json.Marshal(b.payload) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return b.sig.Sign(p) | ||||
| } | ||||
| 
 | ||||
| func (b *encryptedBuilder) Claims(i interface{}) Builder { | ||||
| 	return &encryptedBuilder{ | ||||
| 		builder: b.builder.claims(i), | ||||
| 		enc:     b.enc, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (b *encryptedBuilder) CompactSerialize() (string, error) { | ||||
| 	enc, err := b.encrypt() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	return enc.CompactSerialize() | ||||
| } | ||||
| 
 | ||||
| func (b *encryptedBuilder) FullSerialize() (string, error) { | ||||
| 	enc, err := b.encrypt() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	return enc.FullSerialize(), nil | ||||
| } | ||||
| 
 | ||||
| func (b *encryptedBuilder) Token() (*JSONWebToken, error) { | ||||
| 	enc, err := b.encrypt() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return b.builder.token(enc.Decrypt, []jose.Header{enc.Header}) | ||||
| } | ||||
| 
 | ||||
| func (b *encryptedBuilder) encrypt() (*jose.JSONWebEncryption, error) { | ||||
| 	if b.err != nil { | ||||
| 		return nil, b.err | ||||
| 	} | ||||
| 
 | ||||
| 	p, err := json.Marshal(b.payload) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return b.enc.Encrypt(p) | ||||
| } | ||||
| 
 | ||||
| func (b *nestedBuilder) Claims(i interface{}) NestedBuilder { | ||||
| 	return &nestedBuilder{ | ||||
| 		builder: b.builder.claims(i), | ||||
| 		sig:     b.sig, | ||||
| 		enc:     b.enc, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (b *nestedBuilder) Token() (*NestedJSONWebToken, error) { | ||||
| 	enc, err := b.signAndEncrypt() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &NestedJSONWebToken{ | ||||
| 		enc:     enc, | ||||
| 		Headers: []jose.Header{enc.Header}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (b *nestedBuilder) CompactSerialize() (string, error) { | ||||
| 	enc, err := b.signAndEncrypt() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	return enc.CompactSerialize() | ||||
| } | ||||
| 
 | ||||
| func (b *nestedBuilder) FullSerialize() (string, error) { | ||||
| 	enc, err := b.signAndEncrypt() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	return enc.FullSerialize(), nil | ||||
| } | ||||
| 
 | ||||
| func (b *nestedBuilder) signAndEncrypt() (*jose.JSONWebEncryption, error) { | ||||
| 	if b.err != nil { | ||||
| 		return nil, b.err | ||||
| 	} | ||||
| 
 | ||||
| 	p, err := json.Marshal(b.payload) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	sig, err := b.sig.Sign(p) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	p2, err := sig.CompactSerialize() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return b.enc.Encrypt([]byte(p2)) | ||||
| } | ||||
|  | @ -1,121 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2016 Zbigniew Mandziejewicz | ||||
|  * Copyright 2016 Square, Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package jwt | ||||
| 
 | ||||
| import ( | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"gopkg.in/square/go-jose.v2/json" | ||||
| ) | ||||
| 
 | ||||
| // Claims represents public claim values (as specified in RFC 7519).
 | ||||
| type Claims struct { | ||||
| 	Issuer    string       `json:"iss,omitempty"` | ||||
| 	Subject   string       `json:"sub,omitempty"` | ||||
| 	Audience  Audience     `json:"aud,omitempty"` | ||||
| 	Expiry    *NumericDate `json:"exp,omitempty"` | ||||
| 	NotBefore *NumericDate `json:"nbf,omitempty"` | ||||
| 	IssuedAt  *NumericDate `json:"iat,omitempty"` | ||||
| 	ID        string       `json:"jti,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // NumericDate represents date and time as the number of seconds since the
 | ||||
| // epoch, ignoring leap seconds. Non-integer values can be represented
 | ||||
| // in the serialized format, but we round to the nearest second.
 | ||||
| // See RFC7519 Section 2: https://tools.ietf.org/html/rfc7519#section-2
 | ||||
| type NumericDate int64 | ||||
| 
 | ||||
| // NewNumericDate constructs NumericDate from time.Time value.
 | ||||
| func NewNumericDate(t time.Time) *NumericDate { | ||||
| 	if t.IsZero() { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// While RFC 7519 technically states that NumericDate values may be
 | ||||
| 	// non-integer values, we don't bother serializing timestamps in
 | ||||
| 	// claims with sub-second accurancy and just round to the nearest
 | ||||
| 	// second instead. Not convined sub-second accuracy is useful here.
 | ||||
| 	out := NumericDate(t.Unix()) | ||||
| 	return &out | ||||
| } | ||||
| 
 | ||||
| // MarshalJSON serializes the given NumericDate into its JSON representation.
 | ||||
| func (n NumericDate) MarshalJSON() ([]byte, error) { | ||||
| 	return []byte(strconv.FormatInt(int64(n), 10)), nil | ||||
| } | ||||
| 
 | ||||
| // UnmarshalJSON reads a date from its JSON representation.
 | ||||
| func (n *NumericDate) UnmarshalJSON(b []byte) error { | ||||
| 	s := string(b) | ||||
| 
 | ||||
| 	f, err := strconv.ParseFloat(s, 64) | ||||
| 	if err != nil { | ||||
| 		return ErrUnmarshalNumericDate | ||||
| 	} | ||||
| 
 | ||||
| 	*n = NumericDate(f) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Time returns time.Time representation of NumericDate.
 | ||||
| func (n *NumericDate) Time() time.Time { | ||||
| 	if n == nil { | ||||
| 		return time.Time{} | ||||
| 	} | ||||
| 	return time.Unix(int64(*n), 0) | ||||
| } | ||||
| 
 | ||||
| // Audience represents the recipients that the token is intended for.
 | ||||
| type Audience []string | ||||
| 
 | ||||
| // UnmarshalJSON reads an audience from its JSON representation.
 | ||||
| func (s *Audience) UnmarshalJSON(b []byte) error { | ||||
| 	var v interface{} | ||||
| 	if err := json.Unmarshal(b, &v); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	switch v := v.(type) { | ||||
| 	case string: | ||||
| 		*s = []string{v} | ||||
| 	case []interface{}: | ||||
| 		a := make([]string, len(v)) | ||||
| 		for i, e := range v { | ||||
| 			s, ok := e.(string) | ||||
| 			if !ok { | ||||
| 				return ErrUnmarshalAudience | ||||
| 			} | ||||
| 			a[i] = s | ||||
| 		} | ||||
| 		*s = a | ||||
| 	default: | ||||
| 		return ErrUnmarshalAudience | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (s Audience) Contains(v string) bool { | ||||
| 	for _, a := range s { | ||||
| 		if a == v { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | @ -1,22 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2017 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| /* | ||||
| 
 | ||||
| Package jwt provides an implementation of the JSON Web Token standard. | ||||
| 
 | ||||
| */ | ||||
| package jwt | ||||
|  | @ -1,53 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2016 Zbigniew Mandziejewicz | ||||
|  * Copyright 2016 Square, Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package jwt | ||||
| 
 | ||||
| import "errors" | ||||
| 
 | ||||
| // ErrUnmarshalAudience indicates that aud claim could not be unmarshalled.
 | ||||
| var ErrUnmarshalAudience = errors.New("square/go-jose/jwt: expected string or array value to unmarshal to Audience") | ||||
| 
 | ||||
| // ErrUnmarshalNumericDate indicates that JWT NumericDate could not be unmarshalled.
 | ||||
| var ErrUnmarshalNumericDate = errors.New("square/go-jose/jwt: expected number value to unmarshal NumericDate") | ||||
| 
 | ||||
| // ErrInvalidClaims indicates that given claims have invalid type.
 | ||||
| var ErrInvalidClaims = errors.New("square/go-jose/jwt: expected claims to be value convertible into JSON object") | ||||
| 
 | ||||
| // ErrInvalidIssuer indicates invalid iss claim.
 | ||||
| var ErrInvalidIssuer = errors.New("square/go-jose/jwt: validation failed, invalid issuer claim (iss)") | ||||
| 
 | ||||
| // ErrInvalidSubject indicates invalid sub claim.
 | ||||
| var ErrInvalidSubject = errors.New("square/go-jose/jwt: validation failed, invalid subject claim (sub)") | ||||
| 
 | ||||
| // ErrInvalidAudience indicated invalid aud claim.
 | ||||
| var ErrInvalidAudience = errors.New("square/go-jose/jwt: validation failed, invalid audience claim (aud)") | ||||
| 
 | ||||
| // ErrInvalidID indicates invalid jti claim.
 | ||||
| var ErrInvalidID = errors.New("square/go-jose/jwt: validation failed, invalid ID claim (jti)") | ||||
| 
 | ||||
| // ErrNotValidYet indicates that token is used before time indicated in nbf claim.
 | ||||
| var ErrNotValidYet = errors.New("square/go-jose/jwt: validation failed, token not valid yet (nbf)") | ||||
| 
 | ||||
| // ErrExpired indicates that token is used after expiry time indicated in exp claim.
 | ||||
| var ErrExpired = errors.New("square/go-jose/jwt: validation failed, token is expired (exp)") | ||||
| 
 | ||||
| // ErrIssuedInTheFuture indicates that the iat field is in the future.
 | ||||
| var ErrIssuedInTheFuture = errors.New("square/go-jose/jwt: validation field, token issued in the future (iat)") | ||||
| 
 | ||||
| // ErrInvalidContentType indicates that token requires JWT cty header.
 | ||||
| var ErrInvalidContentType = errors.New("square/go-jose/jwt: expected content type to be JWT (cty header)") | ||||
|  | @ -1,169 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2016 Zbigniew Mandziejewicz | ||||
|  * Copyright 2016 Square, Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package jwt | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	jose "gopkg.in/square/go-jose.v2" | ||||
| 	"gopkg.in/square/go-jose.v2/json" | ||||
| ) | ||||
| 
 | ||||
| // JSONWebToken represents a JSON Web Token (as specified in RFC7519).
 | ||||
| type JSONWebToken struct { | ||||
| 	payload           func(k interface{}) ([]byte, error) | ||||
| 	unverifiedPayload func() []byte | ||||
| 	Headers           []jose.Header | ||||
| } | ||||
| 
 | ||||
| type NestedJSONWebToken struct { | ||||
| 	enc     *jose.JSONWebEncryption | ||||
| 	Headers []jose.Header | ||||
| } | ||||
| 
 | ||||
| // Claims deserializes a JSONWebToken into dest using the provided key.
 | ||||
| func (t *JSONWebToken) Claims(key interface{}, dest ...interface{}) error { | ||||
| 	payloadKey := tryJWKS(t.Headers, key) | ||||
| 
 | ||||
| 	b, err := t.payload(payloadKey) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	for _, d := range dest { | ||||
| 		if err := json.Unmarshal(b, d); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // UnsafeClaimsWithoutVerification deserializes the claims of a
 | ||||
| // JSONWebToken into the dests. For signed JWTs, the claims are not
 | ||||
| // verified. This function won't work for encrypted JWTs.
 | ||||
| func (t *JSONWebToken) UnsafeClaimsWithoutVerification(dest ...interface{}) error { | ||||
| 	if t.unverifiedPayload == nil { | ||||
| 		return fmt.Errorf("square/go-jose: Cannot get unverified claims") | ||||
| 	} | ||||
| 	claims := t.unverifiedPayload() | ||||
| 	for _, d := range dest { | ||||
| 		if err := json.Unmarshal(claims, d); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (t *NestedJSONWebToken) Decrypt(decryptionKey interface{}) (*JSONWebToken, error) { | ||||
| 	key := tryJWKS(t.Headers, decryptionKey) | ||||
| 
 | ||||
| 	b, err := t.enc.Decrypt(key) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	sig, err := ParseSigned(string(b)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return sig, nil | ||||
| } | ||||
| 
 | ||||
| // ParseSigned parses token from JWS form.
 | ||||
| func ParseSigned(s string) (*JSONWebToken, error) { | ||||
| 	sig, err := jose.ParseSigned(s) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	headers := make([]jose.Header, len(sig.Signatures)) | ||||
| 	for i, signature := range sig.Signatures { | ||||
| 		headers[i] = signature.Header | ||||
| 	} | ||||
| 
 | ||||
| 	return &JSONWebToken{ | ||||
| 		payload:           sig.Verify, | ||||
| 		unverifiedPayload: sig.UnsafePayloadWithoutVerification, | ||||
| 		Headers:           headers, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // ParseEncrypted parses token from JWE form.
 | ||||
| func ParseEncrypted(s string) (*JSONWebToken, error) { | ||||
| 	enc, err := jose.ParseEncrypted(s) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &JSONWebToken{ | ||||
| 		payload: enc.Decrypt, | ||||
| 		Headers: []jose.Header{enc.Header}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // ParseSignedAndEncrypted parses signed-then-encrypted token from JWE form.
 | ||||
| func ParseSignedAndEncrypted(s string) (*NestedJSONWebToken, error) { | ||||
| 	enc, err := jose.ParseEncrypted(s) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	contentType, _ := enc.Header.ExtraHeaders[jose.HeaderContentType].(string) | ||||
| 	if strings.ToUpper(contentType) != "JWT" { | ||||
| 		return nil, ErrInvalidContentType | ||||
| 	} | ||||
| 
 | ||||
| 	return &NestedJSONWebToken{ | ||||
| 		enc:     enc, | ||||
| 		Headers: []jose.Header{enc.Header}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func tryJWKS(headers []jose.Header, key interface{}) interface{} { | ||||
| 	var jwks jose.JSONWebKeySet | ||||
| 
 | ||||
| 	switch jwksType := key.(type) { | ||||
| 	case *jose.JSONWebKeySet: | ||||
| 		jwks = *jwksType | ||||
| 	case jose.JSONWebKeySet: | ||||
| 		jwks = jwksType | ||||
| 	default: | ||||
| 		return key | ||||
| 	} | ||||
| 
 | ||||
| 	var kid string | ||||
| 	for _, header := range headers { | ||||
| 		if header.KeyID != "" { | ||||
| 			kid = header.KeyID | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if kid == "" { | ||||
| 		return key | ||||
| 	} | ||||
| 
 | ||||
| 	keys := jwks.Key(kid) | ||||
| 	if len(keys) == 0 { | ||||
| 		return key | ||||
| 	} | ||||
| 
 | ||||
| 	return keys[0].Key | ||||
| } | ||||
|  | @ -1,114 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2016 Zbigniew Mandziejewicz | ||||
|  * Copyright 2016 Square, Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package jwt | ||||
| 
 | ||||
| import "time" | ||||
| 
 | ||||
| const ( | ||||
| 	// DefaultLeeway defines the default leeway for matching NotBefore/Expiry claims.
 | ||||
| 	DefaultLeeway = 1.0 * time.Minute | ||||
| ) | ||||
| 
 | ||||
| // Expected defines values used for protected claims validation.
 | ||||
| // If field has zero value then validation is skipped.
 | ||||
| type Expected struct { | ||||
| 	// Issuer matches the "iss" claim exactly.
 | ||||
| 	Issuer string | ||||
| 	// Subject matches the "sub" claim exactly.
 | ||||
| 	Subject string | ||||
| 	// Audience matches the values in "aud" claim, regardless of their order.
 | ||||
| 	Audience Audience | ||||
| 	// ID matches the "jti" claim exactly.
 | ||||
| 	ID string | ||||
| 	// Time matches the "exp", "nbf" and "iat" claims with leeway.
 | ||||
| 	Time time.Time | ||||
| } | ||||
| 
 | ||||
| // WithTime copies expectations with new time.
 | ||||
| func (e Expected) WithTime(t time.Time) Expected { | ||||
| 	e.Time = t | ||||
| 	return e | ||||
| } | ||||
| 
 | ||||
| // Validate checks claims in a token against expected values.
 | ||||
| // A default leeway value of one minute is used to compare time values.
 | ||||
| //
 | ||||
| // The default leeway will cause the token to be deemed valid until one
 | ||||
| // minute after the expiration time. If you're a server application that
 | ||||
| // wants to give an extra minute to client tokens, use this
 | ||||
| // function. If you're a client application wondering if the server
 | ||||
| // will accept your token, use ValidateWithLeeway with a leeway <=0,
 | ||||
| // otherwise this function might make you think a token is valid when
 | ||||
| // it is not.
 | ||||
| func (c Claims) Validate(e Expected) error { | ||||
| 	return c.ValidateWithLeeway(e, DefaultLeeway) | ||||
| } | ||||
| 
 | ||||
| // ValidateWithLeeway checks claims in a token against expected values. A
 | ||||
| // custom leeway may be specified for comparing time values. You may pass a
 | ||||
| // zero value to check time values with no leeway, but you should not that
 | ||||
| // numeric date values are rounded to the nearest second and sub-second
 | ||||
| // precision is not supported.
 | ||||
| //
 | ||||
| // The leeway gives some extra time to the token from the server's
 | ||||
| // point of view. That is, if the token is expired, ValidateWithLeeway
 | ||||
| // will still accept the token for 'leeway' amount of time. This fails
 | ||||
| // if you're using this function to check if a server will accept your
 | ||||
| // token, because it will think the token is valid even after it
 | ||||
| // expires. So if you're a client validating if the token is valid to
 | ||||
| // be submitted to a server, use leeway <=0, if you're a server
 | ||||
| // validation a token, use leeway >=0.
 | ||||
| func (c Claims) ValidateWithLeeway(e Expected, leeway time.Duration) error { | ||||
| 	if e.Issuer != "" && e.Issuer != c.Issuer { | ||||
| 		return ErrInvalidIssuer | ||||
| 	} | ||||
| 
 | ||||
| 	if e.Subject != "" && e.Subject != c.Subject { | ||||
| 		return ErrInvalidSubject | ||||
| 	} | ||||
| 
 | ||||
| 	if e.ID != "" && e.ID != c.ID { | ||||
| 		return ErrInvalidID | ||||
| 	} | ||||
| 
 | ||||
| 	if len(e.Audience) != 0 { | ||||
| 		for _, v := range e.Audience { | ||||
| 			if !c.Audience.Contains(v) { | ||||
| 				return ErrInvalidAudience | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if !e.Time.IsZero() { | ||||
| 		if c.NotBefore != nil && e.Time.Add(leeway).Before(c.NotBefore.Time()) { | ||||
| 			return ErrNotValidYet | ||||
| 		} | ||||
| 
 | ||||
| 		if c.Expiry != nil && e.Time.Add(-leeway).After(c.Expiry.Time()) { | ||||
| 			return ErrExpired | ||||
| 		} | ||||
| 
 | ||||
| 		// IssuedAt is optional but cannot be in the future. This is not required by the RFC, but
 | ||||
| 		// something is misconfigured if this happens and we should not trust it.
 | ||||
| 		if c.IssuedAt != nil && e.Time.Add(leeway).Before(c.IssuedAt.Time()) { | ||||
| 			return ErrIssuedInTheFuture | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
|  | @ -1,144 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2018 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package jose | ||||
| 
 | ||||
| // OpaqueSigner is an interface that supports signing payloads with opaque
 | ||||
| // private key(s). Private key operations performed by implementers may, for
 | ||||
| // example, occur in a hardware module. An OpaqueSigner may rotate signing keys
 | ||||
| // transparently to the user of this interface.
 | ||||
| type OpaqueSigner interface { | ||||
| 	// Public returns the public key of the current signing key.
 | ||||
| 	Public() *JSONWebKey | ||||
| 	// Algs returns a list of supported signing algorithms.
 | ||||
| 	Algs() []SignatureAlgorithm | ||||
| 	// SignPayload signs a payload with the current signing key using the given
 | ||||
| 	// algorithm.
 | ||||
| 	SignPayload(payload []byte, alg SignatureAlgorithm) ([]byte, error) | ||||
| } | ||||
| 
 | ||||
| type opaqueSigner struct { | ||||
| 	signer OpaqueSigner | ||||
| } | ||||
| 
 | ||||
| func newOpaqueSigner(alg SignatureAlgorithm, signer OpaqueSigner) (recipientSigInfo, error) { | ||||
| 	var algSupported bool | ||||
| 	for _, salg := range signer.Algs() { | ||||
| 		if alg == salg { | ||||
| 			algSupported = true | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if !algSupported { | ||||
| 		return recipientSigInfo{}, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	return recipientSigInfo{ | ||||
| 		sigAlg:    alg, | ||||
| 		publicKey: signer.Public, | ||||
| 		signer: &opaqueSigner{ | ||||
| 			signer: signer, | ||||
| 		}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (o *opaqueSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) { | ||||
| 	out, err := o.signer.SignPayload(payload, alg) | ||||
| 	if err != nil { | ||||
| 		return Signature{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	return Signature{ | ||||
| 		Signature: out, | ||||
| 		protected: &rawHeader{}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // OpaqueVerifier is an interface that supports verifying payloads with opaque
 | ||||
| // public key(s). An OpaqueSigner may rotate signing keys transparently to the
 | ||||
| // user of this interface.
 | ||||
| type OpaqueVerifier interface { | ||||
| 	VerifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error | ||||
| } | ||||
| 
 | ||||
| type opaqueVerifier struct { | ||||
| 	verifier OpaqueVerifier | ||||
| } | ||||
| 
 | ||||
| func (o *opaqueVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error { | ||||
| 	return o.verifier.VerifyPayload(payload, signature, alg) | ||||
| } | ||||
| 
 | ||||
| // OpaqueKeyEncrypter is an interface that supports encrypting keys with an opaque key.
 | ||||
| type OpaqueKeyEncrypter interface { | ||||
| 	// KeyID returns the kid
 | ||||
| 	KeyID() string | ||||
| 	// Algs returns a list of supported key encryption algorithms.
 | ||||
| 	Algs() []KeyAlgorithm | ||||
| 	// encryptKey encrypts the CEK using the given algorithm.
 | ||||
| 	encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) | ||||
| } | ||||
| 
 | ||||
| type opaqueKeyEncrypter struct { | ||||
| 	encrypter OpaqueKeyEncrypter | ||||
| } | ||||
| 
 | ||||
| func newOpaqueKeyEncrypter(alg KeyAlgorithm, encrypter OpaqueKeyEncrypter) (recipientKeyInfo, error) { | ||||
| 	var algSupported bool | ||||
| 	for _, salg := range encrypter.Algs() { | ||||
| 		if alg == salg { | ||||
| 			algSupported = true | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if !algSupported { | ||||
| 		return recipientKeyInfo{}, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	return recipientKeyInfo{ | ||||
| 		keyID:  encrypter.KeyID(), | ||||
| 		keyAlg: alg, | ||||
| 		keyEncrypter: &opaqueKeyEncrypter{ | ||||
| 			encrypter: encrypter, | ||||
| 		}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (oke *opaqueKeyEncrypter) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) { | ||||
| 	return oke.encrypter.encryptKey(cek, alg) | ||||
| } | ||||
| 
 | ||||
| //OpaqueKeyDecrypter is an interface that supports decrypting keys with an opaque key.
 | ||||
| type OpaqueKeyDecrypter interface { | ||||
| 	DecryptKey(encryptedKey []byte, header Header) ([]byte, error) | ||||
| } | ||||
| 
 | ||||
| type opaqueKeyDecrypter struct { | ||||
| 	decrypter OpaqueKeyDecrypter | ||||
| } | ||||
| 
 | ||||
| func (okd *opaqueKeyDecrypter) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) { | ||||
| 	mergedHeaders := rawHeader{} | ||||
| 	mergedHeaders.merge(&headers) | ||||
| 	mergedHeaders.merge(recipient.header) | ||||
| 
 | ||||
| 	header, err := mergedHeaders.sanitized() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return okd.decrypter.DecryptKey(recipient.encryptedKey, header) | ||||
| } | ||||
|  | @ -1,520 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2014 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package jose | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/elliptic" | ||||
| 	"crypto/x509" | ||||
| 	"encoding/base64" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"gopkg.in/square/go-jose.v2/json" | ||||
| ) | ||||
| 
 | ||||
| // KeyAlgorithm represents a key management algorithm.
 | ||||
| type KeyAlgorithm string | ||||
| 
 | ||||
| // SignatureAlgorithm represents a signature (or MAC) algorithm.
 | ||||
| type SignatureAlgorithm string | ||||
| 
 | ||||
| // ContentEncryption represents a content encryption algorithm.
 | ||||
| type ContentEncryption string | ||||
| 
 | ||||
| // CompressionAlgorithm represents an algorithm used for plaintext compression.
 | ||||
| type CompressionAlgorithm string | ||||
| 
 | ||||
| // ContentType represents type of the contained data.
 | ||||
| type ContentType string | ||||
| 
 | ||||
| var ( | ||||
| 	// ErrCryptoFailure represents an error in cryptographic primitive. This
 | ||||
| 	// occurs when, for example, a message had an invalid authentication tag or
 | ||||
| 	// could not be decrypted.
 | ||||
| 	ErrCryptoFailure = errors.New("square/go-jose: error in cryptographic primitive") | ||||
| 
 | ||||
| 	// ErrUnsupportedAlgorithm indicates that a selected algorithm is not
 | ||||
| 	// supported. This occurs when trying to instantiate an encrypter for an
 | ||||
| 	// algorithm that is not yet implemented.
 | ||||
| 	ErrUnsupportedAlgorithm = errors.New("square/go-jose: unknown/unsupported algorithm") | ||||
| 
 | ||||
| 	// ErrUnsupportedKeyType indicates that the given key type/format is not
 | ||||
| 	// supported. This occurs when trying to instantiate an encrypter and passing
 | ||||
| 	// it a key of an unrecognized type or with unsupported parameters, such as
 | ||||
| 	// an RSA private key with more than two primes.
 | ||||
| 	ErrUnsupportedKeyType = errors.New("square/go-jose: unsupported key type/format") | ||||
| 
 | ||||
| 	// ErrInvalidKeySize indicates that the given key is not the correct size
 | ||||
| 	// for the selected algorithm. This can occur, for example, when trying to
 | ||||
| 	// encrypt with AES-256 but passing only a 128-bit key as input.
 | ||||
| 	ErrInvalidKeySize = errors.New("square/go-jose: invalid key size for algorithm") | ||||
| 
 | ||||
| 	// ErrNotSupported serialization of object is not supported. This occurs when
 | ||||
| 	// trying to compact-serialize an object which can't be represented in
 | ||||
| 	// compact form.
 | ||||
| 	ErrNotSupported = errors.New("square/go-jose: compact serialization not supported for object") | ||||
| 
 | ||||
| 	// ErrUnprotectedNonce indicates that while parsing a JWS or JWE object, a
 | ||||
| 	// nonce header parameter was included in an unprotected header object.
 | ||||
| 	ErrUnprotectedNonce = errors.New("square/go-jose: Nonce parameter included in unprotected header") | ||||
| ) | ||||
| 
 | ||||
| // Key management algorithms
 | ||||
| const ( | ||||
| 	ED25519            = KeyAlgorithm("ED25519") | ||||
| 	RSA1_5             = KeyAlgorithm("RSA1_5")             // RSA-PKCS1v1.5
 | ||||
| 	RSA_OAEP           = KeyAlgorithm("RSA-OAEP")           // RSA-OAEP-SHA1
 | ||||
| 	RSA_OAEP_256       = KeyAlgorithm("RSA-OAEP-256")       // RSA-OAEP-SHA256
 | ||||
| 	A128KW             = KeyAlgorithm("A128KW")             // AES key wrap (128)
 | ||||
| 	A192KW             = KeyAlgorithm("A192KW")             // AES key wrap (192)
 | ||||
| 	A256KW             = KeyAlgorithm("A256KW")             // AES key wrap (256)
 | ||||
| 	DIRECT             = KeyAlgorithm("dir")                // Direct encryption
 | ||||
| 	ECDH_ES            = KeyAlgorithm("ECDH-ES")            // ECDH-ES
 | ||||
| 	ECDH_ES_A128KW     = KeyAlgorithm("ECDH-ES+A128KW")     // ECDH-ES + AES key wrap (128)
 | ||||
| 	ECDH_ES_A192KW     = KeyAlgorithm("ECDH-ES+A192KW")     // ECDH-ES + AES key wrap (192)
 | ||||
| 	ECDH_ES_A256KW     = KeyAlgorithm("ECDH-ES+A256KW")     // ECDH-ES + AES key wrap (256)
 | ||||
| 	A128GCMKW          = KeyAlgorithm("A128GCMKW")          // AES-GCM key wrap (128)
 | ||||
| 	A192GCMKW          = KeyAlgorithm("A192GCMKW")          // AES-GCM key wrap (192)
 | ||||
| 	A256GCMKW          = KeyAlgorithm("A256GCMKW")          // AES-GCM key wrap (256)
 | ||||
| 	PBES2_HS256_A128KW = KeyAlgorithm("PBES2-HS256+A128KW") // PBES2 + HMAC-SHA256 + AES key wrap (128)
 | ||||
| 	PBES2_HS384_A192KW = KeyAlgorithm("PBES2-HS384+A192KW") // PBES2 + HMAC-SHA384 + AES key wrap (192)
 | ||||
| 	PBES2_HS512_A256KW = KeyAlgorithm("PBES2-HS512+A256KW") // PBES2 + HMAC-SHA512 + AES key wrap (256)
 | ||||
| ) | ||||
| 
 | ||||
| // Signature algorithms
 | ||||
| const ( | ||||
| 	EdDSA = SignatureAlgorithm("EdDSA") | ||||
| 	HS256 = SignatureAlgorithm("HS256") // HMAC using SHA-256
 | ||||
| 	HS384 = SignatureAlgorithm("HS384") // HMAC using SHA-384
 | ||||
| 	HS512 = SignatureAlgorithm("HS512") // HMAC using SHA-512
 | ||||
| 	RS256 = SignatureAlgorithm("RS256") // RSASSA-PKCS-v1.5 using SHA-256
 | ||||
| 	RS384 = SignatureAlgorithm("RS384") // RSASSA-PKCS-v1.5 using SHA-384
 | ||||
| 	RS512 = SignatureAlgorithm("RS512") // RSASSA-PKCS-v1.5 using SHA-512
 | ||||
| 	ES256 = SignatureAlgorithm("ES256") // ECDSA using P-256 and SHA-256
 | ||||
| 	ES384 = SignatureAlgorithm("ES384") // ECDSA using P-384 and SHA-384
 | ||||
| 	ES512 = SignatureAlgorithm("ES512") // ECDSA using P-521 and SHA-512
 | ||||
| 	PS256 = SignatureAlgorithm("PS256") // RSASSA-PSS using SHA256 and MGF1-SHA256
 | ||||
| 	PS384 = SignatureAlgorithm("PS384") // RSASSA-PSS using SHA384 and MGF1-SHA384
 | ||||
| 	PS512 = SignatureAlgorithm("PS512") // RSASSA-PSS using SHA512 and MGF1-SHA512
 | ||||
| ) | ||||
| 
 | ||||
| // Content encryption algorithms
 | ||||
| const ( | ||||
| 	A128CBC_HS256 = ContentEncryption("A128CBC-HS256") // AES-CBC + HMAC-SHA256 (128)
 | ||||
| 	A192CBC_HS384 = ContentEncryption("A192CBC-HS384") // AES-CBC + HMAC-SHA384 (192)
 | ||||
| 	A256CBC_HS512 = ContentEncryption("A256CBC-HS512") // AES-CBC + HMAC-SHA512 (256)
 | ||||
| 	A128GCM       = ContentEncryption("A128GCM")       // AES-GCM (128)
 | ||||
| 	A192GCM       = ContentEncryption("A192GCM")       // AES-GCM (192)
 | ||||
| 	A256GCM       = ContentEncryption("A256GCM")       // AES-GCM (256)
 | ||||
| ) | ||||
| 
 | ||||
| // Compression algorithms
 | ||||
| const ( | ||||
| 	NONE    = CompressionAlgorithm("")    // No compression
 | ||||
| 	DEFLATE = CompressionAlgorithm("DEF") // DEFLATE (RFC 1951)
 | ||||
| ) | ||||
| 
 | ||||
| // A key in the protected header of a JWS object. Use of the Header...
 | ||||
| // constants is preferred to enhance type safety.
 | ||||
| type HeaderKey string | ||||
| 
 | ||||
| const ( | ||||
| 	HeaderType        HeaderKey = "typ" // string
 | ||||
| 	HeaderContentType           = "cty" // string
 | ||||
| 
 | ||||
| 	// These are set by go-jose and shouldn't need to be set by consumers of the
 | ||||
| 	// library.
 | ||||
| 	headerAlgorithm   = "alg"  // string
 | ||||
| 	headerEncryption  = "enc"  // ContentEncryption
 | ||||
| 	headerCompression = "zip"  // CompressionAlgorithm
 | ||||
| 	headerCritical    = "crit" // []string
 | ||||
| 
 | ||||
| 	headerAPU = "apu" // *byteBuffer
 | ||||
| 	headerAPV = "apv" // *byteBuffer
 | ||||
| 	headerEPK = "epk" // *JSONWebKey
 | ||||
| 	headerIV  = "iv"  // *byteBuffer
 | ||||
| 	headerTag = "tag" // *byteBuffer
 | ||||
| 	headerX5c = "x5c" // []*x509.Certificate
 | ||||
| 
 | ||||
| 	headerJWK   = "jwk"   // *JSONWebKey
 | ||||
| 	headerKeyID = "kid"   // string
 | ||||
| 	headerNonce = "nonce" // string
 | ||||
| 	headerB64   = "b64"   // bool
 | ||||
| 
 | ||||
| 	headerP2C = "p2c" // *byteBuffer (int)
 | ||||
| 	headerP2S = "p2s" // *byteBuffer ([]byte)
 | ||||
| 
 | ||||
| ) | ||||
| 
 | ||||
| // supportedCritical is the set of supported extensions that are understood and processed.
 | ||||
| var supportedCritical = map[string]bool{ | ||||
| 	headerB64: true, | ||||
| } | ||||
| 
 | ||||
| // rawHeader represents the JOSE header for JWE/JWS objects (used for parsing).
 | ||||
| //
 | ||||
| // The decoding of the constituent items is deferred because we want to marshal
 | ||||
| // some members into particular structs rather than generic maps, but at the
 | ||||
| // same time we need to receive any extra fields unhandled by this library to
 | ||||
| // pass through to consuming code in case it wants to examine them.
 | ||||
| type rawHeader map[HeaderKey]*json.RawMessage | ||||
| 
 | ||||
| // Header represents the read-only JOSE header for JWE/JWS objects.
 | ||||
| type Header struct { | ||||
| 	KeyID      string | ||||
| 	JSONWebKey *JSONWebKey | ||||
| 	Algorithm  string | ||||
| 	Nonce      string | ||||
| 
 | ||||
| 	// Unverified certificate chain parsed from x5c header.
 | ||||
| 	certificates []*x509.Certificate | ||||
| 
 | ||||
| 	// Any headers not recognised above get unmarshalled
 | ||||
| 	// from JSON in a generic manner and placed in this map.
 | ||||
| 	ExtraHeaders map[HeaderKey]interface{} | ||||
| } | ||||
| 
 | ||||
| // Certificates verifies & returns the certificate chain present
 | ||||
| // in the x5c header field of a message, if one was present. Returns
 | ||||
| // an error if there was no x5c header present or the chain could
 | ||||
| // not be validated with the given verify options.
 | ||||
| func (h Header) Certificates(opts x509.VerifyOptions) ([][]*x509.Certificate, error) { | ||||
| 	if len(h.certificates) == 0 { | ||||
| 		return nil, errors.New("square/go-jose: no x5c header present in message") | ||||
| 	} | ||||
| 
 | ||||
| 	leaf := h.certificates[0] | ||||
| 	if opts.Intermediates == nil { | ||||
| 		opts.Intermediates = x509.NewCertPool() | ||||
| 		for _, intermediate := range h.certificates[1:] { | ||||
| 			opts.Intermediates.AddCert(intermediate) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return leaf.Verify(opts) | ||||
| } | ||||
| 
 | ||||
| func (parsed rawHeader) set(k HeaderKey, v interface{}) error { | ||||
| 	b, err := json.Marshal(v) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	parsed[k] = makeRawMessage(b) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // getString gets a string from the raw JSON, defaulting to "".
 | ||||
| func (parsed rawHeader) getString(k HeaderKey) string { | ||||
| 	v, ok := parsed[k] | ||||
| 	if !ok || v == nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	var s string | ||||
| 	err := json.Unmarshal(*v, &s) | ||||
| 	if err != nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
| 
 | ||||
| // getByteBuffer gets a byte buffer from the raw JSON. Returns (nil, nil) if
 | ||||
| // not specified.
 | ||||
| func (parsed rawHeader) getByteBuffer(k HeaderKey) (*byteBuffer, error) { | ||||
| 	v := parsed[k] | ||||
| 	if v == nil { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	var bb *byteBuffer | ||||
| 	err := json.Unmarshal(*v, &bb) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return bb, nil | ||||
| } | ||||
| 
 | ||||
| // getAlgorithm extracts parsed "alg" from the raw JSON as a KeyAlgorithm.
 | ||||
| func (parsed rawHeader) getAlgorithm() KeyAlgorithm { | ||||
| 	return KeyAlgorithm(parsed.getString(headerAlgorithm)) | ||||
| } | ||||
| 
 | ||||
| // getSignatureAlgorithm extracts parsed "alg" from the raw JSON as a SignatureAlgorithm.
 | ||||
| func (parsed rawHeader) getSignatureAlgorithm() SignatureAlgorithm { | ||||
| 	return SignatureAlgorithm(parsed.getString(headerAlgorithm)) | ||||
| } | ||||
| 
 | ||||
| // getEncryption extracts parsed "enc" from the raw JSON.
 | ||||
| func (parsed rawHeader) getEncryption() ContentEncryption { | ||||
| 	return ContentEncryption(parsed.getString(headerEncryption)) | ||||
| } | ||||
| 
 | ||||
| // getCompression extracts parsed "zip" from the raw JSON.
 | ||||
| func (parsed rawHeader) getCompression() CompressionAlgorithm { | ||||
| 	return CompressionAlgorithm(parsed.getString(headerCompression)) | ||||
| } | ||||
| 
 | ||||
| func (parsed rawHeader) getNonce() string { | ||||
| 	return parsed.getString(headerNonce) | ||||
| } | ||||
| 
 | ||||
| // getEPK extracts parsed "epk" from the raw JSON.
 | ||||
| func (parsed rawHeader) getEPK() (*JSONWebKey, error) { | ||||
| 	v := parsed[headerEPK] | ||||
| 	if v == nil { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	var epk *JSONWebKey | ||||
| 	err := json.Unmarshal(*v, &epk) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return epk, nil | ||||
| } | ||||
| 
 | ||||
| // getAPU extracts parsed "apu" from the raw JSON.
 | ||||
| func (parsed rawHeader) getAPU() (*byteBuffer, error) { | ||||
| 	return parsed.getByteBuffer(headerAPU) | ||||
| } | ||||
| 
 | ||||
| // getAPV extracts parsed "apv" from the raw JSON.
 | ||||
| func (parsed rawHeader) getAPV() (*byteBuffer, error) { | ||||
| 	return parsed.getByteBuffer(headerAPV) | ||||
| } | ||||
| 
 | ||||
| // getIV extracts parsed "iv" from the raw JSON.
 | ||||
| func (parsed rawHeader) getIV() (*byteBuffer, error) { | ||||
| 	return parsed.getByteBuffer(headerIV) | ||||
| } | ||||
| 
 | ||||
| // getTag extracts parsed "tag" from the raw JSON.
 | ||||
| func (parsed rawHeader) getTag() (*byteBuffer, error) { | ||||
| 	return parsed.getByteBuffer(headerTag) | ||||
| } | ||||
| 
 | ||||
| // getJWK extracts parsed "jwk" from the raw JSON.
 | ||||
| func (parsed rawHeader) getJWK() (*JSONWebKey, error) { | ||||
| 	v := parsed[headerJWK] | ||||
| 	if v == nil { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	var jwk *JSONWebKey | ||||
| 	err := json.Unmarshal(*v, &jwk) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return jwk, nil | ||||
| } | ||||
| 
 | ||||
| // getCritical extracts parsed "crit" from the raw JSON. If omitted, it
 | ||||
| // returns an empty slice.
 | ||||
| func (parsed rawHeader) getCritical() ([]string, error) { | ||||
| 	v := parsed[headerCritical] | ||||
| 	if v == nil { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 
 | ||||
| 	var q []string | ||||
| 	err := json.Unmarshal(*v, &q) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return q, nil | ||||
| } | ||||
| 
 | ||||
| // getS2C extracts parsed "p2c" from the raw JSON.
 | ||||
| func (parsed rawHeader) getP2C() (int, error) { | ||||
| 	v := parsed[headerP2C] | ||||
| 	if v == nil { | ||||
| 		return 0, nil | ||||
| 	} | ||||
| 
 | ||||
| 	var p2c int | ||||
| 	err := json.Unmarshal(*v, &p2c) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| 	return p2c, nil | ||||
| } | ||||
| 
 | ||||
| // getS2S extracts parsed "p2s" from the raw JSON.
 | ||||
| func (parsed rawHeader) getP2S() (*byteBuffer, error) { | ||||
| 	return parsed.getByteBuffer(headerP2S) | ||||
| } | ||||
| 
 | ||||
| // getB64 extracts parsed "b64" from the raw JSON, defaulting to true.
 | ||||
| func (parsed rawHeader) getB64() (bool, error) { | ||||
| 	v := parsed[headerB64] | ||||
| 	if v == nil { | ||||
| 		return true, nil | ||||
| 	} | ||||
| 
 | ||||
| 	var b64 bool | ||||
| 	err := json.Unmarshal(*v, &b64) | ||||
| 	if err != nil { | ||||
| 		return true, err | ||||
| 	} | ||||
| 	return b64, nil | ||||
| } | ||||
| 
 | ||||
| // sanitized produces a cleaned-up header object from the raw JSON.
 | ||||
| func (parsed rawHeader) sanitized() (h Header, err error) { | ||||
| 	for k, v := range parsed { | ||||
| 		if v == nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		switch k { | ||||
| 		case headerJWK: | ||||
| 			var jwk *JSONWebKey | ||||
| 			err = json.Unmarshal(*v, &jwk) | ||||
| 			if err != nil { | ||||
| 				err = fmt.Errorf("failed to unmarshal JWK: %v: %#v", err, string(*v)) | ||||
| 				return | ||||
| 			} | ||||
| 			h.JSONWebKey = jwk | ||||
| 		case headerKeyID: | ||||
| 			var s string | ||||
| 			err = json.Unmarshal(*v, &s) | ||||
| 			if err != nil { | ||||
| 				err = fmt.Errorf("failed to unmarshal key ID: %v: %#v", err, string(*v)) | ||||
| 				return | ||||
| 			} | ||||
| 			h.KeyID = s | ||||
| 		case headerAlgorithm: | ||||
| 			var s string | ||||
| 			err = json.Unmarshal(*v, &s) | ||||
| 			if err != nil { | ||||
| 				err = fmt.Errorf("failed to unmarshal algorithm: %v: %#v", err, string(*v)) | ||||
| 				return | ||||
| 			} | ||||
| 			h.Algorithm = s | ||||
| 		case headerNonce: | ||||
| 			var s string | ||||
| 			err = json.Unmarshal(*v, &s) | ||||
| 			if err != nil { | ||||
| 				err = fmt.Errorf("failed to unmarshal nonce: %v: %#v", err, string(*v)) | ||||
| 				return | ||||
| 			} | ||||
| 			h.Nonce = s | ||||
| 		case headerX5c: | ||||
| 			c := []string{} | ||||
| 			err = json.Unmarshal(*v, &c) | ||||
| 			if err != nil { | ||||
| 				err = fmt.Errorf("failed to unmarshal x5c header: %v: %#v", err, string(*v)) | ||||
| 				return | ||||
| 			} | ||||
| 			h.certificates, err = parseCertificateChain(c) | ||||
| 			if err != nil { | ||||
| 				err = fmt.Errorf("failed to unmarshal x5c header: %v: %#v", err, string(*v)) | ||||
| 				return | ||||
| 			} | ||||
| 		default: | ||||
| 			if h.ExtraHeaders == nil { | ||||
| 				h.ExtraHeaders = map[HeaderKey]interface{}{} | ||||
| 			} | ||||
| 			var v2 interface{} | ||||
| 			err = json.Unmarshal(*v, &v2) | ||||
| 			if err != nil { | ||||
| 				err = fmt.Errorf("failed to unmarshal value: %v: %#v", err, string(*v)) | ||||
| 				return | ||||
| 			} | ||||
| 			h.ExtraHeaders[k] = v2 | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func parseCertificateChain(chain []string) ([]*x509.Certificate, error) { | ||||
| 	out := make([]*x509.Certificate, len(chain)) | ||||
| 	for i, cert := range chain { | ||||
| 		raw, err := base64.StdEncoding.DecodeString(cert) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		out[i], err = x509.ParseCertificate(raw) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	return out, nil | ||||
| } | ||||
| 
 | ||||
| func (dst rawHeader) isSet(k HeaderKey) bool { | ||||
| 	dvr := dst[k] | ||||
| 	if dvr == nil { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	var dv interface{} | ||||
| 	err := json.Unmarshal(*dvr, &dv) | ||||
| 	if err != nil { | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	if dvStr, ok := dv.(string); ok { | ||||
| 		return dvStr != "" | ||||
| 	} | ||||
| 
 | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // Merge headers from src into dst, giving precedence to headers from l.
 | ||||
| func (dst rawHeader) merge(src *rawHeader) { | ||||
| 	if src == nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	for k, v := range *src { | ||||
| 		if dst.isSet(k) { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		dst[k] = v | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Get JOSE name of curve
 | ||||
| func curveName(crv elliptic.Curve) (string, error) { | ||||
| 	switch crv { | ||||
| 	case elliptic.P256(): | ||||
| 		return "P-256", nil | ||||
| 	case elliptic.P384(): | ||||
| 		return "P-384", nil | ||||
| 	case elliptic.P521(): | ||||
| 		return "P-521", nil | ||||
| 	default: | ||||
| 		return "", fmt.Errorf("square/go-jose: unsupported/unknown elliptic curve") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Get size of curve in bytes
 | ||||
| func curveSize(crv elliptic.Curve) int { | ||||
| 	bits := crv.Params().BitSize | ||||
| 
 | ||||
| 	div := bits / 8 | ||||
| 	mod := bits % 8 | ||||
| 
 | ||||
| 	if mod == 0 { | ||||
| 		return div | ||||
| 	} | ||||
| 
 | ||||
| 	return div + 1 | ||||
| } | ||||
| 
 | ||||
| func makeRawMessage(b []byte) *json.RawMessage { | ||||
| 	rm := json.RawMessage(b) | ||||
| 	return &rm | ||||
| } | ||||
|  | @ -1,441 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2014 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package jose | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/rsa" | ||||
| 	"encoding/base64" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"golang.org/x/crypto/ed25519" | ||||
| 
 | ||||
| 	"gopkg.in/square/go-jose.v2/json" | ||||
| ) | ||||
| 
 | ||||
| // NonceSource represents a source of random nonces to go into JWS objects
 | ||||
| type NonceSource interface { | ||||
| 	Nonce() (string, error) | ||||
| } | ||||
| 
 | ||||
| // Signer represents a signer which takes a payload and produces a signed JWS object.
 | ||||
| type Signer interface { | ||||
| 	Sign(payload []byte) (*JSONWebSignature, error) | ||||
| 	Options() SignerOptions | ||||
| } | ||||
| 
 | ||||
| // SigningKey represents an algorithm/key used to sign a message.
 | ||||
| type SigningKey struct { | ||||
| 	Algorithm SignatureAlgorithm | ||||
| 	Key       interface{} | ||||
| } | ||||
| 
 | ||||
| // SignerOptions represents options that can be set when creating signers.
 | ||||
| type SignerOptions struct { | ||||
| 	NonceSource NonceSource | ||||
| 	EmbedJWK    bool | ||||
| 
 | ||||
| 	// Optional map of additional keys to be inserted into the protected header
 | ||||
| 	// of a JWS object. Some specifications which make use of JWS like to insert
 | ||||
| 	// additional values here. All values must be JSON-serializable.
 | ||||
| 	ExtraHeaders map[HeaderKey]interface{} | ||||
| } | ||||
| 
 | ||||
| // WithHeader adds an arbitrary value to the ExtraHeaders map, initializing it
 | ||||
| // if necessary. It returns itself and so can be used in a fluent style.
 | ||||
| func (so *SignerOptions) WithHeader(k HeaderKey, v interface{}) *SignerOptions { | ||||
| 	if so.ExtraHeaders == nil { | ||||
| 		so.ExtraHeaders = map[HeaderKey]interface{}{} | ||||
| 	} | ||||
| 	so.ExtraHeaders[k] = v | ||||
| 	return so | ||||
| } | ||||
| 
 | ||||
| // WithContentType adds a content type ("cty") header and returns the updated
 | ||||
| // SignerOptions.
 | ||||
| func (so *SignerOptions) WithContentType(contentType ContentType) *SignerOptions { | ||||
| 	return so.WithHeader(HeaderContentType, contentType) | ||||
| } | ||||
| 
 | ||||
| // WithType adds a type ("typ") header and returns the updated SignerOptions.
 | ||||
| func (so *SignerOptions) WithType(typ ContentType) *SignerOptions { | ||||
| 	return so.WithHeader(HeaderType, typ) | ||||
| } | ||||
| 
 | ||||
| // WithCritical adds the given names to the critical ("crit") header and returns
 | ||||
| // the updated SignerOptions.
 | ||||
| func (so *SignerOptions) WithCritical(names ...string) *SignerOptions { | ||||
| 	if so.ExtraHeaders[headerCritical] == nil { | ||||
| 		so.WithHeader(headerCritical, make([]string, 0, len(names))) | ||||
| 	} | ||||
| 	crit := so.ExtraHeaders[headerCritical].([]string) | ||||
| 	so.ExtraHeaders[headerCritical] = append(crit, names...) | ||||
| 	return so | ||||
| } | ||||
| 
 | ||||
| // WithBase64 adds a base64url-encode payload ("b64") header and returns the updated
 | ||||
| // SignerOptions. When the "b64" value is "false", the payload is not base64 encoded.
 | ||||
| func (so *SignerOptions) WithBase64(b64 bool) *SignerOptions { | ||||
| 	if !b64 { | ||||
| 		so.WithHeader(headerB64, b64) | ||||
| 		so.WithCritical(headerB64) | ||||
| 	} | ||||
| 	return so | ||||
| } | ||||
| 
 | ||||
| type payloadSigner interface { | ||||
| 	signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) | ||||
| } | ||||
| 
 | ||||
| type payloadVerifier interface { | ||||
| 	verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error | ||||
| } | ||||
| 
 | ||||
| type genericSigner struct { | ||||
| 	recipients   []recipientSigInfo | ||||
| 	nonceSource  NonceSource | ||||
| 	embedJWK     bool | ||||
| 	extraHeaders map[HeaderKey]interface{} | ||||
| } | ||||
| 
 | ||||
| type recipientSigInfo struct { | ||||
| 	sigAlg    SignatureAlgorithm | ||||
| 	publicKey func() *JSONWebKey | ||||
| 	signer    payloadSigner | ||||
| } | ||||
| 
 | ||||
| func staticPublicKey(jwk *JSONWebKey) func() *JSONWebKey { | ||||
| 	return func() *JSONWebKey { | ||||
| 		return jwk | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // NewSigner creates an appropriate signer based on the key type
 | ||||
| func NewSigner(sig SigningKey, opts *SignerOptions) (Signer, error) { | ||||
| 	return NewMultiSigner([]SigningKey{sig}, opts) | ||||
| } | ||||
| 
 | ||||
| // NewMultiSigner creates a signer for multiple recipients
 | ||||
| func NewMultiSigner(sigs []SigningKey, opts *SignerOptions) (Signer, error) { | ||||
| 	signer := &genericSigner{recipients: []recipientSigInfo{}} | ||||
| 
 | ||||
| 	if opts != nil { | ||||
| 		signer.nonceSource = opts.NonceSource | ||||
| 		signer.embedJWK = opts.EmbedJWK | ||||
| 		signer.extraHeaders = opts.ExtraHeaders | ||||
| 	} | ||||
| 
 | ||||
| 	for _, sig := range sigs { | ||||
| 		err := signer.addRecipient(sig.Algorithm, sig.Key) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return signer, nil | ||||
| } | ||||
| 
 | ||||
| // newVerifier creates a verifier based on the key type
 | ||||
| func newVerifier(verificationKey interface{}) (payloadVerifier, error) { | ||||
| 	switch verificationKey := verificationKey.(type) { | ||||
| 	case ed25519.PublicKey: | ||||
| 		return &edEncrypterVerifier{ | ||||
| 			publicKey: verificationKey, | ||||
| 		}, nil | ||||
| 	case *rsa.PublicKey: | ||||
| 		return &rsaEncrypterVerifier{ | ||||
| 			publicKey: verificationKey, | ||||
| 		}, nil | ||||
| 	case *ecdsa.PublicKey: | ||||
| 		return &ecEncrypterVerifier{ | ||||
| 			publicKey: verificationKey, | ||||
| 		}, nil | ||||
| 	case []byte: | ||||
| 		return &symmetricMac{ | ||||
| 			key: verificationKey, | ||||
| 		}, nil | ||||
| 	case JSONWebKey: | ||||
| 		return newVerifier(verificationKey.Key) | ||||
| 	case *JSONWebKey: | ||||
| 		return newVerifier(verificationKey.Key) | ||||
| 	} | ||||
| 	if ov, ok := verificationKey.(OpaqueVerifier); ok { | ||||
| 		return &opaqueVerifier{verifier: ov}, nil | ||||
| 	} | ||||
| 	return nil, ErrUnsupportedKeyType | ||||
| } | ||||
| 
 | ||||
| func (ctx *genericSigner) addRecipient(alg SignatureAlgorithm, signingKey interface{}) error { | ||||
| 	recipient, err := makeJWSRecipient(alg, signingKey) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.recipients = append(ctx.recipients, recipient) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func makeJWSRecipient(alg SignatureAlgorithm, signingKey interface{}) (recipientSigInfo, error) { | ||||
| 	switch signingKey := signingKey.(type) { | ||||
| 	case ed25519.PrivateKey: | ||||
| 		return newEd25519Signer(alg, signingKey) | ||||
| 	case *rsa.PrivateKey: | ||||
| 		return newRSASigner(alg, signingKey) | ||||
| 	case *ecdsa.PrivateKey: | ||||
| 		return newECDSASigner(alg, signingKey) | ||||
| 	case []byte: | ||||
| 		return newSymmetricSigner(alg, signingKey) | ||||
| 	case JSONWebKey: | ||||
| 		return newJWKSigner(alg, signingKey) | ||||
| 	case *JSONWebKey: | ||||
| 		return newJWKSigner(alg, *signingKey) | ||||
| 	} | ||||
| 	if signer, ok := signingKey.(OpaqueSigner); ok { | ||||
| 		return newOpaqueSigner(alg, signer) | ||||
| 	} | ||||
| 	return recipientSigInfo{}, ErrUnsupportedKeyType | ||||
| } | ||||
| 
 | ||||
| func newJWKSigner(alg SignatureAlgorithm, signingKey JSONWebKey) (recipientSigInfo, error) { | ||||
| 	recipient, err := makeJWSRecipient(alg, signingKey.Key) | ||||
| 	if err != nil { | ||||
| 		return recipientSigInfo{}, err | ||||
| 	} | ||||
| 	if recipient.publicKey != nil && recipient.publicKey() != nil { | ||||
| 		// recipient.publicKey is a JWK synthesized for embedding when recipientSigInfo
 | ||||
| 		// was created for the inner key (such as a RSA or ECDSA public key). It contains
 | ||||
| 		// the pub key for embedding, but doesn't have extra params like key id.
 | ||||
| 		publicKey := signingKey | ||||
| 		publicKey.Key = recipient.publicKey().Key | ||||
| 		recipient.publicKey = staticPublicKey(&publicKey) | ||||
| 
 | ||||
| 		// This should be impossible, but let's check anyway.
 | ||||
| 		if !recipient.publicKey().IsPublic() { | ||||
| 			return recipientSigInfo{}, errors.New("square/go-jose: public key was unexpectedly not public") | ||||
| 		} | ||||
| 	} | ||||
| 	return recipient, nil | ||||
| } | ||||
| 
 | ||||
| func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) { | ||||
| 	obj := &JSONWebSignature{} | ||||
| 	obj.payload = payload | ||||
| 	obj.Signatures = make([]Signature, len(ctx.recipients)) | ||||
| 
 | ||||
| 	for i, recipient := range ctx.recipients { | ||||
| 		protected := map[HeaderKey]interface{}{ | ||||
| 			headerAlgorithm: string(recipient.sigAlg), | ||||
| 		} | ||||
| 
 | ||||
| 		if recipient.publicKey != nil && recipient.publicKey() != nil { | ||||
| 			// We want to embed the JWK or set the kid header, but not both. Having a protected
 | ||||
| 			// header that contains an embedded JWK while also simultaneously containing the kid
 | ||||
| 			// header is confusing, and at least in ACME the two are considered to be mutually
 | ||||
| 			// exclusive. The fact that both can exist at the same time is a somewhat unfortunate
 | ||||
| 			// result of the JOSE spec. We've decided that this library will only include one or
 | ||||
| 			// the other to avoid this confusion.
 | ||||
| 			//
 | ||||
| 			// See https://github.com/square/go-jose/issues/157 for more context.
 | ||||
| 			if ctx.embedJWK { | ||||
| 				protected[headerJWK] = recipient.publicKey() | ||||
| 			} else { | ||||
| 				keyID := recipient.publicKey().KeyID | ||||
| 				if keyID != "" { | ||||
| 					protected[headerKeyID] = keyID | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if ctx.nonceSource != nil { | ||||
| 			nonce, err := ctx.nonceSource.Nonce() | ||||
| 			if err != nil { | ||||
| 				return nil, fmt.Errorf("square/go-jose: Error generating nonce: %v", err) | ||||
| 			} | ||||
| 			protected[headerNonce] = nonce | ||||
| 		} | ||||
| 
 | ||||
| 		for k, v := range ctx.extraHeaders { | ||||
| 			protected[k] = v | ||||
| 		} | ||||
| 
 | ||||
| 		serializedProtected := mustSerializeJSON(protected) | ||||
| 		needsBase64 := true | ||||
| 
 | ||||
| 		if b64, ok := protected[headerB64]; ok { | ||||
| 			if needsBase64, ok = b64.(bool); !ok { | ||||
| 				return nil, errors.New("square/go-jose: Invalid b64 header parameter") | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		var input bytes.Buffer | ||||
| 
 | ||||
| 		input.WriteString(base64.RawURLEncoding.EncodeToString(serializedProtected)) | ||||
| 		input.WriteByte('.') | ||||
| 
 | ||||
| 		if needsBase64 { | ||||
| 			input.WriteString(base64.RawURLEncoding.EncodeToString(payload)) | ||||
| 		} else { | ||||
| 			input.Write(payload) | ||||
| 		} | ||||
| 
 | ||||
| 		signatureInfo, err := recipient.signer.signPayload(input.Bytes(), recipient.sigAlg) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		signatureInfo.protected = &rawHeader{} | ||||
| 		for k, v := range protected { | ||||
| 			b, err := json.Marshal(v) | ||||
| 			if err != nil { | ||||
| 				return nil, fmt.Errorf("square/go-jose: Error marshalling item %#v: %v", k, err) | ||||
| 			} | ||||
| 			(*signatureInfo.protected)[k] = makeRawMessage(b) | ||||
| 		} | ||||
| 		obj.Signatures[i] = signatureInfo | ||||
| 	} | ||||
| 
 | ||||
| 	return obj, nil | ||||
| } | ||||
| 
 | ||||
| func (ctx *genericSigner) Options() SignerOptions { | ||||
| 	return SignerOptions{ | ||||
| 		NonceSource:  ctx.nonceSource, | ||||
| 		EmbedJWK:     ctx.embedJWK, | ||||
| 		ExtraHeaders: ctx.extraHeaders, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Verify validates the signature on the object and returns the payload.
 | ||||
| // This function does not support multi-signature, if you desire multi-sig
 | ||||
| // verification use VerifyMulti instead.
 | ||||
| //
 | ||||
| // Be careful when verifying signatures based on embedded JWKs inside the
 | ||||
| // payload header. You cannot assume that the key received in a payload is
 | ||||
| // trusted.
 | ||||
| func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error) { | ||||
| 	err := obj.DetachedVerify(obj.payload, verificationKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return obj.payload, nil | ||||
| } | ||||
| 
 | ||||
| // UnsafePayloadWithoutVerification returns the payload without
 | ||||
| // verifying it. The content returned from this function cannot be
 | ||||
| // trusted.
 | ||||
| func (obj JSONWebSignature) UnsafePayloadWithoutVerification() []byte { | ||||
| 	return obj.payload | ||||
| } | ||||
| 
 | ||||
| // DetachedVerify validates a detached signature on the given payload. In
 | ||||
| // most cases, you will probably want to use Verify instead. DetachedVerify
 | ||||
| // is only useful if you have a payload and signature that are separated from
 | ||||
| // each other.
 | ||||
| func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey interface{}) error { | ||||
| 	verifier, err := newVerifier(verificationKey) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if len(obj.Signatures) > 1 { | ||||
| 		return errors.New("square/go-jose: too many signatures in payload; expecting only one") | ||||
| 	} | ||||
| 
 | ||||
| 	signature := obj.Signatures[0] | ||||
| 	headers := signature.mergedHeaders() | ||||
| 	critical, err := headers.getCritical() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	for _, name := range critical { | ||||
| 		if !supportedCritical[name] { | ||||
| 			return ErrCryptoFailure | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	input, err := obj.computeAuthData(payload, &signature) | ||||
| 	if err != nil { | ||||
| 		return ErrCryptoFailure | ||||
| 	} | ||||
| 
 | ||||
| 	alg := headers.getSignatureAlgorithm() | ||||
| 	err = verifier.verifyPayload(input, signature.Signature, alg) | ||||
| 	if err == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	return ErrCryptoFailure | ||||
| } | ||||
| 
 | ||||
| // VerifyMulti validates (one of the multiple) signatures on the object and
 | ||||
| // returns the index of the signature that was verified, along with the signature
 | ||||
| // object and the payload. We return the signature and index to guarantee that
 | ||||
| // callers are getting the verified value.
 | ||||
| func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signature, []byte, error) { | ||||
| 	idx, sig, err := obj.DetachedVerifyMulti(obj.payload, verificationKey) | ||||
| 	if err != nil { | ||||
| 		return -1, Signature{}, nil, err | ||||
| 	} | ||||
| 	return idx, sig, obj.payload, nil | ||||
| } | ||||
| 
 | ||||
| // DetachedVerifyMulti validates a detached signature on the given payload with
 | ||||
| // a signature/object that has potentially multiple signers. This returns the index
 | ||||
| // of the signature that was verified, along with the signature object. We return
 | ||||
| // the signature and index to guarantee that callers are getting the verified value.
 | ||||
| //
 | ||||
| // In most cases, you will probably want to use Verify or VerifyMulti instead.
 | ||||
| // DetachedVerifyMulti is only useful if you have a payload and signature that are
 | ||||
| // separated from each other, and the signature can have multiple signers at the
 | ||||
| // same time.
 | ||||
| func (obj JSONWebSignature) DetachedVerifyMulti(payload []byte, verificationKey interface{}) (int, Signature, error) { | ||||
| 	verifier, err := newVerifier(verificationKey) | ||||
| 	if err != nil { | ||||
| 		return -1, Signature{}, err | ||||
| 	} | ||||
| 
 | ||||
| outer: | ||||
| 	for i, signature := range obj.Signatures { | ||||
| 		headers := signature.mergedHeaders() | ||||
| 		critical, err := headers.getCritical() | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		for _, name := range critical { | ||||
| 			if !supportedCritical[name] { | ||||
| 				continue outer | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		input, err := obj.computeAuthData(payload, &signature) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		alg := headers.getSignatureAlgorithm() | ||||
| 		err = verifier.verifyPayload(input, signature.Signature, alg) | ||||
| 		if err == nil { | ||||
| 			return i, signature, nil | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return -1, Signature{}, ErrCryptoFailure | ||||
| } | ||||
|  | @ -1,482 +0,0 @@ | |||
| /*- | ||||
|  * Copyright 2014 Square Inc. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| package jose | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/aes" | ||||
| 	"crypto/cipher" | ||||
| 	"crypto/hmac" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/sha256" | ||||
| 	"crypto/sha512" | ||||
| 	"crypto/subtle" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"hash" | ||||
| 	"io" | ||||
| 
 | ||||
| 	"golang.org/x/crypto/pbkdf2" | ||||
| 	"gopkg.in/square/go-jose.v2/cipher" | ||||
| ) | ||||
| 
 | ||||
| // Random reader (stubbed out in tests)
 | ||||
| var RandReader = rand.Reader | ||||
| 
 | ||||
| const ( | ||||
| 	// RFC7518 recommends a minimum of 1,000 iterations:
 | ||||
| 	// https://tools.ietf.org/html/rfc7518#section-4.8.1.2
 | ||||
| 	// NIST recommends a minimum of 10,000:
 | ||||
| 	// https://pages.nist.gov/800-63-3/sp800-63b.html
 | ||||
| 	// 1Password uses 100,000:
 | ||||
| 	// https://support.1password.com/pbkdf2/
 | ||||
| 	defaultP2C = 100000 | ||||
| 	// Default salt size: 128 bits
 | ||||
| 	defaultP2SSize = 16 | ||||
| ) | ||||
| 
 | ||||
| // Dummy key cipher for shared symmetric key mode
 | ||||
| type symmetricKeyCipher struct { | ||||
| 	key []byte // Pre-shared content-encryption key
 | ||||
| 	p2c int    // PBES2 Count
 | ||||
| 	p2s []byte // PBES2 Salt Input
 | ||||
| } | ||||
| 
 | ||||
| // Signer/verifier for MAC modes
 | ||||
| type symmetricMac struct { | ||||
| 	key []byte | ||||
| } | ||||
| 
 | ||||
| // Input/output from an AEAD operation
 | ||||
| type aeadParts struct { | ||||
| 	iv, ciphertext, tag []byte | ||||
| } | ||||
| 
 | ||||
| // A content cipher based on an AEAD construction
 | ||||
| type aeadContentCipher struct { | ||||
| 	keyBytes     int | ||||
| 	authtagBytes int | ||||
| 	getAead      func(key []byte) (cipher.AEAD, error) | ||||
| } | ||||
| 
 | ||||
| // Random key generator
 | ||||
| type randomKeyGenerator struct { | ||||
| 	size int | ||||
| } | ||||
| 
 | ||||
| // Static key generator
 | ||||
| type staticKeyGenerator struct { | ||||
| 	key []byte | ||||
| } | ||||
| 
 | ||||
| // Create a new content cipher based on AES-GCM
 | ||||
| func newAESGCM(keySize int) contentCipher { | ||||
| 	return &aeadContentCipher{ | ||||
| 		keyBytes:     keySize, | ||||
| 		authtagBytes: 16, | ||||
| 		getAead: func(key []byte) (cipher.AEAD, error) { | ||||
| 			aes, err := aes.NewCipher(key) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 
 | ||||
| 			return cipher.NewGCM(aes) | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Create a new content cipher based on AES-CBC+HMAC
 | ||||
| func newAESCBC(keySize int) contentCipher { | ||||
| 	return &aeadContentCipher{ | ||||
| 		keyBytes:     keySize * 2, | ||||
| 		authtagBytes: keySize, | ||||
| 		getAead: func(key []byte) (cipher.AEAD, error) { | ||||
| 			return josecipher.NewCBCHMAC(key, aes.NewCipher) | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Get an AEAD cipher object for the given content encryption algorithm
 | ||||
| func getContentCipher(alg ContentEncryption) contentCipher { | ||||
| 	switch alg { | ||||
| 	case A128GCM: | ||||
| 		return newAESGCM(16) | ||||
| 	case A192GCM: | ||||
| 		return newAESGCM(24) | ||||
| 	case A256GCM: | ||||
| 		return newAESGCM(32) | ||||
| 	case A128CBC_HS256: | ||||
| 		return newAESCBC(16) | ||||
| 	case A192CBC_HS384: | ||||
| 		return newAESCBC(24) | ||||
| 	case A256CBC_HS512: | ||||
| 		return newAESCBC(32) | ||||
| 	default: | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // getPbkdf2Params returns the key length and hash function used in
 | ||||
| // pbkdf2.Key.
 | ||||
| func getPbkdf2Params(alg KeyAlgorithm) (int, func() hash.Hash) { | ||||
| 	switch alg { | ||||
| 	case PBES2_HS256_A128KW: | ||||
| 		return 16, sha256.New | ||||
| 	case PBES2_HS384_A192KW: | ||||
| 		return 24, sha512.New384 | ||||
| 	case PBES2_HS512_A256KW: | ||||
| 		return 32, sha512.New | ||||
| 	default: | ||||
| 		panic("invalid algorithm") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // getRandomSalt generates a new salt of the given size.
 | ||||
| func getRandomSalt(size int) ([]byte, error) { | ||||
| 	salt := make([]byte, size) | ||||
| 	_, err := io.ReadFull(RandReader, salt) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return salt, nil | ||||
| } | ||||
| 
 | ||||
| // newSymmetricRecipient creates a JWE encrypter based on AES-GCM key wrap.
 | ||||
| func newSymmetricRecipient(keyAlg KeyAlgorithm, key []byte) (recipientKeyInfo, error) { | ||||
| 	switch keyAlg { | ||||
| 	case DIRECT, A128GCMKW, A192GCMKW, A256GCMKW, A128KW, A192KW, A256KW: | ||||
| 	case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW: | ||||
| 	default: | ||||
| 		return recipientKeyInfo{}, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	return recipientKeyInfo{ | ||||
| 		keyAlg: keyAlg, | ||||
| 		keyEncrypter: &symmetricKeyCipher{ | ||||
| 			key: key, | ||||
| 		}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // newSymmetricSigner creates a recipientSigInfo based on the given key.
 | ||||
| func newSymmetricSigner(sigAlg SignatureAlgorithm, key []byte) (recipientSigInfo, error) { | ||||
| 	// Verify that key management algorithm is supported by this encrypter
 | ||||
| 	switch sigAlg { | ||||
| 	case HS256, HS384, HS512: | ||||
| 	default: | ||||
| 		return recipientSigInfo{}, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	return recipientSigInfo{ | ||||
| 		sigAlg: sigAlg, | ||||
| 		signer: &symmetricMac{ | ||||
| 			key: key, | ||||
| 		}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // Generate a random key for the given content cipher
 | ||||
| func (ctx randomKeyGenerator) genKey() ([]byte, rawHeader, error) { | ||||
| 	key := make([]byte, ctx.size) | ||||
| 	_, err := io.ReadFull(RandReader, key) | ||||
| 	if err != nil { | ||||
| 		return nil, rawHeader{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	return key, rawHeader{}, nil | ||||
| } | ||||
| 
 | ||||
| // Key size for random generator
 | ||||
| func (ctx randomKeyGenerator) keySize() int { | ||||
| 	return ctx.size | ||||
| } | ||||
| 
 | ||||
| // Generate a static key (for direct mode)
 | ||||
| func (ctx staticKeyGenerator) genKey() ([]byte, rawHeader, error) { | ||||
| 	cek := make([]byte, len(ctx.key)) | ||||
| 	copy(cek, ctx.key) | ||||
| 	return cek, rawHeader{}, nil | ||||
| } | ||||
| 
 | ||||
| // Key size for static generator
 | ||||
| func (ctx staticKeyGenerator) keySize() int { | ||||
| 	return len(ctx.key) | ||||
| } | ||||
| 
 | ||||
| // Get key size for this cipher
 | ||||
| func (ctx aeadContentCipher) keySize() int { | ||||
| 	return ctx.keyBytes | ||||
| } | ||||
| 
 | ||||
| // Encrypt some data
 | ||||
| func (ctx aeadContentCipher) encrypt(key, aad, pt []byte) (*aeadParts, error) { | ||||
| 	// Get a new AEAD instance
 | ||||
| 	aead, err := ctx.getAead(key) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Initialize a new nonce
 | ||||
| 	iv := make([]byte, aead.NonceSize()) | ||||
| 	_, err = io.ReadFull(RandReader, iv) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	ciphertextAndTag := aead.Seal(nil, iv, pt, aad) | ||||
| 	offset := len(ciphertextAndTag) - ctx.authtagBytes | ||||
| 
 | ||||
| 	return &aeadParts{ | ||||
| 		iv:         iv, | ||||
| 		ciphertext: ciphertextAndTag[:offset], | ||||
| 		tag:        ciphertextAndTag[offset:], | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // Decrypt some data
 | ||||
| func (ctx aeadContentCipher) decrypt(key, aad []byte, parts *aeadParts) ([]byte, error) { | ||||
| 	aead, err := ctx.getAead(key) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if len(parts.iv) != aead.NonceSize() || len(parts.tag) < ctx.authtagBytes { | ||||
| 		return nil, ErrCryptoFailure | ||||
| 	} | ||||
| 
 | ||||
| 	return aead.Open(nil, parts.iv, append(parts.ciphertext, parts.tag...), aad) | ||||
| } | ||||
| 
 | ||||
| // Encrypt the content encryption key.
 | ||||
| func (ctx *symmetricKeyCipher) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) { | ||||
| 	switch alg { | ||||
| 	case DIRECT: | ||||
| 		return recipientInfo{ | ||||
| 			header: &rawHeader{}, | ||||
| 		}, nil | ||||
| 	case A128GCMKW, A192GCMKW, A256GCMKW: | ||||
| 		aead := newAESGCM(len(ctx.key)) | ||||
| 
 | ||||
| 		parts, err := aead.encrypt(ctx.key, []byte{}, cek) | ||||
| 		if err != nil { | ||||
| 			return recipientInfo{}, err | ||||
| 		} | ||||
| 
 | ||||
| 		header := &rawHeader{} | ||||
| 		header.set(headerIV, newBuffer(parts.iv)) | ||||
| 		header.set(headerTag, newBuffer(parts.tag)) | ||||
| 
 | ||||
| 		return recipientInfo{ | ||||
| 			header:       header, | ||||
| 			encryptedKey: parts.ciphertext, | ||||
| 		}, nil | ||||
| 	case A128KW, A192KW, A256KW: | ||||
| 		block, err := aes.NewCipher(ctx.key) | ||||
| 		if err != nil { | ||||
| 			return recipientInfo{}, err | ||||
| 		} | ||||
| 
 | ||||
| 		jek, err := josecipher.KeyWrap(block, cek) | ||||
| 		if err != nil { | ||||
| 			return recipientInfo{}, err | ||||
| 		} | ||||
| 
 | ||||
| 		return recipientInfo{ | ||||
| 			encryptedKey: jek, | ||||
| 			header:       &rawHeader{}, | ||||
| 		}, nil | ||||
| 	case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW: | ||||
| 		if len(ctx.p2s) == 0 { | ||||
| 			salt, err := getRandomSalt(defaultP2SSize) | ||||
| 			if err != nil { | ||||
| 				return recipientInfo{}, err | ||||
| 			} | ||||
| 			ctx.p2s = salt | ||||
| 		} | ||||
| 
 | ||||
| 		if ctx.p2c <= 0 { | ||||
| 			ctx.p2c = defaultP2C | ||||
| 		} | ||||
| 
 | ||||
| 		// salt is UTF8(Alg) || 0x00 || Salt Input
 | ||||
| 		salt := bytes.Join([][]byte{[]byte(alg), ctx.p2s}, []byte{0x00}) | ||||
| 
 | ||||
| 		// derive key
 | ||||
| 		keyLen, h := getPbkdf2Params(alg) | ||||
| 		key := pbkdf2.Key(ctx.key, salt, ctx.p2c, keyLen, h) | ||||
| 
 | ||||
| 		// use AES cipher with derived key
 | ||||
| 		block, err := aes.NewCipher(key) | ||||
| 		if err != nil { | ||||
| 			return recipientInfo{}, err | ||||
| 		} | ||||
| 
 | ||||
| 		jek, err := josecipher.KeyWrap(block, cek) | ||||
| 		if err != nil { | ||||
| 			return recipientInfo{}, err | ||||
| 		} | ||||
| 
 | ||||
| 		header := &rawHeader{} | ||||
| 		header.set(headerP2C, ctx.p2c) | ||||
| 		header.set(headerP2S, newBuffer(ctx.p2s)) | ||||
| 
 | ||||
| 		return recipientInfo{ | ||||
| 			encryptedKey: jek, | ||||
| 			header:       header, | ||||
| 		}, nil | ||||
| 	} | ||||
| 
 | ||||
| 	return recipientInfo{}, ErrUnsupportedAlgorithm | ||||
| } | ||||
| 
 | ||||
| // Decrypt the content encryption key.
 | ||||
| func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) { | ||||
| 	switch headers.getAlgorithm() { | ||||
| 	case DIRECT: | ||||
| 		cek := make([]byte, len(ctx.key)) | ||||
| 		copy(cek, ctx.key) | ||||
| 		return cek, nil | ||||
| 	case A128GCMKW, A192GCMKW, A256GCMKW: | ||||
| 		aead := newAESGCM(len(ctx.key)) | ||||
| 
 | ||||
| 		iv, err := headers.getIV() | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("square/go-jose: invalid IV: %v", err) | ||||
| 		} | ||||
| 		tag, err := headers.getTag() | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("square/go-jose: invalid tag: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		parts := &aeadParts{ | ||||
| 			iv:         iv.bytes(), | ||||
| 			ciphertext: recipient.encryptedKey, | ||||
| 			tag:        tag.bytes(), | ||||
| 		} | ||||
| 
 | ||||
| 		cek, err := aead.decrypt(ctx.key, []byte{}, parts) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		return cek, nil | ||||
| 	case A128KW, A192KW, A256KW: | ||||
| 		block, err := aes.NewCipher(ctx.key) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return cek, nil | ||||
| 	case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW: | ||||
| 		p2s, err := headers.getP2S() | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("square/go-jose: invalid P2S: %v", err) | ||||
| 		} | ||||
| 		if p2s == nil || len(p2s.data) == 0 { | ||||
| 			return nil, fmt.Errorf("square/go-jose: invalid P2S: must be present") | ||||
| 		} | ||||
| 
 | ||||
| 		p2c, err := headers.getP2C() | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("square/go-jose: invalid P2C: %v", err) | ||||
| 		} | ||||
| 		if p2c <= 0 { | ||||
| 			return nil, fmt.Errorf("square/go-jose: invalid P2C: must be a positive integer") | ||||
| 		} | ||||
| 
 | ||||
| 		// salt is UTF8(Alg) || 0x00 || Salt Input
 | ||||
| 		alg := headers.getAlgorithm() | ||||
| 		salt := bytes.Join([][]byte{[]byte(alg), p2s.bytes()}, []byte{0x00}) | ||||
| 
 | ||||
| 		// derive key
 | ||||
| 		keyLen, h := getPbkdf2Params(alg) | ||||
| 		key := pbkdf2.Key(ctx.key, salt, p2c, keyLen, h) | ||||
| 
 | ||||
| 		// use AES cipher with derived key
 | ||||
| 		block, err := aes.NewCipher(key) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return cek, nil | ||||
| 	} | ||||
| 
 | ||||
| 	return nil, ErrUnsupportedAlgorithm | ||||
| } | ||||
| 
 | ||||
| // Sign the given payload
 | ||||
| func (ctx symmetricMac) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) { | ||||
| 	mac, err := ctx.hmac(payload, alg) | ||||
| 	if err != nil { | ||||
| 		return Signature{}, errors.New("square/go-jose: failed to compute hmac") | ||||
| 	} | ||||
| 
 | ||||
| 	return Signature{ | ||||
| 		Signature: mac, | ||||
| 		protected: &rawHeader{}, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // Verify the given payload
 | ||||
| func (ctx symmetricMac) verifyPayload(payload []byte, mac []byte, alg SignatureAlgorithm) error { | ||||
| 	expected, err := ctx.hmac(payload, alg) | ||||
| 	if err != nil { | ||||
| 		return errors.New("square/go-jose: failed to compute hmac") | ||||
| 	} | ||||
| 
 | ||||
| 	if len(mac) != len(expected) { | ||||
| 		return errors.New("square/go-jose: invalid hmac") | ||||
| 	} | ||||
| 
 | ||||
| 	match := subtle.ConstantTimeCompare(mac, expected) | ||||
| 	if match != 1 { | ||||
| 		return errors.New("square/go-jose: invalid hmac") | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Compute the HMAC based on the given alg value
 | ||||
| func (ctx symmetricMac) hmac(payload []byte, alg SignatureAlgorithm) ([]byte, error) { | ||||
| 	var hash func() hash.Hash | ||||
| 
 | ||||
| 	switch alg { | ||||
| 	case HS256: | ||||
| 		hash = sha256.New | ||||
| 	case HS384: | ||||
| 		hash = sha512.New384 | ||||
| 	case HS512: | ||||
| 		hash = sha512.New | ||||
| 	default: | ||||
| 		return nil, ErrUnsupportedAlgorithm | ||||
| 	} | ||||
| 
 | ||||
| 	hmac := hmac.New(hash, ctx.key) | ||||
| 
 | ||||
| 	// According to documentation, Write() on hash never fails
 | ||||
| 	_, _ = hmac.Write(payload) | ||||
| 	return hmac.Sum(nil), nil | ||||
| } | ||||
|  | @ -77,8 +77,8 @@ github.com/coredns/coredns/plugin/pkg/transport | |||
| github.com/coredns/coredns/plugin/pkg/uniq | ||||
| github.com/coredns/coredns/plugin/test | ||||
| github.com/coredns/coredns/request | ||||
| # github.com/coreos/go-oidc/v3 v3.4.0 | ||||
| ## explicit; go 1.14 | ||||
| # github.com/coreos/go-oidc/v3 v3.6.0 | ||||
| ## explicit; go 1.19 | ||||
| github.com/coreos/go-oidc/v3/oidc | ||||
| # github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf | ||||
| ## explicit | ||||
|  | @ -246,10 +246,6 @@ github.com/pkg/errors | |||
| # github.com/pmezard/go-difflib v1.0.0 | ||||
| ## explicit | ||||
| github.com/pmezard/go-difflib/difflib | ||||
| # github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 | ||||
| ## explicit | ||||
| github.com/pquerna/cachecontrol | ||||
| github.com/pquerna/cachecontrol/cacheobject | ||||
| # github.com/prometheus/client_golang v1.13.0 | ||||
| ## explicit; go 1.17 | ||||
| github.com/prometheus/client_golang/prometheus | ||||
|  | @ -381,7 +377,6 @@ golang.org/x/mod/semver | |||
| ## explicit; go 1.17 | ||||
| golang.org/x/net/bpf | ||||
| golang.org/x/net/context | ||||
| golang.org/x/net/context/ctxhttp | ||||
| golang.org/x/net/http/httpguts | ||||
| golang.org/x/net/http2 | ||||
| golang.org/x/net/http2/hpack | ||||
|  | @ -396,7 +391,7 @@ golang.org/x/net/ipv6 | |||
| golang.org/x/net/proxy | ||||
| golang.org/x/net/trace | ||||
| golang.org/x/net/websocket | ||||
| # golang.org/x/oauth2 v0.4.0 | ||||
| # golang.org/x/oauth2 v0.6.0 | ||||
| ## explicit; go 1.17 | ||||
| golang.org/x/oauth2 | ||||
| golang.org/x/oauth2/internal | ||||
|  | @ -554,18 +549,9 @@ google.golang.org/protobuf/types/known/timestamppb | |||
| google.golang.org/protobuf/types/known/wrapperspb | ||||
| # gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c | ||||
| ## explicit; go 1.11 | ||||
| # gopkg.in/coreos/go-oidc.v2 v2.2.1 | ||||
| ## explicit | ||||
| gopkg.in/coreos/go-oidc.v2 | ||||
| # gopkg.in/natefinch/lumberjack.v2 v2.0.0 | ||||
| ## explicit | ||||
| gopkg.in/natefinch/lumberjack.v2 | ||||
| # gopkg.in/square/go-jose.v2 v2.6.0 | ||||
| ## explicit | ||||
| gopkg.in/square/go-jose.v2 | ||||
| gopkg.in/square/go-jose.v2/cipher | ||||
| gopkg.in/square/go-jose.v2/json | ||||
| gopkg.in/square/go-jose.v2/jwt | ||||
| # gopkg.in/yaml.v2 v2.4.0 | ||||
| ## explicit; go 1.15 | ||||
| gopkg.in/yaml.v2 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue