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  }