internal/credentials: fix a bug and add one more helper function SPIFFEIDFromCert (#3929)

* internal/credentials: fix a bug and add one more helper function
diff --git a/internal/credentials/spiffe.go b/internal/credentials/spiffe.go
index b66e3db..be70b6c 100644
--- a/internal/credentials/spiffe.go
+++ b/internal/credentials/spiffe.go
@@ -25,6 +25,7 @@
 
 import (
 	"crypto/tls"
+	"crypto/x509"
 	"net/url"
 
 	"google.golang.org/grpc/grpclog"
@@ -38,8 +39,17 @@
 	if len(state.PeerCertificates) == 0 || len(state.PeerCertificates[0].URIs) == 0 {
 		return nil
 	}
+	return SPIFFEIDFromCert(state.PeerCertificates[0])
+}
+
+// SPIFFEIDFromCert parses the SPIFFE ID from x509.Certificate. If the SPIFFE
+// ID format is invalid, return nil with warning.
+func SPIFFEIDFromCert(cert *x509.Certificate) *url.URL {
+	if cert == nil || cert.URIs == nil {
+		return nil
+	}
 	var spiffeID *url.URL
-	for _, uri := range state.PeerCertificates[0].URIs {
+	for _, uri := range cert.URIs {
 		if uri == nil || uri.Scheme != "spiffe" || uri.Opaque != "" || (uri.User != nil && uri.User.Username() != "") {
 			continue
 		}
@@ -48,7 +58,7 @@
 			logger.Warning("invalid SPIFFE ID: total ID length larger than 2048 bytes")
 			return nil
 		}
-		if len(uri.Host) == 0 || len(uri.RawPath) == 0 || len(uri.Path) == 0 {
+		if len(uri.Host) == 0 || len(uri.Path) == 0 {
 			logger.Warning("invalid SPIFFE ID: domain or workload ID is empty")
 			return nil
 		}
@@ -57,7 +67,7 @@
 			return nil
 		}
 		// A valid SPIFFE certificate can only have exactly one URI SAN field.
-		if len(state.PeerCertificates[0].URIs) > 1 {
+		if len(cert.URIs) > 1 {
 			logger.Warning("invalid SPIFFE ID: multiple URI SANs")
 			return nil
 		}
diff --git a/internal/credentials/spiffe_test.go b/internal/credentials/spiffe_test.go
index 324874e..599481a 100644
--- a/internal/credentials/spiffe_test.go
+++ b/internal/credentials/spiffe_test.go
@@ -21,12 +21,17 @@
 import (
 	"crypto/tls"
 	"crypto/x509"
+	"encoding/pem"
+	"io/ioutil"
 	"net/url"
 	"testing"
 
 	"google.golang.org/grpc/internal/grpctest"
+	"google.golang.org/grpc/testdata"
 )
 
+const wantURI = "spiffe://foo.bar.com/client/workload/1"
+
 type s struct {
 	grpctest.Tester
 }
@@ -40,12 +45,12 @@
 		name string
 		urls []*url.URL
 		// If we expect a SPIFFE ID to be returned.
-		expectID bool
+		wantID bool
 	}{
 		{
-			name:     "empty URIs",
-			urls:     []*url.URL{},
-			expectID: false,
+			name:   "empty URIs",
+			urls:   []*url.URL{},
+			wantID: false,
 		},
 		{
 			name: "good SPIFFE ID",
@@ -57,7 +62,7 @@
 					RawPath: "workload/wl1",
 				},
 			},
-			expectID: true,
+			wantID: true,
 		},
 		{
 			name: "invalid host",
@@ -69,7 +74,7 @@
 					RawPath: "workload/wl1",
 				},
 			},
-			expectID: false,
+			wantID: false,
 		},
 		{
 			name: "invalid path",
@@ -81,7 +86,7 @@
 					RawPath: "",
 				},
 			},
-			expectID: false,
+			wantID: false,
 		},
 		{
 			name: "large path",
@@ -93,7 +98,7 @@
 					RawPath: string(make([]byte, 2050)),
 				},
 			},
-			expectID: false,
+			wantID: false,
 		},
 		{
 			name: "large host",
@@ -105,7 +110,7 @@
 					RawPath: "workload/wl1",
 				},
 			},
-			expectID: false,
+			wantID: false,
 		},
 		{
 			name: "multiple URI SANs",
@@ -129,7 +134,7 @@
 					RawPath: "workload/wl1",
 				},
 			},
-			expectID: false,
+			wantID: false,
 		},
 		{
 			name: "multiple URI SANs without SPIFFE ID",
@@ -147,7 +152,7 @@
 					RawPath: "workload/wl1",
 				},
 			},
