go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/tokenserver/appengine/impl/utils/tokensigning/inspector_test.go (about)

     1  // Copyright 2017 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 tokensigning
    16  
    17  import (
    18  	"context"
    19  	"encoding/base64"
    20  	"testing"
    21  	"time"
    22  
    23  	"google.golang.org/protobuf/proto"
    24  
    25  	"go.chromium.org/luci/common/clock"
    26  	"go.chromium.org/luci/common/clock/testclock"
    27  	"go.chromium.org/luci/server/auth/delegation/messages"
    28  	"go.chromium.org/luci/server/auth/signing"
    29  	"go.chromium.org/luci/server/auth/signing/signingtest"
    30  
    31  	. "github.com/smartystreets/goconvey/convey"
    32  	. "go.chromium.org/luci/common/testing/assertions"
    33  )
    34  
    35  func TestInspectToken(t *testing.T) {
    36  	t.Parallel()
    37  
    38  	ctx := context.Background()
    39  	ctx, tc := testclock.UseTime(ctx, testclock.TestTimeUTC)
    40  
    41  	signer := signingtest.NewSigner(&signing.ServiceInfo{
    42  		ServiceAccountName: "service@example.com",
    43  	})
    44  	inspector := inspectorForTest(signer, "")
    45  
    46  	original := &messages.Subtoken{
    47  		DelegatedIdentity: "user:delegated@example.com",
    48  		RequestorIdentity: "user:requestor@example.com",
    49  		CreationTime:      clock.Now(ctx).Unix(),
    50  		ValidityDuration:  3600,
    51  		Audience:          []string{"*"},
    52  		Services:          []string{"*"},
    53  	}
    54  	good, _ := signerForTest(signer, "").SignToken(ctx, original)
    55  
    56  	Convey("Happy path", t, func() {
    57  		ins, err := inspector.InspectToken(ctx, good)
    58  		So(err, ShouldBeNil)
    59  		So(ins.Signed, ShouldBeTrue)
    60  		So(ins.NonExpired, ShouldBeTrue)
    61  		So(ins.InvalidityReason, ShouldEqual, "")
    62  		So(ins.Envelope, ShouldHaveSameTypeAs, &messages.DelegationToken{})
    63  		So(ins.Body, ShouldResembleProto, original)
    64  	})
    65  
    66  	Convey("Not base64", t, func() {
    67  		ins, err := inspector.InspectToken(ctx, "@@@@@@@@@@@@@")
    68  		So(err, ShouldBeNil)
    69  		So(ins, ShouldResemble, &Inspection{
    70  			InvalidityReason: "not base64 - illegal base64 data at input byte 0",
    71  		})
    72  	})
    73  
    74  	Convey("Not valid envelope proto", t, func() {
    75  		ins, err := inspector.InspectToken(ctx, "zzzz")
    76  		So(err, ShouldBeNil)
    77  		So(ins.InvalidityReason, ShouldStartWith, "can't unmarshal the envelope - proto")
    78  	})
    79  
    80  	Convey("Bad signature", t, func() {
    81  		env, _, _ := deserializeForTest(ctx, good, signer)
    82  		env.Pkcs1Sha256Sig = []byte("lalala")
    83  		blob, _ := proto.Marshal(env)
    84  		tok := base64.RawURLEncoding.EncodeToString(blob)
    85  
    86  		ins, err := inspector.InspectToken(ctx, tok)
    87  		So(err, ShouldBeNil)
    88  		So(ins.Signed, ShouldBeFalse)
    89  		So(ins.NonExpired, ShouldBeTrue)
    90  		So(ins.InvalidityReason, ShouldEqual, "bad signature - crypto/rsa: verification error")
    91  		So(ins.Envelope, ShouldHaveSameTypeAs, &messages.DelegationToken{})
    92  		So(ins.Body, ShouldResembleProto, original) // recovered the token body nonetheless
    93  	})
    94  
    95  	Convey("Wrong SigningContext", t, func() {
    96  		inspectorWithCtx := inspectorForTest(signer, "Some context")
    97  
    98  		// Symptoms are same as in "Bad signature".
    99  		ins, err := inspectorWithCtx.InspectToken(ctx, good)
   100  		So(err, ShouldBeNil)
   101  		So(ins.Signed, ShouldBeFalse)
   102  		So(ins.NonExpired, ShouldBeTrue)
   103  		So(ins.InvalidityReason, ShouldEqual, "bad signature - crypto/rsa: verification error")
   104  		So(ins.Envelope, ShouldHaveSameTypeAs, &messages.DelegationToken{})
   105  		So(ins.Body, ShouldResembleProto, original) // recovered the token body nonetheless
   106  	})
   107  
   108  	Convey("Expired", t, func() {
   109  		tc.Add(2 * time.Hour)
   110  
   111  		ins, err := inspector.InspectToken(ctx, good)
   112  		So(err, ShouldBeNil)
   113  		So(ins.Signed, ShouldBeTrue)
   114  		So(ins.NonExpired, ShouldBeFalse)
   115  		So(ins.InvalidityReason, ShouldEqual, "expired")
   116  		So(ins.Envelope, ShouldHaveSameTypeAs, &messages.DelegationToken{})
   117  		So(ins.Body, ShouldResembleProto, original)
   118  	})
   119  }
   120  
   121  func inspectorForTest(certs CertificatesSupplier, signingCtx string) *Inspector {
   122  	return &Inspector{
   123  		Certificates:   certs,
   124  		SigningContext: signingCtx,
   125  		Envelope:       func() proto.Message { return &messages.DelegationToken{} },
   126  		Body:           func() proto.Message { return &messages.Subtoken{} },
   127  		Unwrap: func(e proto.Message) Unwrapped {
   128  			env := e.(*messages.DelegationToken)
   129  			return Unwrapped{
   130  				Body:         env.SerializedSubtoken,
   131  				RsaSHA256Sig: env.Pkcs1Sha256Sig,
   132  				KeyID:        env.SigningKeyId,
   133  			}
   134  		},
   135  		Lifespan: func(b proto.Message) Lifespan {
   136  			body := b.(*messages.Subtoken)
   137  			return Lifespan{
   138  				NotBefore: time.Unix(body.CreationTime, 0),
   139  				NotAfter:  time.Unix(body.CreationTime+int64(body.ValidityDuration), 0),
   140  			}
   141  		},
   142  	}
   143  }