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 = ¤tGen 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 }