go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/auth/signing/certs_test.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package signing 16 17 import ( 18 "context" 19 "encoding/pem" 20 "fmt" 21 "net/http" 22 "testing" 23 24 "go.chromium.org/luci/server/auth/internal" 25 "go.chromium.org/luci/server/caching" 26 27 . "github.com/smartystreets/goconvey/convey" 28 . "go.chromium.org/luci/common/testing/assertions" 29 ) 30 31 var certBlob = `-----BEGIN CERTIFICATE----- 32 MIIBDjCBu6ADAgECAgEBMAsGCSqGSIb3DQEBCzAAMCAXDTAxMDkwOTAxNDY0MFoY 33 DzIyODYxMTIwMTc0NjQwWjAAMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMGYtc/k 34 vp1Sr2zZFWPu534tqX9chKxhADlLbPR4A+ojKl/EchYCV6DE7Ikogx02PFpYZe3A 35 3a4hccSufwr3wtMCAwEAAaMgMB4wDgYDVR0PAQH/BAQDAgCAMAwGA1UdEwEB/wQC 36 MAAwCwYJKoZIhvcNAQELA0EAI/3v5eWNzA2oudenR8Vo5EY0j3zCUVhlHRErlcUR 37 I69yAHZUpJ9lzcwmHcaCJ76m/jDINZrYoL/4aSlDEGgHmw== 38 -----END CERTIFICATE----- 39 ` 40 41 func TestFetchCertificates(t *testing.T) { 42 t.Parallel() 43 44 const testURL = "https://test.example.com" 45 46 Convey("With empty cache", t, func() { 47 ctx := caching.WithEmptyProcessCache(context.Background()) 48 49 Convey("Works", func() { 50 ctx := internal.WithTestTransport(ctx, func(r *http.Request, body string) (int, string) { 51 So(r.URL.String(), ShouldEqual, testURL) 52 return 200, fmt.Sprintf(`{ 53 "service_account_name": "blah@blah.com", 54 "certificates": [{ 55 "key_name": "abc", 56 "x509_certificate_pem": %q 57 }], 58 "timestamp": 1446166229439210 59 }`, certBlob) 60 }) 61 62 certs, err := FetchCertificates(ctx, testURL) 63 So(err, ShouldBeNil) 64 So(certs.ServiceAccountName, ShouldEqual, "blah@blah.com") 65 So(len(certs.Certificates), ShouldEqual, 1) 66 }) 67 68 Convey("Errors", func() { 69 ctx := internal.WithTestTransport(ctx, func(r *http.Request, body string) (int, string) { 70 return 401, "fail" 71 }) 72 73 _, err := FetchCertificates(ctx, testURL) 74 So(err, ShouldNotBeNil) 75 }) 76 77 Convey("Bad JSON", func() { 78 ctx := internal.WithTestTransport(ctx, func(r *http.Request, body string) (int, string) { 79 return 200, fmt.Sprintf(`{ 80 "certificates": [{ 81 "key_name": "abc", 82 "x509_certificate_pem": %q 83 }], 84 "timestamp": "not an int" 85 }`, certBlob) 86 }) 87 88 _, err := FetchCertificates(ctx, testURL) 89 So(err, ShouldNotBeNil) 90 }) 91 }) 92 } 93 94 func TestFetchCertificatesForServiceAccount(t *testing.T) { 95 t.Parallel() 96 97 Convey("Works", t, func() { 98 ctx := caching.WithEmptyProcessCache(context.Background()) 99 ctx = internal.WithTestTransport(ctx, func(r *http.Request, body string) (int, string) { 100 So(r.URL.String(), ShouldEqual, "https://www.googleapis.com/robot/v1/metadata/x509/robot%40robots.gserviceaccount.com") 101 return 200, `{ 102 "0392f9886770640357cbb29e57d3698291b1e805": "-----BEGIN CERTIFICATE-----\nblah 1\n-----END CERTIFICATE-----\n", 103 "f5db308971078d1496c262cc06b6e7f87652af55": "-----BEGIN CERTIFICATE-----\nblah 2\n-----END CERTIFICATE-----\n" 104 }` 105 }) 106 107 certs, err := FetchCertificatesForServiceAccount(ctx, "robot@robots.gserviceaccount.com") 108 So(err, ShouldBeNil) 109 So(certs.ServiceAccountName, ShouldEqual, "robot@robots.gserviceaccount.com") 110 So(certs.Certificates, ShouldResemble, []Certificate{ 111 { 112 KeyName: "0392f9886770640357cbb29e57d3698291b1e805", 113 X509CertificatePEM: "-----BEGIN CERTIFICATE-----\nblah 1\n-----END CERTIFICATE-----\n", 114 }, 115 { 116 KeyName: "f5db308971078d1496c262cc06b6e7f87652af55", 117 X509CertificatePEM: "-----BEGIN CERTIFICATE-----\nblah 2\n-----END CERTIFICATE-----\n", 118 }, 119 }) 120 }) 121 } 122 123 func TestFetchGoogleOAuth2Certificates(t *testing.T) { 124 t.Parallel() 125 126 Convey("Works", t, func() { 127 ctx := caching.WithEmptyProcessCache(context.Background()) 128 ctx = internal.WithTestTransport(ctx, func(r *http.Request, body string) (int, string) { 129 So(r.URL.String(), ShouldEqual, "https://www.googleapis.com/oauth2/v1/certs") 130 return 200, `{ 131 "0392f9886770640357cbb29e57d3698291b1e805": "-----BEGIN CERTIFICATE-----\nblah 1\n-----END CERTIFICATE-----\n", 132 "f5db308971078d1496c262cc06b6e7f87652af55": "-----BEGIN CERTIFICATE-----\nblah 2\n-----END CERTIFICATE-----\n" 133 }` 134 }) 135 136 certs, err := FetchGoogleOAuth2Certificates(ctx) 137 So(err, ShouldBeNil) 138 So(certs.Certificates, ShouldResemble, []Certificate{ 139 { 140 KeyName: "0392f9886770640357cbb29e57d3698291b1e805", 141 X509CertificatePEM: "-----BEGIN CERTIFICATE-----\nblah 1\n-----END CERTIFICATE-----\n", 142 }, 143 { 144 KeyName: "f5db308971078d1496c262cc06b6e7f87652af55", 145 X509CertificatePEM: "-----BEGIN CERTIFICATE-----\nblah 2\n-----END CERTIFICATE-----\n", 146 }, 147 }) 148 }) 149 } 150 151 func TestCertificateForKey(t *testing.T) { 152 t.Parallel() 153 154 Convey("Works", t, func() { 155 certs := PublicCertificates{ 156 Certificates: []Certificate{ 157 { 158 KeyName: "abc", 159 X509CertificatePEM: certBlob, 160 }, 161 }, 162 } 163 cert, err := certs.CertificateForKey("abc") 164 So(err, ShouldBeNil) 165 So(cert, ShouldNotBeNil) 166 167 // Code coverage for cache hit. 168 cert, err = certs.CertificateForKey("abc") 169 So(err, ShouldBeNil) 170 So(cert, ShouldNotBeNil) 171 }) 172 173 Convey("Bad PEM", t, func() { 174 certs := PublicCertificates{ 175 Certificates: []Certificate{ 176 { 177 KeyName: "abc", 178 X509CertificatePEM: "not a pem", 179 }, 180 }, 181 } 182 cert, err := certs.CertificateForKey("abc") 183 So(err, ShouldErrLike, "not PEM") 184 So(cert, ShouldBeNil) 185 }) 186 187 Convey("Bad cert", t, func() { 188 certs := PublicCertificates{ 189 Certificates: []Certificate{ 190 { 191 KeyName: "abc", 192 X509CertificatePEM: string(pem.EncodeToMemory(&pem.Block{ 193 Type: "CERTIFICATE", 194 Bytes: []byte("not a certificate"), 195 })), 196 }, 197 }, 198 } 199 cert, err := certs.CertificateForKey("abc") 200 So(err, ShouldNotBeNil) 201 So(cert, ShouldBeNil) 202 }) 203 204 Convey("Missing key", t, func() { 205 certs := PublicCertificates{} 206 cert, err := certs.CertificateForKey("abc") 207 So(err, ShouldErrLike, "no such certificate") 208 So(cert, ShouldBeNil) 209 }) 210 } 211 212 func TestCheckSignature(t *testing.T) { 213 // See signingtest/signer_test.go for where this cert and signature were 214 // generated. 'signingtest' module itself can't be imported due to import 215 // cycle. 216 217 t.Parallel() 218 219 Convey("Works", t, func() { 220 certs := PublicCertificates{ 221 Certificates: []Certificate{ 222 { 223 KeyName: "abc", 224 X509CertificatePEM: certBlob, 225 }, 226 }, 227 } 228 229 blob := []byte("some blob") 230 231 signature := []byte{ 232 0x66, 0x2d, 0xa6, 0xa0, 0x65, 0x63, 0x8b, 0x83, 0xc5, 0x45, 0xeb, 0xfd, 233 0x88, 0xec, 0x9, 0x41, 0x59, 0x92, 0xd0, 0x48, 0x78, 0x37, 0xc2, 0x45, 234 0x74, 0xfc, 0x8b, 0x13, 0xa, 0xca, 0x47, 0x7d, 0xd1, 0x24, 0x2c, 0x6c, 235 0xbe, 0x3a, 0xea, 0xc5, 0x12, 0x76, 0xb4, 0xe1, 0xa9, 0x4a, 0x40, 0x40, 236 0x24, 0xf7, 0x1e, 0x7c, 0x91, 0x91, 0xe3, 0x71, 0x4f, 0x21, 0xf4, 0xe4, 237 0xec, 0x65, 0x87, 0x1c, 238 } 239 240 err := certs.CheckSignature("abc", blob, signature) 241 So(err, ShouldBeNil) 242 243 err = certs.CheckSignature("abc", blob, []byte{1, 2, 3}) 244 So(err, ShouldNotBeNil) 245 246 err = certs.CheckSignature("no key", blob, signature) 247 So(err, ShouldNotBeNil) 248 }) 249 }