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

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package libkb
     5  
     6  import (
     7  	"errors"
     8  	"time"
     9  
    10  	"github.com/keybase/client/go/protocol/keybase1"
    11  )
    12  
    13  // DelegatorAggregator manages delegating multiple keys in one post to the
    14  // server When run produces a map which goes into the 'key/multi' 'sigs' list.
    15  type AggSigProducer func() (JSONPayload, keybase1.Seqno, LinkID, error)
    16  
    17  // Run posts an array of delegations to the server. Keeping this simple as we
    18  // don't need any state (yet) `extra` is optional and adds an extra sig,
    19  // produced by something other than a Delegator, after the others.
    20  func DelegatorAggregator(m MetaContext, ds []Delegator, extra AggSigProducer,
    21  	pukBoxes []keybase1.PerUserKeyBox, pukPrev *PerUserKeyPrev, userEKReboxArg *keybase1.UserEkReboxArg) (err error) {
    22  	if len(ds) == 0 {
    23  		return errors.New("Empty delegators to aggregator")
    24  	}
    25  
    26  	// Store all the args to build a single big json later
    27  	var args []JSONPayload
    28  	var uid keybase1.UID
    29  	var lastSeqno keybase1.Seqno
    30  	var lastLinkID LinkID
    31  
    32  	for i := range ds {
    33  		// Mutate the original and not a copy from range
    34  		var d = &ds[i]
    35  
    36  		d.Aggregated = true
    37  		if err := d.Run(m); err != nil {
    38  			return err
    39  		}
    40  
    41  		flatArgs := d.postArg.flattenHTTPArgs(d.postArg.getHTTPArgs())
    42  		args = append(args, convertStringMapToJSONPayload(flatArgs))
    43  		if uid.IsNil() && d.Me != nil {
    44  			uid = d.Me.GetUID()
    45  		}
    46  		lastSeqno = d.proof.Seqno
    47  		lastLinkID = d.linkID
    48  	}
    49  
    50  	if extra != nil {
    51  		x, seqno, linkID, err := extra()
    52  		if err != nil {
    53  			return err
    54  		}
    55  		args = append(args, x)
    56  		lastSeqno = seqno
    57  		lastLinkID = linkID
    58  	}
    59  
    60  	payload := make(JSONPayload)
    61  	payload["sigs"] = args
    62  
    63  	if len(pukBoxes) > 0 {
    64  		AddPerUserKeyServerArg(payload, pukBoxes[0].Generation, pukBoxes, pukPrev)
    65  	} else if pukPrev != nil {
    66  		return errors.New("cannot delegate per-user-key with prev but no boxes")
    67  	}
    68  	AddUserEKReBoxServerArg(payload, userEKReboxArg)
    69  
    70  	// Adopt most parameters from the first item
    71  	var apiArgBase = ds[0]
    72  	var apiArg = apiArgBase.postArg
    73  	apiArg.Args = nil
    74  	apiArg.uArgs = nil
    75  	apiArg.Endpoint = "key/multi"
    76  	apiArg.JSONPayload = payload
    77  	// It's bad to fail provisioning especially after signup.
    78  	apiArg.InitialTimeout = 5 * time.Minute
    79  	apiArg.RetryCount = 10
    80  
    81  	_, err = m.G().API.PostJSON(m, apiArg)
    82  	if err != nil {
    83  		return err
    84  	}
    85  	return MerkleCheckPostedUserSig(m, uid, lastSeqno, lastLinkID)
    86  }
    87  
    88  // Make the "per_user_key" section of an API arg.
    89  // Requires one or more `pukBoxes`
    90  // `pukPrev` is optional.
    91  // Modifies `serverArg`.
    92  func AddPerUserKeyServerArg(serverArg JSONPayload, generation keybase1.PerUserKeyGeneration,
    93  	pukBoxes []keybase1.PerUserKeyBox, pukPrev *PerUserKeyPrev) {
    94  	section := make(JSONPayload)
    95  	section["boxes"] = pukBoxes
    96  	section["generation"] = generation
    97  	if pukPrev != nil {
    98  		section["prev"] = *pukPrev
    99  	}
   100  	serverArg["per_user_key"] = section
   101  }
   102  
   103  // Make the "user_ek_rebox" and "device_eks" section of an API arg.  Modifies
   104  // `serverArg` unless arg is nil.
   105  func AddUserEKReBoxServerArg(serverArg JSONPayload, arg *keybase1.UserEkReboxArg) {
   106  	if arg == nil {
   107  		return
   108  	}
   109  	serverArg["device_eks"] = map[string]string{string(arg.DeviceID): arg.DeviceEkStatementSig}
   110  	serverArg["user_ek_rebox"] = arg.UserEkBoxMetadata
   111  }
   112  
   113  func convertStringMapToJSONPayload(in map[string]string) JSONPayload {
   114  	out := make(JSONPayload)
   115  	for k, v := range in {
   116  		out[k] = v
   117  	}
   118  	return out
   119  }