-			expectID: false,
+			wantID: false,
 		},
 		{
 			name: "multiple URI SANs with one SPIFFE ID",
@@ -165,15 +170,63 @@
 					RawPath: "workload/wl1",
 				},
 			},
-			expectID: false,
+			wantID: false,
 		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			state := tls.ConnectionState{PeerCertificates: []*x509.Certificate{{URIs: tt.urls}}}
 			id := SPIFFEIDFromState(state)
-			if got, want := id != nil, tt.expectID; got != want {
-				t.Errorf("want expectID = %v, but SPIFFE ID is %v", want, id)
+			if got, want := id != nil, tt.wantID; got != want {
+				t.Errorf("want wantID = %v, but SPIFFE ID is %v", want, id)
+			}
+		})
+	}
+}
+
+func (s) TestSPIFFEIDFromCert(t *testing.T) {
+	tests := []struct {
+		name     string
+		dataPath string
+		// If we expect a SPIFFE ID to be returned.
+		wantID bool
+	}{
+		{
+			name:     "good certificate with SPIFFE ID",
+			dataPath: "x509/spiffe_cert.pem",
+			wantID:   true,
+		},
+		{
+			name:     "bad certificate with SPIFFE ID and another URI",
+			dataPath: "x509/multiple_uri_cert.pem",
+			wantID:   false,
+		},
+		{
+			name:     "certificate without SPIFFE ID",
+			dataPath: "x509/client1_cert.pem",
+			wantID:   false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			data, err := ioutil.ReadFile(testdata.Path(tt.dataPath))
+			if err != nil {
+				t.Fatalf("ioutil.ReadFile(%s) failed: %v", testdata.Path(tt.dataPath), err)
+			}
+			block, _ := pem.Decode(data)
+			if block == nil {
+				t.Fatalf("Failed to parse the certificate: byte block is nil")
+			}
+			cert, err := x509.ParseCertificate(block.Bytes)
+			if err != nil {
+				t.Fatalf("x509.ParseCertificate(%b) failed: %v", block.Bytes, err)
+			}
+			uri := SPIFFEIDFromCert(cert)
+			if (uri != nil) != tt.wantID {
+				t.Fatalf("wantID got and want mismatch, got %t, want %t", uri != nil, tt.wantID)
+			}
+			if uri != nil && uri.String() != wantURI {
+				t.Fatalf("SPIFFE ID not expected, got %s, want %s", uri.String(), wantURI)
 			}
 		})
 	}
diff --git a/testdata/x509/create.sh b/testdata/x509/create.sh
index ecfcfd1..5bd3c58 100755
--- a/testdata/x509/create.sh
+++ b/testdata/x509/create.sh
@@ -100,5 +100,24 @@
   -extensions test_client
 openssl verify -verbose -CAfile client_ca_cert.pem  client2_cert.pem
 
+# Generate a cert with SPIFFE ID.
+openssl req -x509                                                         \
+  -newkey rsa:4096                                                        \
+  -keyout spiffe_key.pem                                                  \
+  -out spiffe_cert.pem                                                    \
+  -nodes                                                                  \
+  -days 3650                                                              \
+  -subj /C=US/ST=CA/L=SVL/O=gRPC/CN=test-client1/                         \
+  -addext "subjectAltName = URI:spiffe://foo.bar.com/client/workload/1"
+
+# Generate a cert with SPIFFE ID and another SAN URI field(which doesn't meet SPIFFE specs).
+openssl req -x509                                                         \
+  -newkey rsa:4096                                                        \
+  -keyout multiple_uri_key.pem                                            \
+  -out multiple_uri_cert.pem                                              \
+  -nodes                                                                  \
+  -days 3650                                                              \
+  -subj /C=US/ST=CA/L=SVL/O=gRPC/CN=test-client1/                         \
+  -addext "subjectAltName = URI:spiffe://foo.bar.com/client/workload/1, URI:https://bar.baz.com/client"
 # Cleanup the CSRs.
 rm *_csr.pem
