func (ks *Keyserver) verifyUpdateEdge(req *proto.UpdateRequest) error { if len(req.Update.NewEntry.Index) != vrf.Size { return fmt.Errorf("index '%x' has wrong length (expected %d)", req.Update.NewEntry.Index, vrf.Size) } prevUpdate, err := ks.getUpdate(req.Update.NewEntry.Index, math.MaxUint64) if err != nil { log.Print(err) return fmt.Errorf("internal error") } if prevUpdate == nil { // registration: check email proof if !ks.insecureSkipEmailProof { email, payload, err := dkim.CheckEmailProof(req.DKIMProof, ks.emailProofToAddr, ks.emailProofSubjectPrefix, ks.lookupTXT, ks.clk.Now) if err != nil { return fmt.Errorf("failed to verify DKIM proof: %s", err) } if got, want := email, req.LookupParameters.UserId; got != want { return fmt.Errorf("requested user ID does not match the email proof: %q != %q", got, want) } lastAtIndex := strings.LastIndex(req.LookupParameters.UserId, "@") if lastAtIndex == -1 { return fmt.Errorf("requested user id is not a valid email address: %q", req.LookupParameters.UserId) } if _, ok := ks.emailProofAllowedDomains[req.LookupParameters.UserId[lastAtIndex+1:]]; !ok { return fmt.Errorf("domain not in registration whitelist: %q", req.LookupParameters.UserId[lastAtIndex+1:]) } entryHash, err := base64.StdEncoding.DecodeString(payload) if err != nil { return fmt.Errorf("bad base64 in email proof: %q", payload) } var entryHashProposed [32]byte sha3.ShakeSum256(entryHashProposed[:], req.Update.NewEntry.Encoding) if !bytes.Equal(entryHashProposed[:], entryHash[:]) { return fmt.Errorf("email proof does not match requested entry: %s vs %s (%x)", base64.StdEncoding.EncodeToString(entryHashProposed[:]), payload, req.Update.NewEntry.Encoding) } } } return ks.verifyUpdateDeterministic(prevUpdate, req) }
func (ks *Keyserver) verifyUpdateEdge(req *proto.UpdateRequest) error { if len(req.Update.NewEntry.Index) != vrf.Size { return fmt.Errorf("index '%x' has wrong length (expected %d)", req.Update.NewEntry.Index, vrf.Size) } prevUpdate, err := ks.getUpdate(req.Update.NewEntry.Index, math.MaxUint64) if err != nil { log.Print(err) return fmt.Errorf("internal error") } if prevUpdate == nil { // registration: check email proof if !ks.insecureSkipEmailProof { if req.EmailProof == nil { return fmt.Errorf("No email proof provided") } lastAtIndex := strings.LastIndex(req.LookupParameters.UserId, "@") if lastAtIndex == -1 { return fmt.Errorf("requested user id is not a valid email address: %q", req.LookupParameters.UserId) } // determine registration type switch t := req.EmailProof.ProofType.(type) { case *proto.EmailProof_DKIMProof: if _, ok := ks.dkimProofAllowedDomains[req.LookupParameters.UserId[lastAtIndex+1:]]; !ok { return fmt.Errorf("domain not in registration whitelist: %q", req.LookupParameters.UserId[lastAtIndex+1:]) } email, payload, err := dkim.CheckEmailProof(t.DKIMProof, ks.dkimProofToAddr, ks.dkimProofToAddr, ks.lookupTXT, ks.clk.Now) if err != nil { return fmt.Errorf("failed to verify DKIM proof: %s", err) } if got, want := email, req.LookupParameters.UserId; got != want { return fmt.Errorf("requested user ID does not match the email proof: %q != %q", got, want) } entryHash, err := base64.StdEncoding.DecodeString(payload) if err != nil { return fmt.Errorf("bad base64 in email proof: %q", payload) } var entryHashProposed [32]byte sha3.ShakeSum256(entryHashProposed[:], req.Update.NewEntry.Encoding) if !bytes.Equal(entryHashProposed[:], entryHash[:]) { return fmt.Errorf("email proof does not match requested entry: %s vs %s (%x)", base64.StdEncoding.EncodeToString(entryHashProposed[:]), payload, req.Update.NewEntry.Encoding) } case *proto.EmailProof_OIDCToken: found := false for _, oc := range ks.oidcProofConfig { if _, ok := oc.allowedDomains[req.LookupParameters.UserId[lastAtIndex+1:]]; !ok { continue } found = true email, err := oc.oidcClient.VerifyIDToken(t.OIDCToken) if err != nil { if _, ok := err.(*oidc.ErrExpired); ok { return &errExpired{err: err} } return err } if got, want := email, req.LookupParameters.UserId; got != want { return fmt.Errorf("requested user ID does not match the email proof: %q != %q", got, want) } } if !found { return fmt.Errorf("domain not in registration whitelist: %q", req.LookupParameters.UserId[lastAtIndex+1:]) } case *proto.EmailProof_SAMLResponse: if _, ok := ks.samlProofAllowedDomains[req.LookupParameters.UserId[lastAtIndex+1:]]; !ok { return fmt.Errorf("domain not in registration whitelist: %q", req.LookupParameters.UserId[lastAtIndex+1:]) } email, err := saml.VerifySAMLResponse(t.SAMLResponse, ks.samlProofIDPCert, ks.samlProofConsumerServiceURL, "EmailAddress", ks.samlProofValidity) if err != nil { if _, ok := err.(*saml.ErrExpired); ok { return &errExpired{err: err} } return err } if got, want := email, req.LookupParameters.UserId; got != want { return fmt.Errorf("requested user ID does not match the email proof: %q != %q", got, want) } default: return fmt.Errorf("Invalid email proof type: %T", t) } } } return ks.verifyUpdateDeterministic(prevUpdate, req) }