// TestMacaroonPaperFig6 implements an example flow as described in the macaroons paper: // http://theory.stanford.edu/~ataly/Papers/macaroons.pdf // There are three services, ts, fs, as: // ts is a storage service which has deligated authority to a forum service fs. // The forum service wants to require its users to be logged into to an authentication service as. // // The client obtains a macaroon from fs (minted by ts, with a third party caveat addressed to as). // The client obtains a discharge macaroon from as to satisfy this caveat. // The target service verifies the original macaroon it delegated to fs // No direct contact between as and ts is required func (s *ServiceSuite) TestMacaroonPaperFig6(c *gc.C) { locator := make(bakery.PublicKeyLocatorMap) as := newService(c, "as-loc", locator) ts := newService(c, "ts-loc", locator) fs := newService(c, "fs-loc", locator) // ts creates a macaroon. tsMacaroon, err := ts.NewMacaroon("", nil, nil) c.Assert(err, gc.IsNil) // ts somehow sends the macaroon to fs which adds a third party caveat to be discharged by as. err = fs.AddCaveat(tsMacaroon, checkers.Caveat{Location: "as-loc", Condition: "user==bob"}) c.Assert(err, gc.IsNil) // client asks for a discharge macaroon for each third party caveat d, err := bakery.DischargeAll(tsMacaroon, func(firstPartyLocation string, cav macaroon.Caveat) (*macaroon.Macaroon, error) { c.Assert(firstPartyLocation, gc.Equals, "ts-loc") c.Assert(cav.Location, gc.Equals, "as-loc") mac, err := as.Discharge(strcmpChecker("user==bob"), cav.Id) c.Assert(err, gc.IsNil) return mac, nil }) c.Assert(err, gc.IsNil) err = ts.Check(d, strcmpChecker("")) c.Assert(err, gc.IsNil) }
// TestMacaroonPaperFig6FailsWithBindingOnTamperedSignature runs a similar test as TestMacaroonPaperFig6 // with the discharge macaroon binding being done on a tampered signature. func (s *ServiceSuite) TestMacaroonPaperFig6FailsWithBindingOnTamperedSignature(c *gc.C) { locator := make(bakery.PublicKeyLocatorMap) as := newService(c, "as-loc", locator) ts := newService(c, "ts-loc", locator) fs := newService(c, "fs-loc", locator) // ts creates a macaroon. tsMacaroon, err := ts.NewMacaroon("", nil, nil) c.Assert(err, gc.IsNil) // ts somehow sends the macaroon to fs which adds a third party caveat to be discharged by as. err = fs.AddCaveat(tsMacaroon, checkers.Caveat{Location: "as-loc", Condition: "user==bob"}) c.Assert(err, gc.IsNil) // client asks for a discharge macaroon for each third party caveat d, err := bakery.DischargeAll(tsMacaroon, func(firstPartyLocation string, cav macaroon.Caveat) (*macaroon.Macaroon, error) { c.Assert(firstPartyLocation, gc.Equals, "ts-loc") c.Assert(cav.Location, gc.Equals, "as-loc") mac, err := as.Discharge(strcmpChecker("user==bob"), cav.Id) c.Assert(err, gc.IsNil) return mac, nil }) c.Assert(err, gc.IsNil) // client has all the discharge macaroons. For each discharge macaroon bind it to our tsMacaroon // and add it to our request. for _, dm := range d[1:] { dm.Bind([]byte("tampered-signature")) // Bind against an incorrect signature. } // client makes request to ts. err = ts.Check(d, strcmpChecker("")) c.Assert(err, gc.ErrorMatches, "verification failed: signature mismatch after caveat verification") }
func (*DischargeSuite) TestDischargeAllManyDischarges(c *gc.C) { rootKey := []byte("root key") m0, err := macaroon.New(rootKey, "id0", "location0") c.Assert(err, gc.IsNil) totalRequired := 40 id := 1 addCaveats := func(m *macaroon.Macaroon) { for i := 0; i < 2; i++ { if totalRequired == 0 { break } cid := fmt.Sprint("id", id) err := m.AddThirdPartyCaveat([]byte("root key "+cid), cid, "somewhere") c.Assert(err, gc.IsNil) id++ totalRequired-- } } addCaveats(m0) getDischarge := func(loc string, cav macaroon.Caveat) (*macaroon.Macaroon, error) { c.Assert(loc, gc.Equals, "location0") m, err := macaroon.New([]byte("root key "+cav.Id), cav.Id, "") c.Assert(err, gc.IsNil) addCaveats(m) return m, nil } ms, err := bakery.DischargeAll(m0, getDischarge) c.Assert(err, gc.IsNil) c.Assert(ms, gc.HasLen, 41) err = ms[0].Verify(rootKey, alwaysOK, ms[1:]) c.Assert(err, gc.IsNil) }
func (*DischargeSuite) TestDischargeAllNoDischarges(c *gc.C) { rootKey := []byte("root key") m, err := macaroon.New(rootKey, "id0", "loc0") c.Assert(err, gc.IsNil) ms, err := bakery.DischargeAll(m, noDischarge(c)) c.Assert(err, gc.IsNil) c.Assert(ms, gc.HasLen, 1) c.Assert(ms[0], gc.Equals, m) err = m.Verify(rootKey, alwaysOK, nil) c.Assert(err, gc.IsNil) }
func (s *ServiceSuite) TestDischargeTwoNeedDeclared(c *gc.C) { locator := make(bakery.PublicKeyLocatorMap) firstParty := newService(c, "first", locator) thirdParty := newService(c, "third", locator) // firstParty mints a macaroon with two third party caveats // with overlapping attributes. m, err := firstParty.NewMacaroon("", nil, []checkers.Caveat{ checkers.NeedDeclaredCaveat(checkers.Caveat{ Location: "third", Condition: "x", }, "foo", "bar"), checkers.NeedDeclaredCaveat(checkers.Caveat{ Location: "third", Condition: "y", }, "bar", "baz"), }) c.Assert(err, gc.IsNil) // The client asks for a discharge macaroon for each third party caveat. // Since no declarations are added by the discharger, d, err := bakery.DischargeAll(m, func(_ string, cav macaroon.Caveat) (*macaroon.Macaroon, error) { return thirdParty.Discharge(bakery.ThirdPartyCheckerFunc(func(_, caveat string) ([]checkers.Caveat, error) { return nil, nil }), cav.Id) }) c.Assert(err, gc.IsNil) declared := checkers.InferDeclared(d) c.Assert(declared, gc.DeepEquals, checkers.Declared{ "foo": "", "bar": "", "baz": "", }) err = firstParty.Check(d, checkers.New(declared)) c.Assert(err, gc.IsNil) // If they return conflicting values, the discharge fails. // The client asks for a discharge macaroon for each third party caveat. // Since no declarations are added by the discharger, d, err = bakery.DischargeAll(m, func(_ string, cav macaroon.Caveat) (*macaroon.Macaroon, error) { return thirdParty.Discharge(bakery.ThirdPartyCheckerFunc(func(_, caveat string) ([]checkers.Caveat, error) { switch caveat { case "x": return []checkers.Caveat{ checkers.DeclaredCaveat("foo", "fooval1"), }, nil case "y": return []checkers.Caveat{ checkers.DeclaredCaveat("foo", "fooval2"), checkers.DeclaredCaveat("baz", "bazval"), }, nil } return nil, fmt.Errorf("not matched") }), cav.Id) }) c.Assert(err, gc.IsNil) declared = checkers.InferDeclared(d) c.Assert(declared, gc.DeepEquals, checkers.Declared{ "bar": "", "baz": "bazval", }) err = firstParty.Check(d, checkers.New(declared)) c.Assert(err, gc.ErrorMatches, `verification failed: caveat "declared foo fooval1" not satisfied: got foo=null, expected "fooval1"`) }
func (s *ServiceSuite) TestNeedDeclared(c *gc.C) { locator := make(bakery.PublicKeyLocatorMap) firstParty := newService(c, "first", locator) thirdParty := newService(c, "third", locator) // firstParty mints a macaroon with a third-party caveat addressed // to thirdParty with a need-declared caveat. m, err := firstParty.NewMacaroon("", nil, []checkers.Caveat{ checkers.NeedDeclaredCaveat(checkers.Caveat{ Location: "third", Condition: "something", }, "foo", "bar"), }) c.Assert(err, gc.IsNil) // The client asks for a discharge macaroon for each third party caveat. d, err := bakery.DischargeAll(m, func(_ string, cav macaroon.Caveat) (*macaroon.Macaroon, error) { return thirdParty.Discharge(strcmpChecker("something"), cav.Id) }) c.Assert(err, gc.IsNil) // The required declared attributes should have been added // to the discharge macaroons. declared := checkers.InferDeclared(d) c.Assert(declared, gc.DeepEquals, checkers.Declared{ "foo": "", "bar": "", }) // Make sure the macaroons actually check out correctly // when provided with the declared checker. err = firstParty.Check(d, checkers.New(declared)) c.Assert(err, gc.IsNil) // Try again when the third party does add a required declaration. // The client asks for a discharge macaroon for each third party caveat. d, err = bakery.DischargeAll(m, func(_ string, cav macaroon.Caveat) (*macaroon.Macaroon, error) { checker := thirdPartyCheckerWithCaveats{ checkers.DeclaredCaveat("foo", "a"), checkers.DeclaredCaveat("arble", "b"), } return thirdParty.Discharge(checker, cav.Id) }) c.Assert(err, gc.IsNil) // One attribute should have been added, the other was already there. declared = checkers.InferDeclared(d) c.Assert(declared, gc.DeepEquals, checkers.Declared{ "foo": "a", "bar": "", "arble": "b", }) err = firstParty.Check(d, checkers.New(declared)) c.Assert(err, gc.IsNil) // Try again, but this time pretend a client is sneakily trying // to add another "declared" attribute to alter the declarations. d, err = bakery.DischargeAll(m, func(_ string, cav macaroon.Caveat) (*macaroon.Macaroon, error) { checker := thirdPartyCheckerWithCaveats{ checkers.DeclaredCaveat("foo", "a"), checkers.DeclaredCaveat("arble", "b"), } m, err := thirdParty.Discharge(checker, cav.Id) c.Assert(err, gc.IsNil) // Sneaky client adds a first party caveat. m.AddFirstPartyCaveat(checkers.DeclaredCaveat("foo", "c").Condition) return m, nil }) c.Assert(err, gc.IsNil) declared = checkers.InferDeclared(d) c.Assert(declared, gc.DeepEquals, checkers.Declared{ "bar": "", "arble": "b", }) err = firstParty.Check(d, checkers.New(declared)) c.Assert(err, gc.ErrorMatches, `verification failed: caveat "declared foo a" not satisfied: got foo=null, expected "a"`) }