github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/pvl/interp.go (about)

     1  // Copyright 2016 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package pvl
     5  
     6  import (
     7  	"bytes"
     8  	b64 "encoding/base64"
     9  	"fmt"
    10  	"net/url"
    11  	"regexp"
    12  	"strings"
    13  
    14  	"net"
    15  
    16  	"github.com/PuerkitoBio/goquery"
    17  	"github.com/keybase/client/go/jsonhelpers"
    18  	libkb "github.com/keybase/client/go/libkb"
    19  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    20  	jsonw "github.com/keybase/go-jsonw"
    21  	"github.com/miekg/dns"
    22  )
    23  
    24  // SupportedVersion is which version of PVL is supported by this client.
    25  const SupportedVersion int = 1
    26  
    27  // state of execution in a script
    28  // copies of a scriptState will point to the same internal mutable data, so be careful
    29  type scriptState struct {
    30  	WhichScript int
    31  	PC          int
    32  	Service     keybase1.ProofType
    33  	Regs        namedRegsStore
    34  	Sig         []byte
    35  	HasFetched  bool
    36  	// nil until fetched
    37  	FetchResult *fetchResult
    38  }
    39  
    40  type fetchResult struct {
    41  	fetchMode fetchMode
    42  	// One of these 3 must be filled.
    43  	String string
    44  	HTML   *goquery.Document
    45  	JSON   *jsonw.Wrapper
    46  }
    47  
    48  type regexDescriptor struct {
    49  	Template        string
    50  	CaseInsensitive bool
    51  	MultiLine       bool
    52  }
    53  
    54  type fetchMode string
    55  
    56  const (
    57  	fetchModeJSON   fetchMode = "json"
    58  	fetchModeHTML   fetchMode = "html"
    59  	fetchModeString fetchMode = "string"
    60  	fetchModeDNS    fetchMode = "dns"
    61  )
    62  
    63  type commandName string
    64  
    65  const (
    66  	cmdAssertRegexMatch    commandName = "assert_regex_match"
    67  	cmdAssertFindBase64    commandName = "assert_find_base64"
    68  	cmdAssertCompare       commandName = "assert_compare"
    69  	cmdWhitespaceNormalize commandName = "whitespace_normalize"
    70  	cmdRegexCapture        commandName = "regex_capture"
    71  	cmdReplaceAll          commandName = "replace_all"
    72  	cmdParseURL            commandName = "parse_url"
    73  	cmdFetch               commandName = "fetch"
    74  	cmdParseHTML           commandName = "parse_html"
    75  	cmdSelectorJSON        commandName = "selector_json"
    76  	cmdSelectorCSS         commandName = "selector_css"
    77  	cmdFill                commandName = "fill"
    78  )
    79  
    80  type stateMaker func(int) (scriptState, libkb.ProofError)
    81  
    82  // ProofInfo contains all the data about a proof PVL needs to check it.
    83  // It can be derived from a RemoteProofChainLink and SigHint.
    84  type ProofInfo struct {
    85  	ArmoredSig     string
    86  	Username       string
    87  	RemoteUsername string
    88  	Hostname       string
    89  	Protocol       string
    90  	APIURL         string
    91  	stubDNS        *stubDNSEngine
    92  }
    93  
    94  // NewProofInfo creates a new ProofInfo
    95  func NewProofInfo(link libkb.RemoteProofChainLink, h libkb.SigHint) ProofInfo {
    96  	return ProofInfo{
    97  		ArmoredSig:     link.GetArmoredSig(),
    98  		RemoteUsername: link.GetRemoteUsername(),
    99  		Username:       link.GetUsername(),
   100  		Hostname:       link.GetHostname(),
   101  		Protocol:       link.GetProtocol(),
   102  		APIURL:         h.GetAPIURL(),
   103  	}
   104  }
   105  
   106  // CheckProof verifies one proof by running the pvl on the provided proof information.
   107  func CheckProof(m libkb.MetaContext, pvlS string, service keybase1.ProofType, info ProofInfo) libkb.ProofError {
   108  	m1 := newMetaContext(m, info.stubDNS)
   109  	perr := checkProofInner(m1, pvlS, service, info)
   110  	if perr != nil {
   111  		debug(m1, "CheckProof failed: %v", perr)
   112  	}
   113  	if perr != nil && perr.GetProofStatus() == keybase1.ProofStatus_INVALID_PVL {
   114  		return libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   115  			"Invalid proof verification instructions! Let us know at https://github.com/keybase/keybase-issues/new")
   116  	}
   117  	return perr
   118  }
   119  
   120  func checkProofInner(m metaContext, pvlS string, service keybase1.ProofType, info ProofInfo) libkb.ProofError {
   121  	pvl, err := parse(pvlS)
   122  	if err != nil {
   123  		if strings.Contains(err.Error(), "cannot unmarshal string into Go struct") && strings.Contains(pvlS, "from iced tests") {
   124  			return libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   125  				"Corrupted pvl in merkle tree from iced tests. To fix see test/merkle_pvl.iced. : %v", err)
   126  		}
   127  		return libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   128  			"Could not parse pvl: %v", err)
   129  	}
   130  
   131  	if perr := validateChunk(m, &pvl, service); perr != nil {
   132  		return perr
   133  	}
   134  
   135  	sigBody, sigIDBase, err := libkb.OpenSig(info.ArmoredSig)
   136  	if err != nil {
   137  		return libkb.NewProofError(keybase1.ProofStatus_BAD_SIGNATURE,
   138  			"Bad signature: %v", err)
   139  	}
   140  
   141  	scripts, perr := chunkGetScripts(&pvl, service)
   142  	if perr != nil {
   143  		return perr
   144  	}
   145  
   146  	// validate hostname
   147  	webish := (service == keybase1.ProofType_DNS || service == keybase1.ProofType_GENERIC_WEB_SITE)
   148  	if webish {
   149  		if !validateDomain(info.Hostname) {
   150  			return libkb.NewProofError(keybase1.ProofStatus_BAD_SIGNATURE,
   151  				"Bad hostname in sig: %s", info.Hostname)
   152  		}
   153  	}
   154  
   155  	// validate protocol
   156  	if service == keybase1.ProofType_GENERIC_WEB_SITE {
   157  		_, ok := validateProtocol(info.Protocol, []string{"http", "https"})
   158  		if !ok {
   159  			return libkb.NewProofError(keybase1.ProofStatus_BAD_SIGNATURE,
   160  				"Bad protocol in sig: %s", info.Protocol)
   161  		}
   162  	}
   163  
   164  	sigID := sigIDBase.ToSigIDLegacy()
   165  
   166  	mknewstate := func(i int) (scriptState, libkb.ProofError) {
   167  		state := scriptState{
   168  			WhichScript: i,
   169  			PC:          0,
   170  			Service:     service,
   171  			Regs:        *newNamedRegsStore(),
   172  			Sig:         sigBody,
   173  			HasFetched:  false,
   174  			FetchResult: nil,
   175  		}
   176  
   177  		err := setupRegs(m, &state.Regs, info, sigBody, sigID, service)
   178  		return state, err
   179  	}
   180  
   181  	var errs []libkb.ProofError
   182  	if service == keybase1.ProofType_DNS {
   183  		if perr = runDNS(m, info.Hostname, scripts, mknewstate, sigID.ToMediumID()); perr != nil {
   184  			errs = append(errs, perr)
   185  		}
   186  	} else {
   187  		// Run the scripts in order.
   188  		// If any succeed, the proof succeeds.
   189  		// If one fails, the next takes over.
   190  		// If all fail, log and report errors.
   191  		for i, script := range scripts {
   192  			state, perr := mknewstate(i)
   193  			if perr != nil {
   194  				return perr
   195  			}
   196  			perr = runScript(m, &script, state)
   197  			if perr == nil {
   198  				return nil
   199  			}
   200  			errs = append(errs, perr)
   201  		}
   202  	}
   203  
   204  	switch len(errs) {
   205  	case 0:
   206  		return nil
   207  	case 1:
   208  		return errs[0]
   209  	default:
   210  		for _, err := range errs {
   211  			debug(m, "multiple failures include: %v", err)
   212  		}
   213  		// Arbitrarily use the error code of the first error
   214  		return libkb.NewProofError(errs[0].GetProofStatus(), "Multiple errors while verifying proof")
   215  	}
   216  }
   217  
   218  func setupRegs(m metaContext, regs *namedRegsStore, info ProofInfo, sigBody []byte, sigID keybase1.SigID, service keybase1.ProofType) libkb.ProofError {
   219  	webish := (service == keybase1.ProofType_DNS || service == keybase1.ProofType_GENERIC_WEB_SITE)
   220  
   221  	// hint_url
   222  	if err := regs.Set("hint_url", info.APIURL); err != nil {
   223  		return err
   224  	}
   225  
   226  	// username_service
   227  	if webish {
   228  		if err := regs.Ban("username_service"); err != nil {
   229  			return err
   230  		}
   231  	} else {
   232  		if err := regs.Set("username_service", info.RemoteUsername); err != nil {
   233  			return err
   234  		}
   235  	}
   236  
   237  	// username_keybase
   238  	if err := regs.Set("username_keybase", info.Username); err != nil {
   239  		return err
   240  	}
   241  
   242  	// sig
   243  	// Store it b64 encoded. This is rarely used, assert_find_base64 is better.
   244  	if err := regs.Set("sig", b64.StdEncoding.EncodeToString(sigBody)); err != nil {
   245  		return err
   246  	}
   247  
   248  	// sig_id_medium
   249  	if err := regs.Set("sig_id_medium", sigID.ToMediumID()); err != nil {
   250  		return err
   251  	}
   252  
   253  	// sig_id_short
   254  	if err := regs.Set("sig_id_short", sigID.ToShortID()); err != nil {
   255  		return err
   256  	}
   257  
   258  	// hostname
   259  	if webish {
   260  		if err := regs.Set("hostname", info.Hostname); err != nil {
   261  			return err
   262  		}
   263  	} else {
   264  		if err := regs.Ban("hostname"); err != nil {
   265  			return err
   266  		}
   267  	}
   268  
   269  	// protocol
   270  	if service == keybase1.ProofType_GENERIC_WEB_SITE {
   271  		canonicalProtocol, ok := validateProtocol(info.Protocol, []string{"http", "https"})
   272  		if !ok {
   273  			return libkb.NewProofError(keybase1.ProofStatus_BAD_SIGNATURE,
   274  				"Bad protocol in sig: %s", info.Protocol)
   275  		}
   276  		if err := regs.Set("protocol", canonicalProtocol); err != nil {
   277  			return err
   278  		}
   279  	} else if err := regs.Ban("protocol"); err != nil {
   280  		return err
   281  	}
   282  
   283  	return nil
   284  }
   285  
   286  // Get the list of scripts for a given service.
   287  func chunkGetScripts(pvl *pvlT, service keybase1.ProofType) ([]scriptT, libkb.ProofError) {
   288  	scripts, ok := pvl.Services.Map[service]
   289  	if !ok {
   290  		return nil, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   291  			"No entry for service: %v", service)
   292  	}
   293  	return scripts, nil
   294  }
   295  
   296  // Check that a chunk of PVL is valid code.
   297  // Will always accept valid code, but may not always notice invalidities.
   298  func validateChunk(m metaContext, pvl *pvlT, service keybase1.ProofType) libkb.ProofError {
   299  	if pvl.PvlVersion != SupportedVersion {
   300  		return libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   301  			"PVL is for the wrong version %v != %v", pvl.PvlVersion, SupportedVersion)
   302  	}
   303  
   304  	debug(m, "valid version:%v revision:%v", pvl.PvlVersion, pvl.Revision)
   305  
   306  	scripts, perr := chunkGetScripts(pvl, service)
   307  	if perr != nil {
   308  		return perr
   309  	}
   310  	if len(scripts) == 0 {
   311  		return libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   312  			"Empty scripts list for service: %v", service)
   313  	}
   314  
   315  	// Scan all the scripts (for this service) for errors. Report the first error.
   316  	var errs []libkb.ProofError
   317  	for whichscript, script := range scripts {
   318  		perr = validateScript(m, &script, service, whichscript)
   319  		errs = append(errs, perr)
   320  	}
   321  	return errs[0]
   322  }
   323  
   324  func validateScript(m metaContext, script *scriptT, service keybase1.ProofType, whichscript int) libkb.ProofError {
   325  	// Scan the script.
   326  	// Does not validate each instruction's format. (That is done when running it)
   327  	// Validate each instruction's "error" field.
   328  
   329  	logerr := func(m metaContext, service keybase1.ProofType, whichscript int, pc int, format string, arg ...interface{}) libkb.ProofError {
   330  		debugWithPosition(m, service, whichscript, pc, format, arg...)
   331  		return libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL, format, arg...)
   332  	}
   333  
   334  	var modeknown = false
   335  	var mode fetchMode
   336  	if service == keybase1.ProofType_DNS {
   337  		modeknown = true
   338  		mode = fetchModeDNS
   339  	}
   340  	if len(script.Instructions) < 1 {
   341  		return logerr(m, service, whichscript, 0, "Empty script")
   342  	}
   343  
   344  	for i, ins := range script.Instructions {
   345  		if ins.variantsFilled() != 1 {
   346  			return logerr(m, service, whichscript, i, "exactly 1 variant must appear in instruction")
   347  		}
   348  
   349  		switch {
   350  
   351  		// These can always run, but must be cases so that the default case works.
   352  		case ins.AssertRegexMatch != nil:
   353  		case ins.AssertFindBase64 != nil:
   354  		case ins.AssertCompare != nil:
   355  		case ins.WhitespaceNormalize != nil:
   356  		case ins.RegexCapture != nil:
   357  		case ins.ReplaceAll != nil:
   358  		case ins.ParseURL != nil:
   359  		case ins.Fill != nil:
   360  
   361  		case ins.Fetch != nil:
   362  			// A script can contain only <=1 fetches.
   363  			// A DNS script cannot contain fetches.
   364  
   365  			fetchType := ins.Fetch.Kind
   366  
   367  			if service == keybase1.ProofType_DNS {
   368  				return logerr(m, service, whichscript, i,
   369  					"DNS script cannot contain fetch instruction")
   370  			}
   371  			if modeknown {
   372  				return logerr(m, service, whichscript, i,
   373  					"Script cannot contain multiple fetch instructions")
   374  			}
   375  			switch fetchMode(fetchType) {
   376  			case fetchModeString:
   377  				modeknown = true
   378  				mode = fetchModeString
   379  			case fetchModeHTML:
   380  				modeknown = true
   381  				mode = fetchModeHTML
   382  			case fetchModeJSON:
   383  				modeknown = true
   384  				mode = fetchModeJSON
   385  			default:
   386  				return logerr(m, service, whichscript, i,
   387  					"Unsupported fetch type: %v", fetchType)
   388  			}
   389  		case ins.ParseHTML != nil:
   390  		case ins.SelectorJSON != nil:
   391  			// Can only select after fetching.
   392  			switch {
   393  			case service == keybase1.ProofType_DNS:
   394  				return logerr(m, service, whichscript, i,
   395  					"DNS script cannot use json selector")
   396  			case !modeknown:
   397  				return logerr(m, service, whichscript, i,
   398  					"Script cannot select before fetch")
   399  			case mode != fetchModeJSON:
   400  				return logerr(m, service, whichscript, i,
   401  					"Script contains json selector in non-html mode")
   402  			}
   403  		case ins.SelectorCSS != nil:
   404  			// Can only select one of text, attr, or data.
   405  			if ins.SelectorCSS.Attr != "" && ins.SelectorCSS.Data {
   406  				return logerr(m, service, whichscript, i,
   407  					"Script contains css selector with both 'attr' and 'data' set")
   408  			}
   409  		default:
   410  			return logerr(m, service, whichscript, i,
   411  				"Unsupported PVL instruction: %v", ins)
   412  		}
   413  	}
   414  
   415  	return nil
   416  }
   417  
   418  // Run each script on each TXT record of each domain.
   419  // Succeed if any succeed.
   420  func runDNS(m metaContext, userdomain string, scripts []scriptT, mknewstate stateMaker, sigIDMedium string) libkb.ProofError {
   421  	domains := []string{userdomain, "_keybase." + userdomain}
   422  	var errs []libkb.ProofError
   423  	for _, d := range domains {
   424  		debug(m, "Trying DNS for domain: %v", d)
   425  
   426  		err := runDNSOne(m, d, scripts, mknewstate, sigIDMedium)
   427  		if err != nil {
   428  			errs = append(errs, err)
   429  		} else {
   430  			return nil
   431  		}
   432  	}
   433  
   434  	// Return only the error for the first domain error
   435  	if len(errs) == 0 {
   436  		return nil
   437  	}
   438  	var descs []string
   439  	for _, err := range errs {
   440  		descs = append(descs, err.GetDesc())
   441  	}
   442  	// Use the code from the first error
   443  	return libkb.NewProofError(errs[0].GetProofStatus(), strings.Join(descs, "; "))
   444  }
   445  
   446  func formatDNSServer(srv string) string {
   447  	if strings.Contains(srv, ":") {
   448  		return fmt.Sprintf("[%s]:53", srv)
   449  	}
   450  	return srv + ":53"
   451  }
   452  
   453  func runDNSTXTQuery(m metaContext, domain string) (res []string, err error) {
   454  
   455  	// Attempt to use the built-in resolver first, but this might fail on mobile.
   456  	// The reason for that is currently (as of Go 1.8), LookupTXT does not properly
   457  	// use the cgo DNS routines if they are configured to be used (like they are for mobile).
   458  	// As for now, we can use a different library to specify our own name servers, since the
   459  	// Go resolver will attempt to use /etc/resolv.conf, which is not a thing on mobile.
   460  	if res, err = net.LookupTXT(domain); err != nil {
   461  		debug(m, "DNS LookupTXT failed: %s", err.Error())
   462  	} else {
   463  		return res, nil
   464  	}
   465  
   466  	// Google IPv4 and IPV6 addresses
   467  	publicServers := []string{
   468  		formatDNSServer("8.8.8.8"),
   469  		formatDNSServer("2001:4860:4860::8888"),
   470  	}
   471  	var fetchedSrvs []string
   472  	if m.G().GetDNSNameServerFetcher() != nil {
   473  		fetchedSrvs = m.G().GetDNSNameServerFetcher().GetServers()
   474  		for i := 0; i < len(fetchedSrvs); i++ {
   475  			fetchedSrvs[i] = formatDNSServer(fetchedSrvs[i])
   476  		}
   477  	}
   478  	servers := fetchedSrvs
   479  	servers = append(servers, publicServers...)
   480  
   481  	var r *dns.Msg
   482  	c := dns.Client{}
   483  	msg := dns.Msg{}
   484  	found := false
   485  	for _, srv := range servers {
   486  		debug(m, "DNS trying backup server: %s", srv)
   487  		msg.SetQuestion(domain+".", dns.TypeTXT)
   488  		r, _, err = c.Exchange(&msg, srv)
   489  		if err != nil {
   490  			debug(m, "DNS backup server failed; %s", err.Error())
   491  		} else {
   492  			found = true
   493  			break
   494  		}
   495  	}
   496  	if !found {
   497  		return res, fmt.Errorf("failed to lookup DNS: %s", domain)
   498  	}
   499  
   500  	for _, ans := range r.Answer {
   501  		if record, ok := ans.(*dns.TXT); ok {
   502  			if len(record.Txt) > 0 {
   503  				res = append(res, record.Txt[len(record.Txt)-1])
   504  			}
   505  		}
   506  	}
   507  	return res, err
   508  }
   509  
   510  // Run each script on each TXT record of the domain.
   511  func runDNSOne(m metaContext, domain string, scripts []scriptT, mknewstate stateMaker, sigIDMedium string) libkb.ProofError {
   512  	// Fetch TXT records
   513  	var txts []string
   514  	var err error
   515  	if m.getStubDNS() == nil {
   516  		txts, err = runDNSTXTQuery(m, domain)
   517  	} else {
   518  		txts, err = m.getStubDNS().LookupTXT(domain)
   519  	}
   520  
   521  	if err != nil {
   522  		return libkb.NewProofError(keybase1.ProofStatus_DNS_ERROR,
   523  			"DNS failure for %s: %s", domain, err)
   524  	}
   525  
   526  	for _, record := range txts {
   527  		debug(m, "For DNS domain '%s' got TXT record: '%s'", domain, record)
   528  
   529  		// Try all scripts.
   530  		for i, script := range scripts {
   531  			state, err := mknewstate(i)
   532  			if err != nil {
   533  				return err
   534  			}
   535  
   536  			if err = state.Regs.Set("txt", record); err != nil {
   537  				return err
   538  			}
   539  
   540  			if err = runScript(m, &script, state); err == nil {
   541  				return nil
   542  			}
   543  
   544  			// Discard error, it has already been reported by stepInstruction.
   545  		}
   546  	}
   547  
   548  	return libkb.NewProofError(keybase1.ProofStatus_NOT_FOUND,
   549  		"Checked %d TXT entries of %s, but didn't find signature keybase-site-verification=%s",
   550  		len(txts), domain, sigIDMedium)
   551  }
   552  
   553  func runScript(m metaContext, script *scriptT, startstate scriptState) libkb.ProofError {
   554  	var state = startstate
   555  	if len(script.Instructions) < 1 {
   556  		perr := libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   557  			"Empty scripts are not allowed.")
   558  		debugWithStateError(m, state, perr)
   559  		return perr
   560  	}
   561  	for i, ins := range script.Instructions {
   562  		// Sanity check.
   563  		if state.PC != i {
   564  			perr := libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   565  				"Execution failure, PC mismatch %v %v", state.PC, i)
   566  			debugWithStateError(m, state, perr)
   567  			return perr
   568  		}
   569  
   570  		newstate, perr := stepInstruction(m, ins, state)
   571  		if perr != nil {
   572  			return perr
   573  		}
   574  		state = newstate
   575  		state.PC++
   576  	}
   577  
   578  	// Script executed successfully with no errors.
   579  	return nil
   580  }
   581  
   582  // stepInstruction decides which instruction to run.
   583  func stepInstruction(m metaContext, ins instructionT, state scriptState) (scriptState, libkb.ProofError) {
   584  	debugWithState(m, state, "Running instruction %v: %v", ins.Name(), ins)
   585  
   586  	var newState scriptState
   587  	var stepErr libkb.ProofError
   588  	var customErrSpec *errorT
   589  	switch {
   590  	case ins.AssertRegexMatch != nil:
   591  		newState, stepErr = stepAssertRegexMatch(m, *ins.AssertRegexMatch, state)
   592  		customErrSpec = ins.AssertRegexMatch.Error
   593  	case ins.AssertFindBase64 != nil:
   594  		newState, stepErr = stepAssertFindBase64(m, *ins.AssertFindBase64, state)
   595  		customErrSpec = ins.AssertFindBase64.Error
   596  	case ins.AssertCompare != nil:
   597  		newState, stepErr = stepAssertCompare(m, *ins.AssertCompare, state)
   598  		customErrSpec = ins.AssertCompare.Error
   599  	case ins.WhitespaceNormalize != nil:
   600  		newState, stepErr = stepWhitespaceNormalize(m, *ins.WhitespaceNormalize, state)
   601  		customErrSpec = ins.WhitespaceNormalize.Error
   602  	case ins.RegexCapture != nil:
   603  		newState, stepErr = stepRegexCapture(m, *ins.RegexCapture, state)
   604  		customErrSpec = ins.RegexCapture.Error
   605  	case ins.ReplaceAll != nil:
   606  		newState, stepErr = stepReplaceAll(m, *ins.ReplaceAll, state)
   607  		customErrSpec = ins.ReplaceAll.Error
   608  	case ins.ParseURL != nil:
   609  		newState, stepErr = stepParseURL(m, *ins.ParseURL, state)
   610  		customErrSpec = ins.ParseURL.Error
   611  	case ins.Fetch != nil:
   612  		newState, stepErr = stepFetch(m, *ins.Fetch, state)
   613  		customErrSpec = ins.Fetch.Error
   614  	case ins.ParseHTML != nil:
   615  		newState, stepErr = stepParseHTML(m, *ins.ParseHTML, state)
   616  		customErrSpec = ins.ParseHTML.Error
   617  	case ins.SelectorJSON != nil:
   618  		newState, stepErr = stepSelectorJSON(m, *ins.SelectorJSON, state)
   619  		customErrSpec = ins.SelectorJSON.Error
   620  	case ins.SelectorCSS != nil:
   621  		newState, stepErr = stepSelectorCSS(m, *ins.SelectorCSS, state)
   622  		customErrSpec = ins.SelectorCSS.Error
   623  	case ins.Fill != nil:
   624  		newState, stepErr = stepFill(m, *ins.Fill, state)
   625  		customErrSpec = ins.Fill.Error
   626  	default:
   627  		newState = state
   628  		stepErr = libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   629  			"Invalid instruction: %v", ins)
   630  	}
   631  
   632  	if stepErr != nil {
   633  		debugWithStateError(m, state, stepErr)
   634  		stepErr = replaceCustomError(m, state, customErrSpec, stepErr)
   635  	}
   636  	return newState, stepErr
   637  
   638  }
   639  
   640  func stepAssertRegexMatch(m metaContext, ins assertRegexMatchT, state scriptState) (scriptState, libkb.ProofError) {
   641  	rdesc := regexDescriptor{
   642  		Template:        ins.Pattern,
   643  		CaseInsensitive: ins.CaseInsensitive,
   644  		MultiLine:       ins.MultiLine,
   645  	}
   646  	from, err := state.Regs.Get(ins.From)
   647  	if err != nil {
   648  		return state, err
   649  	}
   650  	re, err := interpretRegex(m, state, rdesc)
   651  	if err != nil {
   652  		return state, err
   653  	}
   654  	if re.MatchString(from) == ins.Negate {
   655  		negate := "not "
   656  		if ins.Negate {
   657  			negate = ""
   658  		}
   659  		debugWithState(m, state, "Regex did %smatch:\n  %v\n  %v\n  %q",
   660  			negate, rdesc.Template, re, from)
   661  		return state, libkb.NewProofError(keybase1.ProofStatus_CONTENT_FAILURE,
   662  			"Regex did %smatch (%v)", negate, rdesc.Template)
   663  	}
   664  
   665  	return state, nil
   666  }
   667  
   668  func stepAssertFindBase64(m metaContext, ins assertFindBase64T, state scriptState) (scriptState, libkb.ProofError) {
   669  	if ins.Needle != "sig" {
   670  		return state, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   671  			"Can only assert_find_base64 for sig")
   672  	}
   673  	haystack, err := state.Regs.Get(ins.Haystack)
   674  	if err != nil {
   675  		return state, err
   676  	}
   677  	if libkb.FindBase64Block(haystack, state.Sig, false) {
   678  		return state, nil
   679  	}
   680  	return state, libkb.NewProofError(keybase1.ProofStatus_TEXT_NOT_FOUND,
   681  		"Signature not found")
   682  }
   683  
   684  func stepAssertCompare(m metaContext, ins assertCompareT, state scriptState) (scriptState, libkb.ProofError) {
   685  	a, err := state.Regs.Get(ins.A)
   686  	if err != nil {
   687  		return state, err
   688  	}
   689  	b, err := state.Regs.Get(ins.B)
   690  	if err != nil {
   691  		return state, err
   692  	}
   693  
   694  	var same bool
   695  	switch ins.Cmp {
   696  	case "exact":
   697  		same = (a == b)
   698  	case "cicmp":
   699  		same = libkb.Cicmp(a, b)
   700  	case "stripdots-then-cicmp":
   701  		norm := func(s string) string {
   702  			return strings.ToLower(strings.ReplaceAll(s, ".", ""))
   703  		}
   704  		same = libkb.Cicmp(norm(a), norm(b))
   705  	default:
   706  		return state, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   707  			"Unsupported comparison method: '%v'", ins.Cmp)
   708  	}
   709  
   710  	if !same {
   711  		debugWithState(m, state, "Comparison (%v) failed\n  %v != %v\n  '%v' != '%v'",
   712  			ins.Cmp, ins.A, ins.B, a, b)
   713  		return state, libkb.NewProofError(keybase1.ProofStatus_CONTENT_FAILURE,
   714  			"Comparison (%v) failed '%v' != '%v'", ins.Cmp, a, b)
   715  	}
   716  
   717  	return state, nil
   718  }
   719  
   720  func stepWhitespaceNormalize(m metaContext, ins whitespaceNormalizeT, state scriptState) (scriptState, libkb.ProofError) {
   721  	from, err := state.Regs.Get(ins.From)
   722  	if err != nil {
   723  		return state, err
   724  	}
   725  	normed := libkb.WhitespaceNormalize(from)
   726  	err = state.Regs.Set(ins.Into, normed)
   727  	return state, err
   728  }
   729  
   730  func stepRegexCapture(m metaContext, ins regexCaptureT, state scriptState) (scriptState, libkb.ProofError) {
   731  	rdesc := regexDescriptor{
   732  		Template:        ins.Pattern,
   733  		CaseInsensitive: ins.CaseInsensitive,
   734  		MultiLine:       ins.MultiLine,
   735  	}
   736  
   737  	from, err := state.Regs.Get(ins.From)
   738  	if err != nil {
   739  		return state, err
   740  	}
   741  
   742  	re, err := interpretRegex(m, state, rdesc)
   743  	if err != nil {
   744  		return state, err
   745  	}
   746  
   747  	// There must be some registers to write results to.
   748  	if len(ins.Into) == 0 {
   749  		return state, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   750  			"Into list cannot be empty")
   751  	}
   752  
   753  	match := re.FindStringSubmatch(from)
   754  	// Assert that the match matched and has at least one capture group.
   755  	// -1 for the ignored first element of match
   756  	if len(match)-1 < len(ins.Into) {
   757  		debugWithState(m, state, "Regex capture did not match enough groups:\n  %v\n  %v\n  %q\n  %v",
   758  			rdesc.Template, re, from, match)
   759  		return state, libkb.NewProofError(keybase1.ProofStatus_CONTENT_FAILURE,
   760  			"Regex capture did not match enough groups (%v)", rdesc.Template)
   761  	}
   762  	for i := 0; i < len(ins.Into); i++ {
   763  		err := state.Regs.Set(ins.Into[i], match[i+1])
   764  		if err != nil {
   765  			return state, err
   766  		}
   767  	}
   768  	return state, nil
   769  }
   770  
   771  func stepReplaceAll(m metaContext, ins replaceAllT, state scriptState) (scriptState, libkb.ProofError) {
   772  	from, err := state.Regs.Get(ins.From)
   773  	if err != nil {
   774  		return state, err
   775  	}
   776  
   777  	replaced := strings.ReplaceAll(from, ins.Old, ins.New)
   778  	if err = state.Regs.Set(ins.Into, replaced); err != nil {
   779  		return state, err
   780  	}
   781  
   782  	return state, nil
   783  }
   784  
   785  func stepParseURL(m metaContext, ins parseURLT, state scriptState) (scriptState, libkb.ProofError) {
   786  	s, err := state.Regs.Get(ins.From)
   787  	if err != nil {
   788  		return state, err
   789  	}
   790  
   791  	u, err2 := url.Parse(s)
   792  	if err2 != nil {
   793  		return state, libkb.NewProofError(keybase1.ProofStatus_FAILED_PARSE,
   794  			"Could not parse url: '%v'", s)
   795  	}
   796  
   797  	if ins.Path != "" {
   798  		err := state.Regs.Set(ins.Path, u.Path)
   799  		if err != nil {
   800  			return state, err
   801  		}
   802  	}
   803  
   804  	if ins.Host != "" {
   805  		err := state.Regs.Set(ins.Host, u.Host)
   806  		if err != nil {
   807  			return state, err
   808  		}
   809  	}
   810  
   811  	if ins.Scheme != "" {
   812  		err := state.Regs.Set(ins.Scheme, u.Scheme)
   813  		if err != nil {
   814  			return state, err
   815  		}
   816  	}
   817  
   818  	return state, nil
   819  }
   820  
   821  func stepFetch(m metaContext, ins fetchT, state scriptState) (scriptState, libkb.ProofError) {
   822  	if state.FetchResult != nil {
   823  		return state, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   824  			"Script cannot contain more than one fetch")
   825  	}
   826  	if state.Service == keybase1.ProofType_DNS {
   827  		return state, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   828  			"Script cannot fetch in DNS mode")
   829  	}
   830  
   831  	from, err := state.Regs.Get(ins.From)
   832  	if err != nil {
   833  		return state, err
   834  	}
   835  	if state.Service == keybase1.ProofType_ROOTER {
   836  		from2, err := rooterRewriteURL(m, from)
   837  		if err != nil {
   838  			return state, libkb.NewProofError(keybase1.ProofStatus_FAILED_PARSE,
   839  				"Could not rewrite rooter URL: %v", err)
   840  		}
   841  		from = from2
   842  	}
   843  
   844  	switch fetchMode(ins.Kind) {
   845  	case fetchModeString:
   846  		debugWithState(m, state, "fetchurl: %v", from)
   847  		res, err1 := m.G().GetExternalAPI().GetText(m.MetaContext, libkb.APIArg{Endpoint: from})
   848  		if err1 != nil {
   849  			return state, libkb.XapiError(err1, from)
   850  		}
   851  		state.FetchResult = &fetchResult{
   852  			fetchMode: fetchModeString,
   853  			String:    res.Body,
   854  		}
   855  		err := state.Regs.Set(ins.Into, state.FetchResult.String)
   856  		if err != nil {
   857  			return state, err
   858  		}
   859  		return state, nil
   860  	case fetchModeJSON:
   861  		if ins.Into != "" {
   862  			return state, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   863  				"JSON fetch must not specify 'into' register")
   864  		}
   865  		res, err1 := m.G().GetExternalAPI().Get(m.MetaContext, libkb.APIArg{Endpoint: from})
   866  		if err1 != nil {
   867  			return state, libkb.XapiError(err1, from)
   868  		}
   869  		state.FetchResult = &fetchResult{
   870  			fetchMode: fetchModeJSON,
   871  			JSON:      res.Body,
   872  		}
   873  		return state, nil
   874  	case fetchModeHTML:
   875  		if ins.Into != "" {
   876  			return state, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   877  				"HTML fetch must not specify 'into' register")
   878  		}
   879  		res, err1 := m.G().GetExternalAPI().GetHTML(m.MetaContext, libkb.APIArg{Endpoint: from})
   880  		if err1 != nil {
   881  			return state, libkb.XapiError(err1, from)
   882  		}
   883  		state.FetchResult = &fetchResult{
   884  			fetchMode: fetchModeHTML,
   885  			HTML:      res.GoQuery,
   886  		}
   887  		return state, nil
   888  	default:
   889  		return state, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   890  			"Unsupported fetch kind %v", ins.Kind)
   891  	}
   892  }
   893  
   894  func stepParseHTML(m metaContext, ins parseHTMLT, state scriptState) (scriptState, libkb.ProofError) {
   895  	from, err := state.Regs.Get(ins.From)
   896  	if err != nil {
   897  		return state, err
   898  	}
   899  
   900  	gq, err2 := goquery.NewDocumentFromReader(bytes.NewBuffer([]byte(from)))
   901  	if err2 != nil {
   902  		return state, libkb.NewProofError(keybase1.ProofStatus_FAILED_PARSE, "Failed to parse html from '%v': %v", ins.From, err2)
   903  	}
   904  
   905  	state.FetchResult = &fetchResult{
   906  		fetchMode: fetchModeHTML,
   907  		HTML:      gq,
   908  	}
   909  	return state, nil
   910  }
   911  
   912  func stepSelectorJSON(m metaContext, ins selectorJSONT, state scriptState) (scriptState, libkb.ProofError) {
   913  	if state.FetchResult == nil || state.FetchResult.fetchMode != fetchModeJSON {
   914  		return state, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   915  			"Cannot use json selector with non-json fetch result")
   916  	}
   917  
   918  	if len(ins.Selectors) < 1 {
   919  		return state, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   920  			"Json selector list must contain at least 1 element")
   921  	}
   922  
   923  	results, perr := runSelectorJSONInner(m, state, state.FetchResult.JSON, ins.Selectors)
   924  	if perr != nil {
   925  		return state, perr
   926  	}
   927  	if len(results) < 1 {
   928  		return state, libkb.NewProofError(keybase1.ProofStatus_CONTENT_FAILURE,
   929  			"Json selector did not match any values")
   930  	}
   931  	s := strings.Join(results, " ")
   932  
   933  	err := state.Regs.Set(ins.Into, s)
   934  	return state, err
   935  }
   936  
   937  func stepSelectorCSS(m metaContext, ins selectorCSST, state scriptState) (scriptState, libkb.ProofError) {
   938  	if state.FetchResult == nil || state.FetchResult.fetchMode != fetchModeHTML {
   939  		return state, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   940  			"Cannot use css selector with non-html fetch result")
   941  	}
   942  
   943  	selection, perr := runCSSSelectorInner(m, state.FetchResult.HTML.Selection, ins.Selectors)
   944  	if perr != nil {
   945  		return state, perr
   946  	}
   947  
   948  	if selection.Size() < 1 {
   949  		return state, libkb.NewProofError(keybase1.ProofStatus_CONTENT_FAILURE,
   950  			"No elements matched by selector")
   951  	}
   952  
   953  	if selection.Size() > 1 && !ins.Multi {
   954  		return state, libkb.NewProofError(keybase1.ProofStatus_CONTENT_FAILURE,
   955  			"CSS selector matched too many elements")
   956  	}
   957  
   958  	// Get the text, attribute, or data.
   959  	var res string
   960  	switch {
   961  	case ins.Attr != "":
   962  		res = selectionAttr(selection, ins.Attr)
   963  	case ins.Data:
   964  		res = selectionData(selection)
   965  	default:
   966  		res = selectionText(selection)
   967  	}
   968  
   969  	err := state.Regs.Set(ins.Into, res)
   970  	return state, err
   971  }
   972  
   973  func stepFill(m metaContext, ins fillT, state scriptState) (scriptState, libkb.ProofError) {
   974  	s, err := substituteExact(ins.With, state)
   975  	if err != nil {
   976  		debugWithState(m, state, "Fill did not succeed:\n  %v\n  %v\n  %v",
   977  			ins.With, err, ins.Into)
   978  		return state, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   979  			"Could not fill variable (%v): %v", ins.Into, err)
   980  	}
   981  
   982  	err = state.Regs.Set(ins.Into, s)
   983  	return state, err
   984  }
   985  
   986  // Run a PVL CSS selector.
   987  // selectors is a list like [ "div .foo", 0, ".bar"] ].
   988  // Each string runs a selector, each integer runs a Eq.
   989  func runCSSSelectorInner(m metaContext, html *goquery.Selection,
   990  	selectors []keybase1.SelectorEntry) (*goquery.Selection, libkb.ProofError) {
   991  	if len(selectors) < 1 {
   992  		return nil, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
   993  			"CSS selectors array must not be empty")
   994  	}
   995  
   996  	selection := html
   997  	for _, selector := range selectors {
   998  		switch {
   999  		case selector.IsIndex:
  1000  			selection = selection.Eq(selector.Index)
  1001  		case selector.IsKey:
  1002  			selection = selection.Find(selector.Key)
  1003  		case selector.IsContents:
  1004  			selection = selection.Contents()
  1005  		default:
  1006  			return nil, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
  1007  				"CSS selector entry must be a string, int, or 'contents' %v", selector)
  1008  		}
  1009  	}
  1010  
  1011  	return selection, nil
  1012  }
  1013  
  1014  func runSelectorJSONInner(m metaContext, state scriptState, selectedObject *jsonw.Wrapper,
  1015  	selectors []keybase1.SelectorEntry) ([]string, libkb.ProofError) {
  1016  	logger := func(format string, args ...interface{}) {
  1017  		debugWithState(m, state, format, args)
  1018  	}
  1019  	jsonResults, perr := jsonhelpers.AtSelectorPath(selectedObject, selectors, logger, libkb.NewInvalidPVLSelectorError)
  1020  	if perrInner, _ := perr.(libkb.ProofError); perrInner != nil {
  1021  		return nil, perrInner
  1022  	}
  1023  	if perr != nil {
  1024  		return nil, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL, "json select error in pvl interp")
  1025  	}
  1026  	results := []string{}
  1027  	for _, object := range jsonResults {
  1028  		s, err := jsonhelpers.JSONStringSimple(object)
  1029  		if err != nil {
  1030  			logger("JSON could not read object: %v (%v)", err, object)
  1031  			continue
  1032  		}
  1033  		results = append(results, s)
  1034  	}
  1035  	return results, nil
  1036  }
  1037  
  1038  // Take a regex descriptor, do variable substitution, and build a regex.
  1039  func interpretRegex(m metaContext, state scriptState, rdesc regexDescriptor) (*regexp.Regexp, libkb.ProofError) {
  1040  	// Check that regex is bounded by "^" and "$".
  1041  	if !strings.HasPrefix(rdesc.Template, "^") {
  1042  		return nil, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
  1043  			"Could not build regex: %v (%v)", "must start with '^'", rdesc.Template)
  1044  	}
  1045  	if !strings.HasSuffix(rdesc.Template, "$") {
  1046  		return nil, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
  1047  			"Could not build regex: %v (%v)", "must end with '$'", rdesc.Template)
  1048  	}
  1049  
  1050  	var optstring = ""
  1051  	if rdesc.CaseInsensitive {
  1052  		optstring += "i"
  1053  	}
  1054  	if rdesc.MultiLine {
  1055  		optstring += "m"
  1056  	}
  1057  
  1058  	var prefix = ""
  1059  	if len(optstring) > 0 {
  1060  		prefix = "(?" + optstring + ")"
  1061  	}
  1062  
  1063  	// Do variable interpolation.
  1064  	prepattern, perr := substituteReEscape(rdesc.Template, state)
  1065  	if perr != nil {
  1066  		return nil, perr
  1067  	}
  1068  	pattern := prefix + prepattern
  1069  
  1070  	// Build the regex.
  1071  	re, err := regexp.Compile(pattern)
  1072  	if err != nil {
  1073  		debugWithState(m, state, "Could not compile regex: %v\n  %v\n  %v", err, rdesc.Template, pattern)
  1074  		return nil, libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
  1075  			"Could not compile regex: %v (%v)", err, rdesc.Template)
  1076  	}
  1077  	return re, nil
  1078  }
  1079  
  1080  // Use a custom error spec to derive an error.
  1081  // spec can be none
  1082  // Copies over the error code if none is specified.
  1083  func replaceCustomError(m metaContext, state scriptState, spec *errorT, err1 libkb.ProofError) libkb.ProofError {
  1084  	if err1 == nil {
  1085  		return err1
  1086  	}
  1087  
  1088  	// Don't rewrite invalid_pvl errors
  1089  	if err1.GetProofStatus() == keybase1.ProofStatus_INVALID_PVL {
  1090  		return err1
  1091  	}
  1092  
  1093  	if spec == nil {
  1094  		return err1
  1095  	}
  1096  
  1097  	if (spec.Status != err1.GetProofStatus()) || (spec.Description != err1.GetDesc()) {
  1098  		newDesc := spec.Description
  1099  		subbedDesc, subErr := substituteExact(spec.Description, state)
  1100  		if subErr == nil {
  1101  			newDesc = subbedDesc
  1102  		}
  1103  		err2 := libkb.NewProofError(spec.Status, newDesc)
  1104  		debugWithState(m, state, "Replacing error with custom error")
  1105  		debugWithStateError(m, state, err2)
  1106  
  1107  		return err2
  1108  	}
  1109  	return err1
  1110  }