diff --git a/testdata/x509/multiple_uri_cert.pem b/testdata/x509/multiple_uri_cert.pem
new file mode 100644
index 0000000..9763799
--- /dev/null
+++ b/testdata/x509/multiple_uri_cert.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIFzjCCA7agAwIBAgIUI8r7b2hX9DRwEQGWuRdk32eU5kowDQYJKoZIhvcNAQEL
+BQAwTjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANTVkwxDTAL
+BgNVBAoMBGdSUEMxFTATBgNVBAMMDHRlc3QtY2xpZW50MTAeFw0yMDEwMDcwNjQx
+MTRaFw0zMDEwMDUwNjQxMTRaME4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEM
+MAoGA1UEBwwDU1ZMMQ0wCwYDVQQKDARnUlBDMRUwEwYDVQQDDAx0ZXN0LWNsaWVu
+dDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCm/zjNYkfCTcq7tnVf
+qkPEde1+M6s2z05iWDfoBeZfC2NwUxIBqAC6XTXTxqYSjEVRCQUzjVxyWQNiwuz7
+pK/xGZhP/Ih2uSQKTw8vkXay4HCOt9DR0S/XGcQNImdbawKgnGven8Jrg8UZDXrt
+9R9Z0nRajB1eXvXOsEEoEfOnYthc6P+MxWJc0lnfaTlowyEgv84Ha13y1h46W6yC
++WNBT/kWqp/mzDTv/Ima8xcqEft9VUZ82qJ1DVt1064x8KOzm2x7F7QSIcjxr39M
+fbASm8Vdnt10XfhdsDVkxTlBJs8WKGn0uw8MyPNjFG01OpYDHLAfJTL3XlvaUjfF
+yDMFsRDVjfkuYIkqAVWQ7eleFfOFBYzaVf2K+2OvCR+vGAPa5NQ59kwogJYLjV/O
+43axChBizcPM0p7gmRhhO7TQz7LLTea30rBJ/YtXdxFR11y9Jdq+i2KwWi8O50iO
+hzxUBkbcQD/W9Bcn7gOkD/pgEGynWvFSs+UHjLeyL0COk0NiuYIMlOgwtI5BGwzD
+bdLuTU/ZQm4BJBjEIGVHFqKyTqUXcw5t9fWxH8V0XNs8zqj9J7lvNKu9b88GnyaJ
+fKMdDO4rVTJHmvFDHP9MUJHC9SabW8+hK0nuU7n3+Pc07ToCAan+Ych5bQHsRMjI
+9EvxKVNfwIwNrmRr3mhbOU9xIQIDAQABo4GjMIGgMB0GA1UdDgQWBBS6jnt9IccJ
+SOuE1KwP68VCBPB4hTAfBgNVHSMEGDAWgBS6jnt9IccJSOuE1KwP68VCBPB4hTAP
+BgNVHRMBAf8EBTADAQH/ME0GA1UdEQRGMESGJnNwaWZmZTovL2Zvby5iYXIuY29t
+L2NsaWVudC93b3JrbG9hZC8xhhpodHRwczovL2Jhci5iYXouY29tL2NsaWVudDAN
+BgkqhkiG9w0BAQsFAAOCAgEAoR4LbmvtSXLiVg7BRilvSxIWgcG6AI75/afuaM20
+PUTpyDhnrPxEaytb5LP0w42BCMoIHXDLE0Jmbxqbi+ku/Qw1R6723J7gwRSUYIg1
+a2S5Gue4AFp7aSLDUZhl0jPphq7OMKozzH5TrDgjKljYjPURClc/ODSlGdzOqlif
+CbDHwrCorb+BFM3aFDE0pF06pnMDXcn/Ob9QCLIpvZEOWe/fJbPtTUiA5cY3knne
+regyhvfqfVZtU52qg+9o6q5QchVqOt19alAsISK9/H4iVE+S79AiYEAU4yM4S6p5
+VW44idy3KXmr5kyVwJhe3t9f5Ckuswmo6hL32ec6M52ElrS8Er0vFt4bjfNgq996
+lTm4/reL/Anko9chQiGBe7F8J82OfxjLoVH9CbZjIoS4LiZPkey3Ze9HUV1sHhM/
+umkL54jRsVjEwwSCIcF9onzmiD8D7FV3AQ9W/RbBF3wZvVBBs9ZKQCxek2pZX/eZ
+Q+BvXwG7NGArowpqbi+tSrW3O+XZzY7nXbbf23jCBwkBn3jvqn1Kwsr/T/HbXUaz
+dDUvkwgyrX7NfvvZ20svtKLlBZTO5D8P9fy0+cHsS0XkPhw6UbHk396hoOmVZ+OG
+E5uVb2sBy+vx+82IwVzWN0o7380AEmAA5nrA6fMaxTxmo07pOF7avAZ34LgHJIjr
+sTM=
+-----END CERTIFICATE-----
diff --git a/testdata/x509/multiple_uri_key.pem b/testdata/x509/multiple_uri_key.pem
new file mode 100644
index 0000000..c2918fd
--- /dev/null
+++ b/testdata/x509/multiple_uri_key.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+zJOG+48NgohVKfBhGN7JyxV1dq/X
+-----END PRIVATE KEY-----
diff --git a/testdata/x509/spiffe_cert.pem b/testdata/x509/spiffe_cert.pem
new file mode 100644
index 0000000..0ce0a1a
--- /dev/null
+++ b/testdata/x509/spiffe_cert.pem
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFsjCCA5qgAwIBAgIUPxiyjwxoDyMeRvl4g9TSdvLlCA0wDQYJKoZIhvcNAQEL
+BQAwTjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANTVkwxDTAL
+BgNVBAoMBGdSUEMxFTATBgNVBAMMDHRlc3QtY2xpZW50MTAeFw0yMDEwMDYxNzI2
+MzFaFw0zMDEwMDQxNzI2MzFaME4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEM
+MAoGA1UEBwwDU1ZMMQ0wCwYDVQQKDARnUlBDMRUwEwYDVQQDDAx0ZXN0LWNsaWVu
+dDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCV84YR/EV55qfFynHh
+QvWEZW5hUI9q0DeD5kG5CarrkOj11rZuQIBZ7X23CJbeoVbrvbYLghsPYJzxS/n3
+Qlwwzb5k+L0Qt+HrBD836HcSK5k1oh0jGGMaGownap+XCZH9g52s/8iiwfI02CmN
+TbwsNp7wtSEFgNOd2OlzhT6wBLF2Q6uxfBmsDpiChxe2Fs1lyan9RH8fYEf7sxwP
+E+SgBfEs7dSG5ZwFfdF+pd1T3IfrVjIxechKO1MO7HTSxbOTj6eHf1NeErDTGPA7
+VrnDCupRgcDGyAhFd54r62R8TbTjn5MwzMxElO45Ck/Ej7Qw/GWeaBHj/dMa6mhE
+R55PvnKuyj+k9t0Rf0HDZyONtY5/OLqI/xVr27Y1o9v5FysNgjWPkZMRpvuCzkeC
+2RuE6k2TfBDRLiCyYu/Zzw+ZtUyTAKtWtefLdQBjrYpnhrDPpmrnTWomX/e9pylE
+WfkyxCswiPnDw7ypI7uFSTkz0+bUaROmAtlPvR+3SjaQDWigwz3eJsdIaeg5AY9q
+//rWaal6l2iR0Ou9L6A9lLxh5iN/ch+OGk4QPK6pFbOy3IqYfmQ+IpAXG0da9RT2
+EN76cNa3bldEjRRON8oQ3HZmhOQJqVxhQciUz84sTjAqH8WvqqbdG9HKUoZ19T5Z
+9vNldjlQn33Mi5gBxdugqdnmCQIDAQABo4GHMIGEMB0GA1UdDgQWBBT8rr0kPapk
+bGLJ4EU1582sw7WlOTAfBgNVHSMEGDAWgBT8rr0kPapkbGLJ4EU1582sw7WlOTAP
+BgNVHRMBAf8EBTADAQH/MDEGA1UdEQQqMCiGJnNwaWZmZTovL2Zvby5iYXIuY29t
+L2NsaWVudC93b3JrbG9hZC8xMA0GCSqGSIb3DQEBCwUAA4ICAQA15Ne+Lz5cN1/B
+fkys4QHDWJ0n5Zy9OtwSW6aTyqIIwls6OOSkJn3qJMoT2oFvoHoOxb0swyN+zUoD
+pmPEd7FHkMm8BhRqoyH3UZGR7kOSIIcfvldVZbW9mD88A04qvLsWkkanMyGhkYV4
+0TXyb8USdjeNm1H32iF4k24czSpvoOYo9HOQv+4aFcqTMnGwS7CvwU6O6vVU8gIy
+HYP/oWnkhap6X7acjPxYoW5IDZdN9vdMz9wQlKlc799lWqOCuwl68NSuTNcNNFyn
+TXfFWZaghb7iXsUezGYTY9glsPxY0Egmbcmxut0gz0U2BNVvNGKUUu55MlAS7yXO
+Y7eTfSSf6DJesFQKwTg8qlyNLjzbLSmhvz6EPV55ToUxPPA9CIOrWQwXv4GdySuH
+bwof3U5p/cq2NDtxv8KGisjK04l++s+Ea8AS6T6O8+08nBFGgfNW331eWtU91JoQ
+e6Q4DWipiNzkIvISk48V8CT9eRB2KD7NsigQprePRN3gDZREh+01gwbVUX2gbtHx
+1RGxEjO6H0kUuaoXF5E6+WGwgn8MA47qUy1WXC5QDFpc5LyaoVaMFv8bcoWSNXAS
+Oes+ZDWDXWq6F+9Kt0zWmO651cVquLTjmgt48fgL6m8rU13ikjH7dFnimrwRxfOD
+p+z97N7TvWfgE1HOmYDfsbaHjPFZKg==
+-----END CERTIFICATE-----
diff --git a/testdata/x509/spiffe_key.pem b/testdata/x509/spiffe_key.pem
new file mode 100644
index 0000000..5462d66
--- /dev/null
+++ b/testdata/x509/spiffe_key.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+Xgln2zbN6APAb+dGDdv27dz4YlasU4s=
+-----END PRIVATE KEY-----