package ape import ( "crypto/ecdsa" "crypto/elliptic" "errors" "fmt" refsV2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/refs" sessionV2 "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/session" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" sessionSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" ) func getAddressParamsSDK(cidV2 *refsV2.ContainerID, objV2 *refsV2.ObjectID) (cnrID cid.ID, objID *oid.ID, err error) { if cidV2 != nil { if err = cnrID.ReadFromV2(*cidV2); err != nil { return } } else { err = errMissingContainerID return } if objV2 != nil { objID = new(oid.ID) if err = objID.ReadFromV2(*objV2); err != nil { return } } return } // originalBearerToken goes down to original request meta header and fetches // bearer token from there. func originalBearerToken(header *sessionV2.RequestMetaHeader) (*bearer.Token, error) { for header.GetOrigin() != nil { header = header.GetOrigin() } tokV2 := header.GetBearerToken() if tokV2 == nil { return nil, nil } var tok bearer.Token return &tok, tok.ReadFromV2(*tokV2) } func ownerFromToken(token *sessionSDK.Object) (*user.ID, *keys.PublicKey, error) { // 1. First check signature of session token. if !token.VerifySignature() { return nil, nil, errInvalidSessionSig } // 2. Then check if session token owner issued the session token // TODO(@cthulhu-rider): #468 implement and use another approach to avoid conversion var tokV2 sessionV2.Token token.WriteToV2(&tokV2) tokenIssuerKey, err := unmarshalPublicKey(tokV2.GetSignature().GetKey()) if err != nil { return nil, nil, fmt.Errorf("invalid key in session token signature: %w", err) } tokenIssuer := token.Issuer() if !isOwnerFromKey(tokenIssuer, tokenIssuerKey) { // TODO: #767 in this case we can issue all owner keys from frostfs.id and check once again return nil, nil, errInvalidSessionOwner } return &tokenIssuer, tokenIssuerKey, nil } func originalBodySignature(v *sessionV2.RequestVerificationHeader) *refsV2.Signature { if v == nil { return nil } for v.GetOrigin() != nil { v = v.GetOrigin() } return v.GetBodySignature() } func unmarshalPublicKey(bs []byte) (*keys.PublicKey, error) { return keys.NewPublicKeyFromBytes(bs, elliptic.P256()) } func isOwnerFromKey(id user.ID, key *keys.PublicKey) bool { if key == nil { return false } var id2 user.ID user.IDFromKey(&id2, (ecdsa.PublicKey)(*key)) return id2.Equals(id) } // assertVerb checks that token verb corresponds to the method. func assertVerb(tok sessionSDK.Object, method string) bool { switch method { case nativeschema.MethodPutObject: return tok.AssertVerb(sessionSDK.VerbObjectPut, sessionSDK.VerbObjectDelete, sessionSDK.VerbObjectPatch) case nativeschema.MethodDeleteObject: return tok.AssertVerb(sessionSDK.VerbObjectDelete) case nativeschema.MethodGetObject: return tok.AssertVerb(sessionSDK.VerbObjectGet) case nativeschema.MethodHeadObject: return tok.AssertVerb( sessionSDK.VerbObjectHead, sessionSDK.VerbObjectGet, sessionSDK.VerbObjectDelete, sessionSDK.VerbObjectRange, sessionSDK.VerbObjectRangeHash, sessionSDK.VerbObjectPatch, ) case nativeschema.MethodSearchObject: return tok.AssertVerb(sessionSDK.VerbObjectSearch, sessionSDK.VerbObjectDelete) case nativeschema.MethodRangeObject: return tok.AssertVerb(sessionSDK.VerbObjectRange, sessionSDK.VerbObjectRangeHash, sessionSDK.VerbObjectPatch) case nativeschema.MethodHashObject: return tok.AssertVerb(sessionSDK.VerbObjectRangeHash) case nativeschema.MethodPatchObject: return tok.AssertVerb(sessionSDK.VerbObjectPatch) } return false } // assertSessionRelation checks if given token describing the FrostFS session // relates to the given container and optional object. Missing object // means that the context isn't bound to any FrostFS object in the container. // Returns no error iff relation is correct. Criteria: // // session is bound to the given container // object is not specified or session is bound to this object // // Session MUST be bound to the particular container, otherwise behavior is undefined. func assertSessionRelation(tok sessionSDK.Object, cnr cid.ID, obj *oid.ID) error { if !tok.AssertContainer(cnr) { return errors.New("requested container is not related to the session") } if obj != nil && !tok.AssertObject(*obj) { return errors.New("requested object is not related to the session") } return nil } func unmarshalPublicKeyWithOwner(rawKey []byte) (*user.ID, *keys.PublicKey, error) { key, err := unmarshalPublicKey(rawKey) if err != nil { return nil, nil, fmt.Errorf("invalid signature key: %w", err) } var idSender user.ID user.IDFromKey(&idSender, (ecdsa.PublicKey)(*key)) return &idSender, key, nil }