git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/client/object_hash.go (about) 1 package client 2 3 import ( 4 "context" 5 "crypto/ecdsa" 6 "fmt" 7 8 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl" 9 v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" 10 v2refs "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" 11 rpcapi "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc" 12 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client" 13 v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" 14 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" 15 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" 16 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum" 17 apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" 18 cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" 19 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" 20 oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" 21 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session" 22 ) 23 24 // PrmObjectHash groups parameters of ObjectHash operation. 25 type PrmObjectHash struct { 26 XHeaders []string 27 28 BearerToken *bearer.Token 29 30 Session *session.Object 31 32 Local bool 33 34 Ranges []object.Range 35 36 Salt []byte 37 38 ChecksumType checksum.Type 39 40 ContainerID *cid.ID 41 42 ObjectID *oid.ID 43 44 Key *ecdsa.PrivateKey 45 } 46 47 // UseKey specifies private key to sign the requests. 48 // If key is not provided, then Client default key is used. 49 // 50 // Deprecated: Use PrmObjectHash.Key instead. 51 func (prm *PrmObjectHash) UseKey(key ecdsa.PrivateKey) { 52 prm.Key = &key 53 } 54 55 // TillichZemorAlgo changes the hash function to Tillich-Zemor 56 // (https://link.springer.com/content/pdf/10.1007/3-540-48658-5_5.pdf). 57 // 58 // By default, SHA256 hash function is used/. 59 // 60 // Deprecated: Use PrmObjectHash.ChecksumType instead. 61 func (prm *PrmObjectHash) TillichZemorAlgo() { 62 prm.ChecksumType = checksum.TZ 63 } 64 65 // ResObjectHash groups resulting values of ObjectHash operation. 66 type ResObjectHash struct { 67 statusRes 68 69 checksums [][]byte 70 } 71 72 // Checksums returns a list of calculated checksums in range order. 73 func (x ResObjectHash) Checksums() [][]byte { 74 return x.checksums 75 } 76 77 func (prm *PrmObjectHash) buildRequest(c *Client) (*v2object.GetRangeHashRequest, error) { 78 if prm.ContainerID == nil { 79 return nil, errorMissingContainer 80 } 81 82 if prm.ObjectID == nil { 83 return nil, errorMissingObject 84 } 85 86 if len(prm.XHeaders)%2 != 0 { 87 return nil, errorInvalidXHeaders 88 } 89 90 if len(prm.Ranges) == 0 { 91 return nil, errorMissingRanges 92 } 93 94 meta := new(v2session.RequestMetaHeader) 95 writeXHeadersToMeta(prm.XHeaders, meta) 96 97 if prm.BearerToken != nil { 98 v2BearerToken := new(acl.BearerToken) 99 prm.BearerToken.WriteToV2(v2BearerToken) 100 meta.SetBearerToken(v2BearerToken) 101 } 102 103 if prm.Session != nil { 104 v2SessionToken := new(v2session.Token) 105 prm.Session.WriteToV2(v2SessionToken) 106 meta.SetSessionToken(v2SessionToken) 107 } 108 109 if prm.Local { 110 meta.SetTTL(1) 111 } 112 113 addr := new(v2refs.Address) 114 115 cnrV2 := new(v2refs.ContainerID) 116 prm.ContainerID.WriteToV2(cnrV2) 117 addr.SetContainerID(cnrV2) 118 119 objV2 := new(v2refs.ObjectID) 120 prm.ObjectID.WriteToV2(objV2) 121 addr.SetObjectID(objV2) 122 123 rs := make([]v2object.Range, len(prm.Ranges)) 124 for i := range prm.Ranges { 125 rs[i].SetOffset(prm.Ranges[i].GetOffset()) 126 rs[i].SetLength(prm.Ranges[i].GetLength()) 127 } 128 129 body := new(v2object.GetRangeHashRequestBody) 130 body.SetAddress(addr) 131 body.SetRanges(rs) 132 body.SetSalt(prm.Salt) 133 134 if prm.ChecksumType == checksum.Unknown { 135 body.SetType(v2refs.SHA256) 136 } else { 137 body.SetType(v2refs.ChecksumType(prm.ChecksumType)) 138 } 139 140 req := new(v2object.GetRangeHashRequest) 141 req.SetBody(body) 142 c.prepareRequest(req, meta) 143 144 return req, nil 145 } 146 147 // ObjectHash requests checksum of the range list of the object payload using 148 // FrostFS API protocol. 149 // 150 // Returns a list of checksums in raw form: the format of hashes and their number 151 // is left for the caller to check. Client preserves the order of the server's response. 152 // 153 // Exactly one return value is non-nil. By default, server status is returned in res structure. 154 // Any client's internal or transport errors are returned as `error`, 155 // If PrmInit.DisableFrostFSFailuresResolution has been called, unsuccessful 156 // FrostFS status codes are included in the returned result structure, 157 // otherwise, are also returned as `error`. 158 // 159 // Returns an error if parameters are set incorrectly (see PrmObjectHash docs). 160 // Context is required and must not be nil. It is used for network communication. 161 // 162 // Return statuses: 163 // - global (see Client docs); 164 // - *apistatus.ContainerNotFound; 165 // - *apistatus.ObjectNotFound; 166 // - *apistatus.ObjectAccessDenied; 167 // - *apistatus.ObjectOutOfRange; 168 // - *apistatus.SessionTokenExpired. 169 func (c *Client) ObjectHash(ctx context.Context, prm PrmObjectHash) (*ResObjectHash, error) { 170 req, err := prm.buildRequest(c) 171 if err != nil { 172 return nil, err 173 } 174 175 key := c.prm.Key 176 if prm.Key != nil { 177 key = *prm.Key 178 } 179 180 err = signature.SignServiceMessage(&key, req) 181 if err != nil { 182 return nil, fmt.Errorf("sign request: %w", err) 183 } 184 185 resp, err := rpcapi.HashObjectRange(&c.c, req, client.WithContext(ctx)) 186 if err != nil { 187 return nil, fmt.Errorf("write request: %w", err) 188 } 189 190 var res ResObjectHash 191 res.st, err = c.processResponse(resp) 192 if err != nil || !apistatus.IsSuccessful(res.st) { 193 return &res, err 194 } 195 196 res.checksums = resp.GetBody().GetHashList() 197 if len(res.checksums) == 0 { 198 return nil, newErrMissingResponseField("hash list") 199 } 200 201 return &res, nil 202 }