diff --git a/code/go/0chain.net/blobbercore/handler/handler_test.go b/code/go/0chain.net/blobbercore/handler/handler_test.go index 4eb32e4cf..a960b0a32 100644 --- a/code/go/0chain.net/blobbercore/handler/handler_test.go +++ b/code/go/0chain.net/blobbercore/handler/handler_test.go @@ -45,12 +45,7 @@ type MockFileBlockGetter struct { var mockFileBlock []byte -func (MockFileBlockGetter) GetFileBlock( - fsStore *filestore.FileFSStore, - allocationID string, - fileData *filestore.FileInputData, - blockNum int64, - numBlocks int64) ([]byte, error) { +func (MockFileBlockGetter) GetFileBlock(fsStore *filestore.FileFSStore, allocationID string, fileData *filestore.FileInputData, blockNum, numBlocks int64) ([]byte, error) { return mockFileBlock, nil } @@ -62,15 +57,44 @@ func resetMockFileBlock() { mockFileBlock = []byte("mock") } -var encscheme zencryption.EncryptionScheme +// var encscheme zencryption.EncryptionScheme + +// func setupEncryptionScheme() { +// encscheme = zencryption.NewEncryptionScheme() +// mnemonic := client.GetClient().Mnemonic +// if _, err := encscheme.Initialize(mnemonic); err != nil { +// panic("initialize encscheme") +// } +// encscheme.InitForEncryption("filetype:audio") +// } + +func signHash(client *client.Client, hash string) (string, error) { + retSignature := "" + for _, kv := range client.Keys { + ss := zcncrypto.NewSignatureScheme("bls0chain") + err := ss.SetPrivateKey(kv.PrivateKey) + if err != nil { + return "", err + } + if len(retSignature) == 0 { + retSignature, err = ss.Sign(hash) + } else { + retSignature, err = ss.Add(retSignature, hash) + } + if err != nil { + return "", err + } + } + return retSignature, nil +} -func setupEncryptionScheme() { - encscheme = zencryption.NewEncryptionScheme() - mnemonic := client.GetClient().Mnemonic +func getEncryptionScheme(mnemonic string) (zencryption.EncryptionScheme, error) { + encscheme := zencryption.NewEncryptionScheme() if _, err := encscheme.Initialize(mnemonic); err != nil { - panic("initialize encscheme") + return nil, err } encscheme.InitForEncryption("filetype:audio") + return encscheme, nil } func init() { @@ -128,8 +152,8 @@ func setup(t *testing.T) { } } -func setupHandlers() (router *mux.Router, opMap map[string]string) { - router = mux.NewRouter() +func setupHandlers() (*mux.Router, map[string]string) { + router := mux.NewRouter() opPath := "/v1/file/objectpath/{allocation}" opName := "Object_Path" @@ -264,10 +288,10 @@ func isEndpointAllowGetReq(name string) bool { } } -func GetAuthTicketForEncryptedFile(allocationID, remotePath, fileHash, clientID, encPublicKey string) (string, error) { +func GetAuthTicketForEncryptedFile(ownerClient *client.Client, allocationID, remotePath, fileHash, clientID, encPublicKey string) (string, error) { at := &marker.AuthTicket{} at.AllocationID = allocationID - at.OwnerID = client.GetClientID() + at.OwnerID = ownerClient.ClientID at.ClientID = clientID at.FileName = remotePath at.FilePathHash = fileHash @@ -280,7 +304,11 @@ func GetAuthTicketForEncryptedFile(allocationID, remotePath, fileHash, clientID, at.Expiration = timestamp + 7776000 at.Timestamp = timestamp at.ReEncryptionKey = "regenkey" - err := at.Sign() + at.Encrypted = true + + hash := encryption.Hash(at.GetHashData()) + var err error + at.Signature, err = signHash(ownerClient, hash) if err != nil { return "", err } @@ -294,21 +322,39 @@ func GetAuthTicketForEncryptedFile(allocationID, remotePath, fileHash, clientID, func TestHandlers_Requiring_Signature(t *testing.T) { setup(t) - clientJson := "{\"client_id\":\"2f34516ed8c567089b7b5572b12950db34a62a07e16770da14b15b170d0d60a9\",\"client_key\":\"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083\",\"keys\":[{\"public_key\":\"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083\",\"private_key\":\"9fef6ff5edc39a79c1d8e5eb7ca7e5ac14d34615ee49e6d8ca12ecec136f5907\"}],\"mnemonics\":\"expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe\",\"version\":\"1.0\",\"date_created\":\"2021-05-30 17:45:06.492093 +0545 +0545 m=+0.139083805\"}" - require.NoError(t, client.PopulateClient(clientJson, "bls0chain")) - setupEncryptionScheme() + clientJson := `{"client_id":"2f34516ed8c567089b7b5572b12950db34a62a07e16770da14b15b170d0d60a9","client_key":"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083","keys":[{"public_key":"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083","private_key":"9fef6ff5edc39a79c1d8e5eb7ca7e5ac14d34615ee49e6d8ca12ecec136f5907"}],"mnemonics":"expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe","version":"1.0","date_created":"2021-05-30 17:45:06.492093 +0545 +0545 m=+0.139083805"}` + guestClientJson := `{"client_id":"213297e22c8282ff85d1d5c99f4967636fe68f842c1351b24bd497246cbd26d9","client_key":"7710b547897e0bddf93a28903875b244db4d320e4170172b19a5d51280c73522e9bb381b184fa3d24d6e1464882bf7f89d24ac4e8d05616d55eb857a6e235383","keys":[{"public_key":"7710b547897e0bddf93a28903875b244db4d320e4170172b19a5d51280c73522e9bb381b184fa3d24d6e1464882bf7f89d24ac4e8d05616d55eb857a6e235383","private_key":"19ca446f814dcd56e28e11d4147f73590a07c7f1a9a6012087808a8602024a08"}],"mnemonics":"crazy dutch object arrest jump fragile oak amateur taxi trigger gap aspect marriage hat slice wool island spike unlock alter include easily say ramp","version":"1.0","date_created":"2022-01-26T07:26:41+05:45"}` + + require.NoError(t, client.PopulateClients([]string{clientJson, guestClientJson}, "bls0chain")) + clients := client.GetClients() + + ownerClient, guestClient := clients[0], clients[1] + + ownerScheme, err := getEncryptionScheme(ownerClient.Mnemonic) + if err != nil { + t.Fatal(err) + } + + guestScheme, err := getEncryptionScheme(guestClient.Mnemonic) + if err != nil { + t.Fatal(err) + } + // require.NoError(t, client.PopulateClient(clientJson, "bls0chain")) + // setupEncryptionScheme() + router, handlers := setupHandlers() sch := zcncrypto.NewSignatureScheme("bls0chain") //sch.Mnemonic = "expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe" - _, err := sch.RecoverKeys("expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe") + _, err = sch.RecoverKeys("expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe") if err != nil { t.Fatal(err) } + ts := time.Now().Add(time.Hour) alloc := makeTestAllocation(common.Timestamp(ts.Unix())) - alloc.OwnerPublicKey = sch.GetPublicKey() - alloc.OwnerID = client.GetClientID() + alloc.OwnerPublicKey = ownerClient.Keys[0].PublicKey + alloc.OwnerID = ownerClient.ClientID const ( path = "/path" @@ -337,6 +383,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { if !isEndpointRequireSignature(name) { continue } + baseSetupDbMock := func(mock sqlmock.Sqlmock) { mock.ExpectBegin() @@ -371,7 +418,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { if !isEndpointAllowGetReq(name) { method = http.MethodPost } - r, err := http.NewRequest(method, url.String(), http.NoBody) + r, err := http.NewRequest(method, url.String(), nil) if err != nil { t.Fatal(err) } @@ -400,7 +447,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { if !isEndpointAllowGetReq(name) { method = http.MethodPost } - r, err := http.NewRequest(method, url.String(), http.NoBody) + r, err := http.NewRequest(method, url.String(), nil) if err != nil { t.Fatal(err) } @@ -441,7 +488,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("path", path) url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodGet, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodGet, url.String(), nil) if err != nil { t.Fatal(err) } @@ -501,7 +548,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("path", path) url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodGet, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodGet, url.String(), nil) if err != nil { t.Fatal(err) } @@ -561,7 +608,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("path", path) url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodPost, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodPost, url.String(), nil) if err != nil { t.Fatal(err) } @@ -632,7 +679,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("path", path) url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodGet, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodGet, url.String(), nil) if err != nil { t.Fatal(err) } @@ -693,7 +740,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("collab_id", "collab id") url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodGet, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodGet, url.String(), nil) if err != nil { t.Fatal(err) } @@ -762,7 +809,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("connection_id", connectionID) url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodPost, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodPost, url.String(), nil) if err != nil { t.Fatal(err) } @@ -842,7 +889,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("dest", "dest") url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodPost, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodPost, url.String(), nil) if err != nil { t.Fatal(err) } @@ -933,7 +980,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("attributes", string(attrBytes)) url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodPost, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodPost, url.String(), nil) if err != nil { t.Fatal(err) } @@ -1125,7 +1172,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { require.NoError(t, formWriter.WriteField("encryption_public_key", shareClientEncryptionPublicKey)) remotePath := "/file.txt" filePathHash := "f15383a1130bd2fae1e52a7a15c432269eeb7def555f1f8b9b9a28bd9611362c" - authTicket, err := GetAuthTicketForEncryptedFile(alloc.ID, remotePath, filePathHash, shareClientID, sch.GetPublicKey()) + authTicket, err := GetAuthTicketForEncryptedFile(ownerClient, alloc.ID, remotePath, filePathHash, shareClientID, ownerClient.Keys[0].PublicKey) if err != nil { t.Fatal(err) } @@ -1190,7 +1237,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { aa := sqlmock.AnyArg() mock.ExpectExec(`INSERT INTO "marketplace_share_info"`). - WithArgs(client.GetClientID(), "da4b54d934890aa415bb043ce1126f2e30a96faf63a4c65c25bbddcb32824d77", "f15383a1130bd2fae1e52a7a15c432269eeb7def555f1f8b9b9a28bd9611362c", "regenkey", aa, false, aa). + WithArgs("2f34516ed8c567089b7b5572b12950db34a62a07e16770da14b15b170d0d60a9", "da4b54d934890aa415bb043ce1126f2e30a96faf63a4c65c25bbddcb32824d77", "f15383a1130bd2fae1e52a7a15c432269eeb7def555f1f8b9b9a28bd9611362c", "regenkey", aa, false, aa). WillReturnResult(sqlmock.NewResult(0, 0)) }, wantCode: http.StatusOK, @@ -1214,7 +1261,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { require.NoError(t, formWriter.WriteField("encryption_public_key", shareClientEncryptionPublicKey)) remotePath := "/file.txt" filePathHash := "f15383a1130bd2fae1e52a7a15c432269eeb7def555f1f8b9b9a28bd9611362c" - authTicket, err := GetAuthTicketForEncryptedFile(alloc.ID, remotePath, filePathHash, shareClientID, sch.GetPublicKey()) + authTicket, err := GetAuthTicketForEncryptedFile(ownerClient, alloc.ID, remotePath, filePathHash, shareClientID, sch.GetPublicKey()) if err != nil { t.Fatal(err) } @@ -1363,6 +1410,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { mock.ExpectExec(regexp.QuoteMeta(`UPDATE "marketplace_share_info"`)). WithArgs(true, "da4b54d934890aa415bb043ce1126f2e30a96faf63a4c65c25bbddcb32824d77", filePathHash). WillReturnResult(sqlmock.NewResult(0, 1)) + }, wantCode: http.StatusOK, wantBody: "{\"message\":\"Path successfully removed from allocation\",\"status\":204}\n", @@ -1442,6 +1490,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { mock.ExpectExec(regexp.QuoteMeta(`UPDATE "marketplace_share_info"`)). WithArgs(true, "da4b54d934890aa415bb043ce1126f2e30a96faf63a4c65c25bbddcb32824d77", filePathHash). WillReturnResult(sqlmock.NewResult(0, 0)) + }, wantCode: http.StatusOK, wantBody: "{\"message\":\"Path not found\",\"status\":404}\n", @@ -1464,12 +1513,12 @@ func TestHandlers_Requiring_Signature(t *testing.T) { require.NoError(t, formWriter.WriteField("path_hash", fileref.GetReferenceLookup(alloc.Tx, remotePath))) require.NoError(t, formWriter.WriteField("block_num", fmt.Sprintf("%d", 1))) rm := &marker.ReadMarker{} - rm.ClientID = client.GetClientID() - rm.ClientPublicKey = client.GetClientPublicKey() + rm.ClientID = ownerClient.ClientID + rm.ClientPublicKey = ownerClient.ClientKey rm.BlobberID = "" rm.AllocationID = alloc.ID - rm.OwnerID = client.GetClientID() - err = rm.Sign() + rm.OwnerID = ownerClient.ClientID + rm.Signature, err = signHash(ownerClient, rm.GetHash()) if err != nil { t.Fatal(err) } @@ -1526,6 +1575,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { filePathHash := fileref.GetReferenceLookup(alloc.Tx, "/file.txt") mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "reference_objects" WHERE`)). WithArgs(alloc.ID, filePathHash).WillReturnError(gorm.ErrRecordNotFound) + }, wantCode: http.StatusBadRequest, wantBody: "{\"code\":\"download_file\",\"error\":\"download_file: invalid file path: record not found\"}\n\n", @@ -1548,13 +1598,13 @@ func TestHandlers_Requiring_Signature(t *testing.T) { require.NoError(t, formWriter.WriteField("path_hash", fileref.GetReferenceLookup(alloc.Tx, remotePath))) require.NoError(t, formWriter.WriteField("block_num", fmt.Sprintf("%d", 1))) rm := &marker.ReadMarker{} - rm.ClientID = client.GetClientID() - rm.ClientPublicKey = client.GetClientPublicKey() + rm.ClientID = ownerClient.ClientID + rm.ClientPublicKey = ownerClient.ClientKey rm.BlobberID = "" rm.AllocationID = alloc.ID rm.ReadCounter = 1 - rm.OwnerID = client.GetClientID() - err = rm.Sign() + rm.OwnerID = ownerClient.ClientID + rm.Signature, err = signHash(ownerClient, rm.GetHash()) if err != nil { t.Fatal(err) } @@ -1617,20 +1667,20 @@ func TestHandlers_Requiring_Signature(t *testing.T) { ) mock.ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "collaborators" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(ownerClient.ClientID). WillReturnError(gorm.ErrRecordNotFound) mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "read_markers" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(ownerClient.ClientID). WillReturnRows( sqlmock.NewRows([]string{"client_id"}). - AddRow(client.GetClientID()), + AddRow(ownerClient.ClientID), ) aa := sqlmock.AnyArg() mock.ExpectExec(`(UPDATE "read_markers" SET)(.+)`). - WithArgs(client.GetClientPublicKey(), alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa). + WithArgs(ownerClient.ClientKey, alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa). WillReturnResult(sqlmock.NewResult(0, 0)) mock.ExpectCommit() @@ -1656,20 +1706,20 @@ func TestHandlers_Requiring_Signature(t *testing.T) { pathHash := fileref.GetReferenceLookup(alloc.Tx, remotePath) require.NoError(t, formWriter.WriteField("path_hash", pathHash)) require.NoError(t, formWriter.WriteField("block_num", fmt.Sprintf("%d", 1))) - authTicket, err := GetAuthTicketForEncryptedFile(alloc.ID, remotePath, pathHash, client.GetClientID(), sch.GetPublicKey()) + authTicket, err := GetAuthTicketForEncryptedFile(ownerClient, alloc.ID, remotePath, pathHash, guestClient.ClientID, ownerClient.Keys[0].PublicKey) if err != nil { t.Fatal(err) } require.NoError(t, formWriter.WriteField("auth_token", authTicket)) rm := &marker.ReadMarker{} - rm.ClientID = client.GetClientID() - rm.ClientPublicKey = client.GetClientPublicKey() + rm.ClientID = guestClient.ClientID + rm.ClientPublicKey = guestClient.ClientKey rm.BlobberID = "" rm.AllocationID = alloc.ID rm.ReadCounter = 1 - rm.OwnerID = client.GetClientID() - err = rm.Sign() + rm.OwnerID = ownerClient.ClientID + rm.Signature, err = signHash(guestClient, rm.GetHash()) if err != nil { t.Fatal(err) } @@ -1693,8 +1743,8 @@ func TestHandlers_Requiring_Signature(t *testing.T) { r.Header.Set("Content-Type", formWriter.FormDataContentType()) r.Header.Set(common.ClientSignatureHeader, sign) - r.Header.Set(common.ClientHeader, alloc.OwnerID) - r.Header.Set(common.ClientKeyHeader, alloc.OwnerPublicKey) + r.Header.Set(common.ClientHeader, guestClient.ClientID) + r.Header.Set(common.ClientKeyHeader, guestClient.ClientKey) return r }(), @@ -1776,19 +1826,19 @@ func TestHandlers_Requiring_Signature(t *testing.T) { pathHash := fileref.GetReferenceLookup(alloc.Tx, remotePath) require.NoError(t, formWriter.WriteField("path_hash", pathHash)) require.NoError(t, formWriter.WriteField("block_num", fmt.Sprintf("%d", 1))) - authTicket, err := GetAuthTicketForEncryptedFile(alloc.ID, remotePath, pathHash, client.GetClientID(), sch.GetPublicKey()) + authTicket, err := GetAuthTicketForEncryptedFile(ownerClient, alloc.ID, remotePath, pathHash, guestClient.ClientID, "") if err != nil { t.Fatal(err) } require.NoError(t, formWriter.WriteField("auth_token", authTicket)) rm := &marker.ReadMarker{} - rm.ClientID = client.GetClientID() - rm.ClientPublicKey = client.GetClientPublicKey() + rm.ClientID = guestClient.ClientID + rm.ClientPublicKey = guestClient.ClientKey rm.BlobberID = "" rm.AllocationID = alloc.ID rm.ReadCounter = 1 - rm.OwnerID = client.GetClientID() - err = rm.Sign() + rm.OwnerID = ownerClient.ClientID + rm.Signature, err = signHash(guestClient, rm.GetHash()) if err != nil { t.Fatal(err) } @@ -1812,8 +1862,8 @@ func TestHandlers_Requiring_Signature(t *testing.T) { r.Header.Set("Content-Type", formWriter.FormDataContentType()) r.Header.Set(common.ClientSignatureHeader, sign) - r.Header.Set(common.ClientHeader, alloc.OwnerID) - r.Header.Set(common.ClientKeyHeader, alloc.OwnerPublicKey) + r.Header.Set(common.ClientHeader, guestClient.ClientID) + r.Header.Set(common.ClientKeyHeader, guestClient.ClientKey) return r }(), @@ -1821,13 +1871,15 @@ func TestHandlers_Requiring_Signature(t *testing.T) { alloc: alloc, begin: func() { dataToEncrypt := "data_to_encrypt" - encMsg, err := encscheme.Encrypt([]byte(dataToEncrypt)) + encMsg, err := ownerScheme.Encrypt([]byte(dataToEncrypt)) if err != nil { t.Fatal(err) } - header := make([]byte, 2*1024) + + header := make([]byte, HeaderChecksumSize) copy(header, encMsg.MessageChecksum+","+encMsg.OverallChecksum) data := append(header, encMsg.EncryptedData...) + fmt.Println("Encrypted data: ", string(data)) setMockFileBlock(data) }, end: func() { @@ -1861,34 +1913,43 @@ func TestHandlers_Requiring_Signature(t *testing.T) { WithArgs(alloc.ID, filePathHash). WillReturnRows( sqlmock.NewRows([]string{"path", "type", "path_hash", "lookup_hash", "content_hash", "encrypted_key", "chunk_size"}). - AddRow("/file.txt", "f", filePathHash, filePathHash, "content_hash", encscheme.GetEncryptedKey(), 65536), + AddRow("/file.txt", "f", filePathHash, filePathHash, "content_hash", ownerScheme.GetEncryptedKey(), 65536), ) mock.ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "collaborators" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(guestClient.ClientID). WillReturnError(gorm.ErrRecordNotFound) + guestPublicEncryptedKey, err := guestScheme.GetPublicKey() + if err != nil { + t.Fatal(err) + } + reEncryptionKey, err := ownerScheme.GetReGenKey(guestPublicEncryptedKey, "filetype:audio") + + if err != nil { + t.Fatal(err) + } + fmt.Printf("\n\nencryptedKey: %v\tgpbk: %v\treKey: %v\n\n", ownerScheme.GetEncryptedKey(), guestPublicEncryptedKey, reEncryptionKey) + mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "marketplace_share_info" WHERE`)). + WithArgs(guestClient.ClientID, filePathHash). + WillReturnRows( + sqlmock.NewRows([]string{"re_encryption_key", "client_encryption_public_key"}). + AddRow(reEncryptionKey, guestPublicEncryptedKey), + ) + mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "read_markers" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(guestClient.ClientID). WillReturnRows( sqlmock.NewRows([]string{"client_id"}). - AddRow(client.GetClientID()), + AddRow(guestClient.ClientID), ) aa := sqlmock.AnyArg() mock.ExpectExec(`UPDATE "read_markers"`). - WithArgs(client.GetClientPublicKey(), alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa, aa). + WithArgs(guestClient.ClientKey, alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa, aa). WillReturnResult(sqlmock.NewResult(0, 0)) - reEncryptionKey, _ := encscheme.GetReGenKey(encscheme.GetEncryptedKey(), "filetype:audio") - mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "marketplace_share_info" WHERE`)). - WithArgs(client.GetClientID(), filePathHash). - WillReturnRows( - sqlmock.NewRows([]string{"re_encryption_key", "client_encryption_public_key"}). - AddRow(reEncryptionKey, encscheme.GetEncryptedKey()), - ) - mock.ExpectCommit() }, wantCode: http.StatusOK, @@ -1915,28 +1976,30 @@ func TestHandlers_Requiring_Signature(t *testing.T) { require.NoError(t, formWriter.WriteField("path_hash", filePathHash)) require.NoError(t, formWriter.WriteField("block_num", fmt.Sprintf("%d", 1))) - authTicket, err := GetAuthTicketForEncryptedFile(alloc.ID, remotePath, pathHash, client.GetClientID(), sch.GetPublicKey()) + authTicket, err := GetAuthTicketForEncryptedFile(ownerClient, alloc.ID, remotePath, pathHash, guestClient.ClientID, "") if err != nil { t.Fatal(err) } require.NoError(t, formWriter.WriteField("auth_token", authTicket)) rm := &marker.ReadMarker{} - rm.ClientID = client.GetClientID() - rm.ClientPublicKey = client.GetClientPublicKey() + rm.ClientID = guestClient.ClientID + rm.ClientPublicKey = guestClient.ClientKey rm.BlobberID = "" rm.AllocationID = alloc.ID rm.ReadCounter = 1 - rm.OwnerID = client.GetClientID() - err = rm.Sign() + rm.OwnerID = ownerClient.ClientID + rm.Signature, err = signHash(guestClient, rm.GetHash()) if err != nil { t.Fatal(err) } + rmData, err := json.Marshal(rm) require.NoError(t, err) require.NoError(t, formWriter.WriteField("read_marker", string(rmData))) if err := formWriter.Close(); err != nil { t.Fatal(err) } + r, err := http.NewRequest(http.MethodPost, url.String(), body) r.Header.Add("Content-Type", formWriter.FormDataContentType()) if err != nil { @@ -1951,8 +2014,8 @@ func TestHandlers_Requiring_Signature(t *testing.T) { r.Header.Set("Content-Type", formWriter.FormDataContentType()) r.Header.Set(common.ClientSignatureHeader, sign) - r.Header.Set(common.ClientHeader, alloc.OwnerID) - r.Header.Set(common.ClientKeyHeader, alloc.OwnerPublicKey) + r.Header.Set(common.ClientHeader, guestClient.ClientID) + r.Header.Set(common.ClientKeyHeader, guestClient.ClientKey) return r }(), @@ -1960,11 +2023,12 @@ func TestHandlers_Requiring_Signature(t *testing.T) { alloc: alloc, begin: func() { dataToEncrypt := "data_to_encrypt" - encMsg, err := encscheme.Encrypt([]byte(dataToEncrypt)) + encMsg, err := ownerScheme.Encrypt([]byte(dataToEncrypt)) if err != nil { t.Fatal(err) } - header := make([]byte, 2*1024) + + header := make([]byte, HeaderChecksumSize) copy(header, encMsg.MessageChecksum+","+encMsg.OverallChecksum) data := append(header, encMsg.EncryptedData...) setMockFileBlock(data) @@ -2000,11 +2064,11 @@ func TestHandlers_Requiring_Signature(t *testing.T) { WithArgs(alloc.ID, filePathHash). WillReturnRows( sqlmock.NewRows([]string{"path", "type", "path_hash", "lookup_hash", "content_hash", "encrypted_key", "parent_path", "chunk_size"}). - AddRow("/file.txt", "f", filePathHash, filePathHash, "content_hash", encscheme.GetEncryptedKey(), "/", fileref.CHUNK_SIZE), + AddRow("/file.txt", "f", filePathHash, filePathHash, "content_hash", ownerScheme.GetEncryptedKey(), "/", fileref.CHUNK_SIZE), ) mock.ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "collaborators" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(guestClient.ClientID). WillReturnError(gorm.ErrRecordNotFound) rootPathHash := fileref.GetReferenceLookup(alloc.Tx, "/") @@ -2015,27 +2079,32 @@ func TestHandlers_Requiring_Signature(t *testing.T) { AddRow("/", "d", rootPathHash, rootPathHash, "content_hash", "", "."), ) + gpbk, err := guestScheme.GetPublicKey() + if err != nil { + t.Fatal(err) + } + + reEncryptionKey, _ := ownerScheme.GetReGenKey(gpbk, "filetype:audio") + mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "marketplace_share_info" WHERE`)). + WithArgs(guestClient.ClientID, rootPathHash). + WillReturnRows( + sqlmock.NewRows([]string{"re_encryption_key", "client_encryption_public_key"}). + AddRow(reEncryptionKey, gpbk), + ) + mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "read_markers" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(guestClient.ClientID). WillReturnRows( sqlmock.NewRows([]string{"client_id"}). - AddRow(client.GetClientID()), + AddRow(guestClient.ClientID), ) aa := sqlmock.AnyArg() mock.ExpectExec(`UPDATE "read_markers"`). - WithArgs(client.GetClientPublicKey(), alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa, aa). + WithArgs(guestClient.ClientKey, alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa, aa). WillReturnResult(sqlmock.NewResult(0, 0)) - reEncryptionKey, _ := encscheme.GetReGenKey(encscheme.GetEncryptedKey(), "filetype:audio") - mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "marketplace_share_info" WHERE`)). - WithArgs(client.GetClientID(), rootPathHash). - WillReturnRows( - sqlmock.NewRows([]string{"re_encryption_key", "client_encryption_public_key"}). - AddRow(reEncryptionKey, encscheme.GetEncryptedKey()), - ) - mock.ExpectCommit() }, wantCode: http.StatusOK, @@ -2062,28 +2131,30 @@ func TestHandlers_Requiring_Signature(t *testing.T) { require.NoError(t, formWriter.WriteField("path_hash", filePathHash)) require.NoError(t, formWriter.WriteField("block_num", fmt.Sprintf("%d", 1))) - authTicket, err := GetAuthTicketForEncryptedFile(alloc.ID, remotePath, pathHash, client.GetClientID(), sch.GetPublicKey()) + authTicket, err := GetAuthTicketForEncryptedFile(ownerClient, alloc.ID, remotePath, pathHash, guestClient.ClientID, "") if err != nil { t.Fatal(err) } require.NoError(t, formWriter.WriteField("auth_token", authTicket)) rm := &marker.ReadMarker{} - rm.ClientID = client.GetClientID() - rm.ClientPublicKey = client.GetClientPublicKey() + rm.ClientID = guestClient.ClientID + rm.ClientPublicKey = guestClient.ClientKey rm.BlobberID = "" rm.AllocationID = alloc.ID rm.ReadCounter = 1 - rm.OwnerID = client.GetClientID() - err = rm.Sign() + rm.OwnerID = alloc.OwnerID + rm.Signature, err = signHash(guestClient, rm.GetHash()) if err != nil { t.Fatal(err) } + rmData, err := json.Marshal(rm) require.NoError(t, err) require.NoError(t, formWriter.WriteField("read_marker", string(rmData))) if err := formWriter.Close(); err != nil { t.Fatal(err) } + r, err := http.NewRequest(http.MethodPost, url.String(), body) r.Header.Add("Content-Type", formWriter.FormDataContentType()) if err != nil { @@ -2098,8 +2169,8 @@ func TestHandlers_Requiring_Signature(t *testing.T) { r.Header.Set("Content-Type", formWriter.FormDataContentType()) r.Header.Set(common.ClientSignatureHeader, sign) - r.Header.Set(common.ClientHeader, alloc.OwnerID) - r.Header.Set(common.ClientKeyHeader, alloc.OwnerPublicKey) + r.Header.Set(common.ClientHeader, guestClient.ClientID) + r.Header.Set(common.ClientKeyHeader, guestClient.ClientKey) return r }(), @@ -2107,11 +2178,12 @@ func TestHandlers_Requiring_Signature(t *testing.T) { alloc: alloc, begin: func() { dataToEncrypt := "data_to_encrypt" - encMsg, err := encscheme.Encrypt([]byte(dataToEncrypt)) + encMsg, err := ownerScheme.Encrypt([]byte(dataToEncrypt)) if err != nil { t.Fatal(err) } - header := make([]byte, 2*1024) + + header := make([]byte, HeaderChecksumSize) copy(header, encMsg.MessageChecksum+","+encMsg.OverallChecksum) data := append(header, encMsg.EncryptedData...) setMockFileBlock(data) @@ -2147,11 +2219,11 @@ func TestHandlers_Requiring_Signature(t *testing.T) { WithArgs(alloc.ID, filePathHash). WillReturnRows( sqlmock.NewRows([]string{"path", "type", "path_hash", "lookup_hash", "content_hash", "encrypted_key", "parent_path", "chunk_size"}). - AddRow("/folder1/subfolder1/file.txt", "f", filePathHash, filePathHash, "content_hash", encscheme.GetEncryptedKey(), "/folder1/subfolder1", filestore.CHUNK_SIZE), + AddRow("/folder1/subfolder1/file.txt", "f", filePathHash, filePathHash, "content_hash", ownerScheme.GetEncryptedKey(), "/folder1/subfolder1", filestore.CHUNK_SIZE), ) mock.ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "collaborators" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(guestClient.ClientID). WillReturnError(gorm.ErrRecordNotFound) rootPathHash := fileref.GetReferenceLookup(alloc.Tx, "/folder1") @@ -2162,27 +2234,32 @@ func TestHandlers_Requiring_Signature(t *testing.T) { AddRow("/folder1", "d", rootPathHash, rootPathHash, "content_hash", "", "."), ) + gpbk, err := guestScheme.GetPublicKey() + if err != nil { + t.Fatal(err) + } + + reEncryptionKey, _ := ownerScheme.GetReGenKey(gpbk, "filetype:audio") + mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "marketplace_share_info" WHERE`)). + WithArgs(guestClient.ClientID, rootPathHash). + WillReturnRows( + sqlmock.NewRows([]string{"re_encryption_key", "client_encryption_public_key"}). + AddRow(reEncryptionKey, gpbk), + ) + mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "read_markers" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(guestClient.ClientID). WillReturnRows( sqlmock.NewRows([]string{"client_id"}). - AddRow(client.GetClientID()), + AddRow(guestClient.ClientID), ) aa := sqlmock.AnyArg() mock.ExpectExec(`UPDATE "read_markers"`). - WithArgs(client.GetClientPublicKey(), alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa, aa). + WithArgs(guestClient.ClientKey, alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa, aa). WillReturnResult(sqlmock.NewResult(0, 0)) - reEncryptionKey, _ := encscheme.GetReGenKey(encscheme.GetEncryptedKey(), "filetype:audio") - mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "marketplace_share_info" WHERE`)). - WithArgs(client.GetClientID(), rootPathHash). - WillReturnRows( - sqlmock.NewRows([]string{"re_encryption_key", "client_encryption_public_key"}). - AddRow(reEncryptionKey, encscheme.GetEncryptedKey()), - ) - mock.ExpectCommit() }, wantCode: http.StatusOK, @@ -2209,28 +2286,30 @@ func TestHandlers_Requiring_Signature(t *testing.T) { require.NoError(t, formWriter.WriteField("path_hash", filePathHash)) require.NoError(t, formWriter.WriteField("block_num", fmt.Sprintf("%d", 1))) - authTicket, err := GetAuthTicketForEncryptedFile(alloc.ID, remotePath, pathHash, client.GetClientID(), sch.GetPublicKey()) + authTicket, err := GetAuthTicketForEncryptedFile(ownerClient, alloc.ID, remotePath, pathHash, guestClient.ClientID, "") if err != nil { t.Fatal(err) } require.NoError(t, formWriter.WriteField("auth_token", authTicket)) rm := &marker.ReadMarker{} - rm.ClientID = client.GetClientID() - rm.ClientPublicKey = client.GetClientPublicKey() + rm.ClientID = guestClient.ClientID + rm.ClientPublicKey = guestClient.ClientKey rm.BlobberID = "" rm.AllocationID = alloc.ID rm.ReadCounter = 1 - rm.OwnerID = client.GetClientID() - err = rm.Sign() + rm.OwnerID = alloc.OwnerID + rm.Signature, err = signHash(guestClient, rm.GetHash()) if err != nil { t.Fatal(err) } + rmData, err := json.Marshal(rm) require.NoError(t, err) require.NoError(t, formWriter.WriteField("read_marker", string(rmData))) if err := formWriter.Close(); err != nil { t.Fatal(err) } + r, err := http.NewRequest(http.MethodPost, url.String(), body) r.Header.Add("Content-Type", formWriter.FormDataContentType()) if err != nil { @@ -2245,8 +2324,8 @@ func TestHandlers_Requiring_Signature(t *testing.T) { r.Header.Set("Content-Type", formWriter.FormDataContentType()) r.Header.Set(common.ClientSignatureHeader, sign) - r.Header.Set(common.ClientHeader, alloc.OwnerID) - r.Header.Set(common.ClientKeyHeader, alloc.OwnerPublicKey) + r.Header.Set(common.ClientHeader, guestClient.ClientID) + r.Header.Set(common.ClientKeyHeader, guestClient.ClientKey) return r }(), @@ -2254,11 +2333,12 @@ func TestHandlers_Requiring_Signature(t *testing.T) { alloc: alloc, begin: func() { dataToEncrypt := "data_to_encrypt" - encMsg, err := encscheme.Encrypt([]byte(dataToEncrypt)) + encMsg, err := ownerScheme.Encrypt([]byte(dataToEncrypt)) if err != nil { t.Fatal(err) } - header := make([]byte, 2*1024) + + header := make([]byte, HeaderChecksumSize) copy(header, encMsg.MessageChecksum+","+encMsg.OverallChecksum) data := append(header, encMsg.EncryptedData...) setMockFileBlock(data) @@ -2294,7 +2374,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { WithArgs(alloc.ID, filePathHash). WillReturnRows( sqlmock.NewRows([]string{"path", "type", "path_hash", "lookup_hash", "content_hash", "encrypted_key", "parent_path", "chunk_size"}). - AddRow("/file.txt", "f", filePathHash, filePathHash, "content_hash", encscheme.GetEncryptedKey(), "/folder2/subfolder1", fileref.CHUNK_SIZE), + AddRow("/file.txt", "f", filePathHash, filePathHash, "content_hash", ownerScheme.GetEncryptedKey(), "/folder2/subfolder1", fileref.CHUNK_SIZE), ) rootPathHash := fileref.GetReferenceLookup(alloc.Tx, "/folder1") @@ -2304,12 +2384,16 @@ func TestHandlers_Requiring_Signature(t *testing.T) { sqlmock.NewRows([]string{"path", "type", "path_hash", "lookup_hash", "content_hash", "encrypted_key", "parent_path"}). AddRow("/folder1", "d", rootPathHash, rootPathHash, "content_hash", "", "/"), ) + }, wantCode: http.StatusBadRequest, wantBody: "{\"code\":\"download_file\",\"error\":\"download_file: cannot verify auth ticket: invalid_parameters: Auth ticket is not valid for the resource being requested\"}\n\n", }, } + tests := append(positiveTests, negativeTests...) + // tests := positiveTests + for _, test := range tests { t.Run(test.name, func(t *testing.T) { mock := datastore.MockTheStore(t) @@ -2323,6 +2407,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { test.end() } + fmt.Printf("\nResponse body: %v", test.args.w.Body.String()) assert.Equal(t, test.wantCode, test.args.w.Result().StatusCode) if test.wantCode != http.StatusOK || test.wantBody != "" { assert.Equal(t, test.wantBody, test.args.w.Body.String()) diff --git a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go index 5aca5f898..b225e1331 100644 --- a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go +++ b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go @@ -11,7 +11,6 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/blobberhttp" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/stats" - "github.com/0chain/blobber/code/go/0chain.net/blobbercore/util" zencryption "github.com/0chain/gosdk/zboxcore/encryption" "net/http" @@ -30,7 +29,6 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/lock" "github.com/0chain/blobber/code/go/0chain.net/core/node" "github.com/0chain/gosdk/constants" - zfileref "github.com/0chain/gosdk/zboxcore/fileref" "gorm.io/datatypes" "gorm.io/gorm" @@ -39,6 +37,13 @@ import ( "go.uber.org/zap" ) +const ( + // EncryptionOverHead takes blockSize increment when data is incremented. + // messageCheckSum(128) + overallChecksum(128) + ","(1) + data-size-increment(16) + EncryptionOverHead = 273 + HeaderChecksumSize = 2048 // Change it to 257 after header size is fixed in chunked upload as well +) + func readPreRedeem(ctx context.Context, alloc *allocation.Allocation, numBlocks, pendNumBlocks int64, payerID string) (err error) { if numBlocks == 0 { return @@ -179,27 +184,21 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r var ( clientID = ctx.Value(constants.ContextKeyClient).(string) allocationTx = ctx.Value(constants.ContextKeyAllocation).(string) - _ = ctx.Value(constants.ContextKeyClientKey).(string) // runtime type check alloc *allocation.Allocation ) - // check client if clientID == "" { return nil, common.NewError("download_file", "invalid client") } - // get and check allocation alloc, err = fsh.verifyAllocation(ctx, allocationTx, false) if err != nil { - return nil, common.NewErrorf("download_file", - "invalid allocation id passed: %v", err) + return nil, common.NewErrorf("download_file", "invalid allocation id passed: %v", err) } - // get and parse file params if err = r.ParseMultipartForm(FormFileParseMaxMemory); err != nil { Logger.Info("download_file - request_parse_error", zap.Error(err)) - return nil, common.NewErrorf("download_file", - "request_parse_error: %v", err) + return nil, common.NewErrorf("download_file", "request_parse_error: %v", err) } pathHash, err := pathHashFromReq(r, alloc.ID) @@ -214,119 +213,93 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r var blockNum int64 blockNum, err = strconv.ParseInt(blockNumStr, 10, 64) - if err != nil || blockNum < 0 { + if err != nil || blockNum < 1 { return nil, common.NewError("download_file", "invalid block number") } - var numBlocksStr = r.FormValue("num_blocks") - if numBlocksStr == "" { - numBlocksStr = "1" - } + numBlocks := int64(1) + numBlocksStr := r.FormValue("num_blocks") - var numBlocks int64 - numBlocks, err = strconv.ParseInt(numBlocksStr, 10, 64) - if err != nil || numBlocks < 0 { - return nil, common.NewError("download_file", - "invalid number of blocks") + if numBlocksStr != "" { + numBlocks, err = strconv.ParseInt(numBlocksStr, 10, 64) + if err != nil || numBlocks <= 0 { + return nil, common.NewError("download_file", "invalid number of blocks") + } } - // get read marker - var ( - readMarkerString = r.FormValue("read_marker") - readMarker = &readmarker.ReadMarker{} - ) - err = json.Unmarshal([]byte(readMarkerString), &readMarker) - if err != nil { + readMarkerString := r.FormValue("read_marker") + readMarker := new(readmarker.ReadMarker) + + if err := json.Unmarshal([]byte(readMarkerString), readMarker); err != nil { return nil, common.NewErrorf("download_file", "invalid parameters, "+ "error parsing the readmarker for download: %v", err) } - var rmObj = &readmarker.ReadMarkerEntity{} + rmObj := new(readmarker.ReadMarkerEntity) rmObj.LatestRM = readMarker if err = rmObj.VerifyMarker(ctx, alloc); err != nil { - return nil, common.NewErrorf("download_file", "invalid read marker, "+ - "failed to verify the read marker: %v", err) + return nil, common.NewErrorf("download_file", "invalid read marker, "+"failed to verify the read marker: %v", err) } // get file reference - var fileref *reference.Ref - fileref, err = reference.GetReferenceFromLookupHash(ctx, alloc.ID, pathHash) + fileref, err := reference.GetReferenceFromLookupHash(ctx, alloc.ID, pathHash) if err != nil { - return nil, common.NewErrorf("download_file", - "invalid file path: %v", err) + return nil, common.NewErrorf("download_file", "invalid file path: %v", err) } if fileref.Type != reference.FILE { - return nil, common.NewErrorf("download_file", - "path is not a file: %v", err) + return nil, common.NewErrorf("download_file", "path is not a file: %v", err) } // set payer: default - var payerID = alloc.OwnerID + payerID := alloc.OwnerID // set payer: check for explicit allocation payer value - if len(alloc.PayerID) > 0 { + if alloc.PayerID != "" { payerID = alloc.PayerID } - // authorize file access - var ( - isOwner = clientID == alloc.OwnerID - isRepairer = clientID == alloc.RepairerID - isCollaborator = reference.IsACollaborator(ctx, fileref.ID, clientID) - ) + isOwner := clientID == alloc.OwnerID + isRepairer := clientID == alloc.RepairerID + isCollaborator := reference.IsACollaborator(ctx, fileref.ID, clientID) - var authToken *readmarker.AuthTicket = nil + var authToken *readmarker.AuthTicket + var shareInfo *reference.ShareInfo - if (!isOwner && !isRepairer && !isCollaborator) || len(r.FormValue("auth_token")) > 0 { - var authTokenString = r.FormValue("auth_token") + if !(isOwner || isCollaborator) { + authTokenString := r.FormValue("auth_token") + if authTokenString == "" { + return nil, common.NewError("invalid_client", "authticket is required") + } - // check auth token - if authToken, err := fsh.verifyAuthTicket(ctx, authTokenString, alloc, fileref, clientID); authToken == nil { + if authToken, err = fsh.verifyAuthTicket(ctx, authTokenString, alloc, fileref, clientID); authToken == nil { return nil, common.NewErrorf("download_file", "cannot verify auth ticket: %v", err) } - authToken = &readmarker.AuthTicket{} - err = json.Unmarshal([]byte(authTokenString), &authToken) - if err != nil { - return nil, common.NewErrorf("download_file", - "error parsing the auth ticket for download: %v", err) + shareInfo, err = reference.GetShareInfo(ctx, authToken.ClientID, authToken.FilePathHash) + if err != nil || shareInfo == nil { + return nil, errors.New("client does not have permission to download the file. share does not exist") + } + + if shareInfo.Revoked { + return nil, errors.New("client does not have permission to download the file. share revoked") } + // set payer: check for command line payer flag (--rx_pay) if r.FormValue("rx_pay") == "true" { payerID = clientID } - // we only check content hash if its authticket is referring to a file - if authToken.RefType == zfileref.FILE && authToken.ActualFileHash != fileref.ActualFileHash { - return nil, errors.New("content hash does not match the requested file content hash") - } - - if authToken.RefType == zfileref.DIRECTORY { - hashes := util.GetParentPathHashes(allocationTx, fileref.Path) - found := false - for _, hash := range hashes { - if hash == authToken.FilePathHash { - found = true - break - } - } - if !found { - return nil, errors.New("auth ticket is not authorized to download file specified") - } - } readMarker.AuthTicket = datatypes.JSON(authTokenString) // check for file payer flag if fileAttrs, err := fileref.GetAttributes(); err != nil { - return nil, common.NewErrorf("download_file", - "error getting file attributes: %v", err) - } else if fileAttrs.WhoPaysForReads == common.WhoPays3rdParty { + return nil, common.NewErrorf("download_file", "error getting file attributes: %v", err) + } else if fileAttrs.WhoPaysForReads == common.WhoPays3rdParty && !(isCollaborator || isRepairer) { payerID = clientID } } - // create read marker var ( rme *readmarker.ReadMarkerEntity @@ -336,33 +309,29 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r rme, err = readmarker.GetLatestReadMarkerEntity(ctx, clientID) if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { - return nil, common.NewErrorf("download_file", - "couldn't get read marker from DB: %v", err) + return nil, common.NewErrorf("download_file", "couldn't get read marker from DB: %v", err) } if rme != nil { latestRM = rme.LatestRM if pendNumBlocks, err = rme.PendNumBlocks(); err != nil { - return nil, common.NewErrorf("download_file", - "couldn't get number of blocks pending redeeming: %v", err) + return nil, common.NewErrorf("download_file", "couldn't get number of blocks pending redeeming: %v", err) } } if latestRM != nil && latestRM.ReadCounter+(numBlocks) != readMarker.ReadCounter { - var response = &blobberhttp.DownloadResponse{ + return &blobberhttp.DownloadResponse{ Success: false, LatestRM: latestRM, Path: fileref.Path, AllocationID: fileref.AllocationID, - } - return response, nil + }, nil } // check out read pool tokens if read_price > 0 err = readPreRedeem(ctx, alloc, numBlocks, pendNumBlocks, payerID) if err != nil { - return nil, common.NewErrorf("download_file", - "pre-redeeming read marker: %v", err) + return nil, common.NewErrorf("download_file", "pre-redeeming read marker: %v", err) } // reading is allowed @@ -370,18 +339,16 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r downloadMode = r.FormValue("content") respData []byte ) - if len(downloadMode) > 0 && downloadMode == DownloadContentThumb { + if downloadMode == DownloadContentThumb { var fileData = &filestore.FileInputData{} fileData.Name = fileref.Name fileData.Path = fileref.Path fileData.Hash = fileref.ThumbnailHash fileData.OnCloud = fileref.OnCloud fileData.ChunkSize = fileref.ChunkSize - respData, err = filestore.GetFileStore().GetFileBlock(alloc.ID, - fileData, blockNum, numBlocks) + respData, err = filestore.GetFileStore().GetFileBlock(alloc.ID, fileData, blockNum, numBlocks) if err != nil { - return nil, common.NewErrorf("download_file", - "couldn't get thumbnail block: %v", err) + return nil, common.NewErrorf("download_file", "couldn't get thumbnail block: %v", err) } } else { var fileData = &filestore.FileInputData{} @@ -391,48 +358,25 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r fileData.OnCloud = fileref.OnCloud fileData.ChunkSize = fileref.ChunkSize - respData, err = filestore.GetFileStore().GetFileBlock(alloc.ID, - fileData, blockNum, numBlocks) + respData, err = filestore.GetFileStore().GetFileBlock(alloc.ID, fileData, blockNum, numBlocks) if err != nil { - return nil, common.NewErrorf("download_file", - "couldn't get file block: %v", err) + return nil, common.NewErrorf("download_file", "couldn't get file block: %v", err) } } readMarker.PayerID = payerID err = readmarker.SaveLatestReadMarker(ctx, readMarker, latestRM == nil) if err != nil { - return nil, common.NewErrorf("download_file", - "couldn't save latest read marker: %v", err) - } - - var shareInfo *reference.ShareInfo - if authToken != nil { - shareInfo, err = reference.GetShareInfo( - ctx, - readMarker.ClientID, - authToken.FilePathHash, - ) - - if err == nil && shareInfo.Revoked { - return nil, errors.New("client does not have permission to download the file. share revoked") - } + Logger.Error(err.Error()) + return nil, common.NewErrorf("download_file", "couldn't save latest read marker") } - if len(fileref.EncryptedKey) > 0 && authToken != nil { - // should not happen, just in case - if shareInfo == nil { - return nil, errors.New("client does not have permission to download the file. share does not exist") - } - - buyerEncryptionPublicKey := shareInfo.ClientEncryptionPublicKey + if fileref.EncryptedKey != "" && authToken != nil { encscheme := zencryption.NewEncryptionScheme() - // reEncrypt does not require pub / private key, - // we could probably make it a classless function - if _, err := encscheme.Initialize(""); err != nil { return nil, err } + if err := encscheme.InitForDecryption("filetype:audio", fileref.EncryptedKey); err != nil { return nil, err } @@ -440,12 +384,12 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r totalSize := len(respData) result := []byte{} for i := 0; i < totalSize; i += int(fileref.ChunkSize) { - encMsg := &zencryption.EncryptedMessage{} + encMsg := new(zencryption.EncryptedMessage) chunkData := respData[i:int64(math.Min(float64(i+int(fileref.ChunkSize)), float64(totalSize)))] - encMsg.EncryptedData = chunkData[(2 * 1024):] + encMsg.EncryptedData = chunkData[HeaderChecksumSize:] - headerBytes := chunkData[:(2 * 1024)] + headerBytes := chunkData[:HeaderChecksumSize] headerBytes = bytes.Trim(headerBytes, "\x00") headerString := string(headerBytes) @@ -458,7 +402,7 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r encMsg.MessageChecksum, encMsg.OverallChecksum = headerChecksums[0], headerChecksums[1] encMsg.EncryptedKey = encscheme.GetEncryptedKey() - reEncMsg, err := encscheme.ReEncrypt(encMsg, shareInfo.ReEncryptionKey, buyerEncryptionPublicKey) + reEncMsg, err := encscheme.ReEncrypt(encMsg, shareInfo.ReEncryptionKey, shareInfo.ClientEncryptionPublicKey) if err != nil { return nil, err } diff --git a/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go b/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go index f4dc23b8d..c98dbe92d 100644 --- a/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go +++ b/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go @@ -81,13 +81,14 @@ func TestDownloadFile(t *testing.T) { parameters struct { isOwner bool isCollaborator bool + isRepairer bool useAuthTicket bool - attribute common.WhoPays - payerId client.Client isRevoked bool isFundedBlobber bool isFunded0Chain bool rxPay bool + attribute common.WhoPays + payerId client.Client // client input from gosdk's BlockDownloadRequest, inData blockDownloadRequest @@ -199,18 +200,34 @@ func TestDownloadFile(t *testing.T) { p parameters, rm marker.ReadMarker, ) { - mocket.Catcher.NewMock().OneTime().WithQuery( - `SELECT * FROM "allocations" WHERE`, - ).WithArgs( - "mock_allocation_Tx", - ).OneTime().WithReply( - []map[string]interface{}{{ - "id": p.allocation.ID, - "expiration_date": mockLongTimeInFuture, - "owner_id": mockOwner.ClientID, - "owner_public_key": mockOwner.ClientKey, - }}, - ) + if p.isRepairer { + mocket.Catcher.NewMock().OneTime().WithQuery( + `SELECT * FROM "allocations" WHERE`, + ).WithArgs( + "mock_allocation_Tx", + ).OneTime().WithReply( + []map[string]interface{}{{ + "id": p.allocation.ID, + "expiration_date": mockLongTimeInFuture, + "owner_id": mockOwner.ClientID, + "owner_public_key": mockOwner.ClientKey, + "repairer_id": mockClient.ClientID, + }}, + ) + } else { + mocket.Catcher.NewMock().OneTime().WithQuery( + `SELECT * FROM "allocations" WHERE`, + ).WithArgs( + "mock_allocation_Tx", + ).OneTime().WithReply( + []map[string]interface{}{{ + "id": p.allocation.ID, + "expiration_date": mockLongTimeInFuture, + "owner_id": mockOwner.ClientID, + "owner_public_key": mockOwner.ClientKey, + }}, + ) + } mocket.Catcher.NewMock().OneTime().WithQuery( `SELECT * FROM "terms" WHERE`, @@ -436,9 +453,17 @@ func TestDownloadFile(t *testing.T) { numBlocks: 10240, rxPay: p.rxPay, } - p.allocation = allocation.Allocation{ - ID: mockAllocationId, - Tx: mockAllocationTx, + if p.isRepairer { + p.allocation = allocation.Allocation{ + ID: mockAllocationId, + Tx: mockAllocationTx, + RepairerID: mockClient.ClientID, + } + } else { + p.allocation = allocation.Allocation{ + ID: mockAllocationId, + Tx: mockAllocationTx, + } } require.True(t, (p.isOwner && !p.isCollaborator && !p.useAuthTicket) || !p.isOwner) require.True(t, p.attribute == common.WhoPays3rdParty || p.attribute == common.WhoPaysOwner) @@ -458,6 +483,7 @@ func TestDownloadFile(t *testing.T) { parameters: parameters{ isOwner: true, isCollaborator: false, + isRepairer: false, useAuthTicket: false, attribute: common.WhoPays3rdParty, isRevoked: false, @@ -471,6 +497,7 @@ func TestDownloadFile(t *testing.T) { parameters: parameters{ isOwner: true, isCollaborator: false, + isRepairer: false, useAuthTicket: false, attribute: common.WhoPays3rdParty, isRevoked: false, @@ -484,6 +511,7 @@ func TestDownloadFile(t *testing.T) { parameters: parameters{ isOwner: true, isCollaborator: false, + isRepairer: false, useAuthTicket: false, attribute: common.WhoPays3rdParty, isRevoked: false, @@ -497,10 +525,11 @@ func TestDownloadFile(t *testing.T) { }, }, { - name: "ok_collaborator", + name: "err_collaborator_without_authticket", parameters: parameters{ isOwner: false, isCollaborator: true, + isRepairer: false, useAuthTicket: false, attribute: common.WhoPays3rdParty, isRevoked: false, @@ -508,12 +537,30 @@ func TestDownloadFile(t *testing.T) { isFunded0Chain: true, rxPay: false, }, + want: want{ + err: false, + }, + }, + { + name: "ok_collaborator_with_authticket", + parameters: parameters{ + isOwner: false, + isCollaborator: true, + isRepairer: false, + useAuthTicket: true, + attribute: common.WhoPays3rdParty, + isRevoked: false, + isFundedBlobber: true, + isFunded0Chain: true, + rxPay: false, + }, }, { name: "ok_authTicket_wp_owner", parameters: parameters{ isOwner: false, isCollaborator: false, + isRepairer: false, useAuthTicket: true, attribute: common.WhoPaysOwner, isRevoked: false, @@ -527,6 +574,7 @@ func TestDownloadFile(t *testing.T) { parameters: parameters{ isOwner: false, isCollaborator: false, + isRepairer: false, useAuthTicket: true, attribute: common.WhoPays3rdParty, isRevoked: false, @@ -540,6 +588,7 @@ func TestDownloadFile(t *testing.T) { parameters: parameters{ isOwner: false, isCollaborator: false, + isRepairer: false, useAuthTicket: true, attribute: common.WhoPays3rdParty, isRevoked: true, @@ -557,6 +606,21 @@ func TestDownloadFile(t *testing.T) { parameters: parameters{ isOwner: false, isCollaborator: false, + isRepairer: false, + useAuthTicket: true, + attribute: common.WhoPaysOwner, + isRevoked: false, + isFundedBlobber: false, + isFunded0Chain: true, + rxPay: true, + }, + }, + { + name: "ok_repairer_with_authticket", + parameters: parameters{ + isOwner: false, + isCollaborator: false, + isRepairer: true, useAuthTicket: true, attribute: common.WhoPaysOwner, isRevoked: false, @@ -565,6 +629,24 @@ func TestDownloadFile(t *testing.T) { rxPay: true, }, }, + { + name: "err_repairer_without_authticket", + parameters: parameters{ + isOwner: false, + isCollaborator: false, + isRepairer: true, + useAuthTicket: false, + attribute: common.WhoPaysOwner, + isRevoked: false, + isFundedBlobber: false, + isFunded0Chain: true, + rxPay: true, + }, + want: want{ + err: true, + errMsg: "invalid_client: authticket is required", + }, + }, } for _, test := range tests { t.Run(test.name, @@ -589,6 +671,7 @@ func TestDownloadFile(t *testing.T) { require.EqualValues(t, test.want.errMsg, err.Error()) return } + }, ) } diff --git a/code/go/0chain.net/blobbercore/handler/storage_handler.go b/code/go/0chain.net/blobbercore/handler/storage_handler.go index 36415f0ae..e543f02cc 100644 --- a/code/go/0chain.net/blobbercore/handler/storage_handler.go +++ b/code/go/0chain.net/blobbercore/handler/storage_handler.go @@ -975,19 +975,9 @@ func pathsFromReq(r *http.Request) ([]string, error) { return paths, nil } -func pathHashFromReq(r *http.Request, allocationID string) (string, error) { - var ( - pathHash = r.FormValue("path_hash") - path = r.FormValue("path") - ) - if pathHash == "" { - if path == "" { - return "", common.NewError("invalid_parameters", "Invalid path") - } - pathHash = reference.GetReferenceLookup(allocationID, path) - } - - return pathHash, nil +func pathHashFromReq(r *http.Request, allocationID string) (pathHash string, err error) { + pathHash, _, err = getPathHash(r, allocationID) + return } func getPathHash(r *http.Request, allocationID string) (pathHash, path string, err error) { diff --git a/go.mod b/go.mod index 3cc92ef50..c351a4708 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/0chain/errors v1.0.3 - github.com/0chain/gosdk v1.4.1-0.20220117131359-7dd554e5a0f5 + github.com/0chain/gosdk v1.4.7 github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/didip/tollbooth/v6 v6.1.1 github.com/go-ini/ini v1.55.0 // indirect diff --git a/go.sum b/go.sum index ecf7ab316..bba4eb086 100644 --- a/go.sum +++ b/go.sum @@ -55,8 +55,8 @@ collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/0chain/errors v1.0.3 h1:QQZPFxTfnMcRdt32DXbzRQIfGWmBsKoEdszKQDb0rRM= github.com/0chain/errors v1.0.3/go.mod h1:xymD6nVgrbgttWwkpSCfLLEJbFO6iHGQwk/yeSuYkIc= -github.com/0chain/gosdk v1.4.1-0.20220117131359-7dd554e5a0f5 h1:39ZQnIjlTe0JBIn8wrLQqMMFhbZX900qdMSveO8EUhc= -github.com/0chain/gosdk v1.4.1-0.20220117131359-7dd554e5a0f5/go.mod h1:FB2xXhQyIM1vwvQ1jC98wNclbDTBwqrG+Z/IQC0LaBs= +github.com/0chain/gosdk v1.4.7 h1:xxy80bIWthbVbgK/r+BLldEkwWwpAsFZKiziZ5GxawQ= +github.com/0chain/gosdk v1.4.7/go.mod h1:G/JUrqvT2WStxFbSpJKnU1Wt37GyatimoqPJfEE10bs= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4=