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

     1  package contacts
     2  
     3  import (
     4  	"context"
     5  	"encoding/base64"
     6  	"errors"
     7  	"github.com/keybase/client/go/encrypteddb"
     8  	"github.com/keybase/client/go/libkb"
     9  	"github.com/keybase/client/go/msgpack"
    10  	"github.com/keybase/client/go/protocol/keybase1"
    11  )
    12  
    13  type ContactResolution struct {
    14  	Description  string
    15  	ResolvedUser keybase1.User
    16  }
    17  
    18  const ContactResolutionEncryptionVersion = 1
    19  
    20  type ContactResolutionForEncryption struct {
    21  	ContactResolution
    22  	EncryptionVersion int
    23  }
    24  
    25  type EncryptedContactResolution struct {
    26  	Blob          []byte
    27  	PukGeneration keybase1.PerUserKeyGeneration
    28  }
    29  
    30  var errWrongContactEncryptionScheme = errors.New(
    31  	"wrong encryption version for encrypted contact blob")
    32  
    33  func getPerUserKeyFn(mctx libkb.MetaContext,
    34  	pukGen *keybase1.PerUserKeyGeneration) (keyFn func(ctx context.
    35  	Context) ([32]byte, error), gen keybase1.PerUserKeyGeneration,
    36  	errOuter error) {
    37  	pukring, err := mctx.G().GetPerUserKeyring(mctx.Ctx())
    38  	if err != nil {
    39  		return nil, gen, err
    40  	}
    41  	err = pukring.Sync(mctx)
    42  	if err != nil {
    43  		return nil, gen, err
    44  	}
    45  	currentGen := pukring.CurrentGeneration()
    46  	if pukGen == nil {
    47  		pukGen = &currentGen
    48  	}
    49  	keyFn = func(ctx context.Context) (res [32]byte, err error) {
    50  		keypair, err := pukring.GetEncryptionKeyByGeneration(mctx, *pukGen)
    51  		if err != nil {
    52  			return res, err
    53  		}
    54  
    55  		// Derive symmetric key from PUK
    56  		skey, err := keypair.SecretSymmetricKey(libkb.EncryptionReasonContactsResolvedServer)
    57  		if err != nil {
    58  			return res, err
    59  		}
    60  
    61  		copy(res[:], skey[:])
    62  		return res, nil
    63  	}
    64  	return keyFn, *pukGen, nil
    65  }
    66  
    67  func encryptContactBlob(mctx libkb.MetaContext, res ContactResolution) (string,
    68  	error) {
    69  	keyFn, gen, err := getPerUserKeyFn(mctx, nil)
    70  	if err != nil {
    71  		return "", err
    72  	}
    73  	boxedRes := ContactResolutionForEncryption{res, ContactResolutionEncryptionVersion}
    74  	encrypted, err := encrypteddb.EncodeBox(mctx.Ctx(), boxedRes, keyFn)
    75  	if err != nil {
    76  		return "", err
    77  	}
    78  	result := EncryptedContactResolution{
    79  		Blob:          encrypted,
    80  		PukGeneration: gen,
    81  	}
    82  	messagePacked, err := msgpack.Encode(result)
    83  	if err != nil {
    84  		return "", err
    85  	}
    86  	return base64.StdEncoding.EncodeToString(messagePacked), nil
    87  }
    88  
    89  func DecryptContactBlob(mctx libkb.MetaContext,
    90  	contactResBlob string) (res ContactResolution, err error) {
    91  	messagePacked, err := base64.StdEncoding.DecodeString(contactResBlob)
    92  	if err != nil {
    93  		return res, err
    94  	}
    95  	var unpacked EncryptedContactResolution
    96  	err = msgpack.Decode(&unpacked, messagePacked)
    97  	if err != nil {
    98  		return res, err
    99  	}
   100  	keyFn, _, err := getPerUserKeyFn(mctx, &unpacked.PukGeneration)
   101  	if err != nil {
   102  		return res, err
   103  	}
   104  	var boxedRes ContactResolutionForEncryption
   105  	err = encrypteddb.DecodeBox(mctx.Ctx(), unpacked.Blob,
   106  		keyFn, &boxedRes)
   107  	if err != nil {
   108  		return res, err
   109  	}
   110  	if boxedRes.EncryptionVersion != ContactResolutionEncryptionVersion {
   111  		return res, errWrongContactEncryptionScheme
   112  	}
   113  	return boxedRes.ContactResolution, nil
   114  }
   115  
   116  func SendEncryptedContactResolutionToServer(mctx libkb.MetaContext,
   117  	resolutions []ContactResolution) error {
   118  
   119  	type resolvedArg struct {
   120  		ResolvedContactBlobBase64 string `json:"blob"`
   121  	}
   122  
   123  	type resolvedRes struct {
   124  		libkb.AppStatusEmbed
   125  	}
   126  
   127  	args := make([]resolvedArg, 0, len(resolutions))
   128  	for _, res := range resolutions {
   129  		blob, err := encryptContactBlob(mctx, res)
   130  		if err != nil {
   131  			return err
   132  		}
   133  		args = append(args, resolvedArg{ResolvedContactBlobBase64: blob})
   134  	}
   135  	payload := make(libkb.JSONPayload)
   136  	payload["resolved_contact_blobs"] = args
   137  
   138  	arg := libkb.APIArg{
   139  		Endpoint:    "contacts/resolved",
   140  		JSONPayload: payload,
   141  		SessionType: libkb.APISessionTypeREQUIRED,
   142  	}
   143  
   144  	var resp resolvedRes
   145  	return mctx.G().API.PostDecode(mctx, arg, &resp)
   146  }