diff --git a/client/object_hash.go b/client/object_hash.go index 6df0d7a..7fdf335 100644 --- a/client/object_hash.go +++ b/client/object_hash.go @@ -13,121 +13,53 @@ import ( v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session" ) // PrmObjectHash groups parameters of ObjectHash operation. type PrmObjectHash struct { - meta v2session.RequestMetaHeader + XHeaders []string - body v2object.GetRangeHashRequestBody + BearerToken *bearer.Token - csAlgo v2refs.ChecksumType + Session *session.Object - addr v2refs.Address + Local bool - keySet bool - key ecdsa.PrivateKey + Ranges []object.Range + + Salt []byte + + ChecksumType checksum.Type + + ContainerID *cid.ID + + ObjectID *oid.ID + + Key *ecdsa.PrivateKey } // UseKey specifies private key to sign the requests. // If key is not provided, then Client default key is used. -func (x *PrmObjectHash) UseKey(key ecdsa.PrivateKey) { - x.keySet = true - x.key = key -} - -// MarkLocal tells the server to execute the operation locally. -func (x *PrmObjectHash) MarkLocal() { - x.meta.SetTTL(1) -} - -// WithinSession specifies session within which object should be read. // -// Creator of the session acquires the authorship of the request. -// This may affect the execution of an operation (e.g. access control). -// -// Must be signed. -func (x *PrmObjectHash) WithinSession(t session.Object) { - var tv2 v2session.Token - t.WriteToV2(&tv2) - - x.meta.SetSessionToken(&tv2) -} - -// WithBearerToken attaches bearer token to be used for the operation. -// -// If set, underlying eACL rules will be used in access control. -// -// Must be signed. -func (x *PrmObjectHash) WithBearerToken(t bearer.Token) { - var v2token acl.BearerToken - t.WriteToV2(&v2token) - x.meta.SetBearerToken(&v2token) -} - -// FromContainer specifies FrostFS container of the object. -// Required parameter. -func (x *PrmObjectHash) FromContainer(id cid.ID) { - var cidV2 v2refs.ContainerID - id.WriteToV2(&cidV2) - - x.addr.SetContainerID(&cidV2) -} - -// ByID specifies identifier of the requested object. -// Required parameter. -func (x *PrmObjectHash) ByID(id oid.ID) { - var idV2 v2refs.ObjectID - id.WriteToV2(&idV2) - - x.addr.SetObjectID(&idV2) -} - -// SetRangeList sets list of ranges in (offset, length) pair format. -// Required parameter. -// -// If passed as slice, then it must not be mutated before the operation completes. -func (x *PrmObjectHash) SetRangeList(r ...uint64) { - ln := len(r) - if ln%2 != 0 { - panic("odd number of range parameters") - } - - rs := make([]v2object.Range, ln/2) - - for i := 0; i < ln/2; i++ { - rs[i].SetOffset(r[2*i]) - rs[i].SetLength(r[2*i+1]) - } - - x.body.SetRanges(rs) +// Deprecated: Use PrmObjectHash.Key instead. +func (prm *PrmObjectHash) UseKey(key ecdsa.PrivateKey) { + prm.Key = &key } // TillichZemorAlgo changes the hash function to Tillich-Zemor // (https://link.springer.com/content/pdf/10.1007/3-540-48658-5_5.pdf). // -// By default, SHA256 hash function is used. -func (x *PrmObjectHash) TillichZemorAlgo() { - x.csAlgo = v2refs.TillichZemor -} - -// UseSalt sets the salt to XOR the data range before hashing. +// By default, SHA256 hash function is used/. // -// Must not be mutated before the operation completes. -func (x *PrmObjectHash) UseSalt(salt []byte) { - x.body.SetSalt(salt) -} - -// WithXHeaders specifies list of extended headers (string key-value pairs) -// to be attached to the request. Must have an even length. -// -// Slice must not be mutated until the operation completes. -func (x *PrmObjectHash) WithXHeaders(hs ...string) { - writeXHeadersToMeta(hs, &x.meta) +// Deprecated: Use PrmObjectHash.ChecksumType instead. +func (prm *PrmObjectHash) TillichZemorAlgo() { + prm.ChecksumType = checksum.TZ } // ResObjectHash groups resulting values of ObjectHash operation. @@ -142,6 +74,76 @@ func (x ResObjectHash) Checksums() [][]byte { return x.checksums } +func (prm *PrmObjectHash) buildRequest(c *Client) (*v2object.GetRangeHashRequest, error) { + if prm.ContainerID == nil { + return nil, errorMissingContainer + } + + if prm.ObjectID == nil { + return nil, errorMissingObject + } + + if len(prm.XHeaders)%2 != 0 { + return nil, errorInvalidXHeaders + } + + if len(prm.Ranges) == 0 { + return nil, errorMissingRanges + } + + meta := new(v2session.RequestMetaHeader) + writeXHeadersToMeta(prm.XHeaders, meta) + + if prm.BearerToken != nil { + v2BearerToken := new(acl.BearerToken) + prm.BearerToken.WriteToV2(v2BearerToken) + meta.SetBearerToken(v2BearerToken) + } + + if prm.Session != nil { + v2SessionToken := new(v2session.Token) + prm.Session.WriteToV2(v2SessionToken) + meta.SetSessionToken(v2SessionToken) + } + + if prm.Local { + meta.SetTTL(1) + } + + addr := new(v2refs.Address) + + cnrV2 := new(v2refs.ContainerID) + prm.ContainerID.WriteToV2(cnrV2) + addr.SetContainerID(cnrV2) + + objV2 := new(v2refs.ObjectID) + prm.ObjectID.WriteToV2(objV2) + addr.SetObjectID(objV2) + + rs := make([]v2object.Range, len(prm.Ranges)) + for i := range prm.Ranges { + rs[i].SetOffset(prm.Ranges[i].GetOffset()) + rs[i].SetLength(prm.Ranges[i].GetLength()) + } + + body := new(v2object.GetRangeHashRequestBody) + body.SetAddress(addr) + body.SetRanges(rs) + body.SetSalt(prm.Salt) + + if prm.ChecksumType == checksum.Unknown { + body.SetType(v2refs.SHA256) + } else { + body.SetType(v2refs.ChecksumType(prm.ChecksumType)) + } + + req := new(v2object.GetRangeHashRequest) + req.SetBody(body) + c.prepareRequest(req, meta) + + return req, nil +} + // ObjectHash requests checksum of the range list of the object payload using // FrostFS API protocol. // @@ -165,37 +167,22 @@ func (x ResObjectHash) Checksums() [][]byte { // - *apistatus.ObjectOutOfRange; // - *apistatus.SessionTokenExpired. func (c *Client) ObjectHash(ctx context.Context, prm PrmObjectHash) (*ResObjectHash, error) { - switch { - case prm.addr.GetContainerID() == nil: - return nil, errorMissingContainer - case prm.addr.GetObjectID() == nil: - return nil, errorMissingObject - case len(prm.body.GetRanges()) == 0: - return nil, errorMissingRanges + req, err := prm.buildRequest(c) + if err != nil { + return nil, err } - prm.body.SetAddress(&prm.addr) - if prm.csAlgo == v2refs.UnknownChecksum { - prm.body.SetType(v2refs.SHA256) - } else { - prm.body.SetType(prm.csAlgo) - } - - var req v2object.GetRangeHashRequest - c.prepareRequest(&req, &prm.meta) - req.SetBody(&prm.body) - key := c.prm.key - if prm.keySet { - key = prm.key + if prm.Key != nil { + key = *prm.Key } - err := signature.SignServiceMessage(&key, &req) + err = signature.SignServiceMessage(&key, req) if err != nil { return nil, fmt.Errorf("sign request: %w", err) } - resp, err := rpcapi.HashObjectRange(&c.c, &req, client.WithContext(ctx)) + resp, err := rpcapi.HashObjectRange(&c.c, req, client.WithContext(ctx)) if err != nil { return nil, fmt.Errorf("write request: %w", err) }