Compare commits

...

12 commits

Author SHA1 Message Date
cd2e46a17c [#10] Add __FROSTFS__ system attributes
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-03-13 09:59:22 +03:00
c46cd37f71 [#10] Generate api
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-03-13 09:59:22 +03:00
ec0d0274fa [#3] signature: Add buffer pool
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2023-03-10 13:36:26 +03:00
73fde0e37c [#3] signature: Verify parts in parallel
Verify request/response parts in parallel

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2023-03-10 13:36:26 +03:00
611f73ad0f [#3] signature: Sign parts in parallel
Sign request/response parts in parallel

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2023-03-10 13:36:26 +03:00
e073c996fc [#3] signature: Add benchmarks
Add benchmarks for sign and verify methods

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2023-03-10 13:36:26 +03:00
d005bf0393 [#3] signature: Refactor sign and verify
Split methods to separate files, drop redundant intefaces

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2023-03-10 13:36:26 +03:00
63eb4dc3ea [#3] api-go: Go version up
Update go version 1.17 -> 1.18

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2023-03-10 13:36:26 +03:00
3f7cb1b5ef [#7] .github: Remove directory
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-09 19:47:04 +00:00
bd67469f43 [#7] pre-commit: Add gitlint hook
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-09 19:47:04 +00:00
3af7645abf [#7] pre-commit: Add golangci-lint hook
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-09 19:47:04 +00:00
5faee63f60 [#7] pre-commit: Add initial configuration
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-09 19:47:04 +00:00
31 changed files with 564 additions and 548 deletions

70
.github/logo.svg vendored
View file

@ -1,70 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 184.2 51.8" style="enable-background:new 0 0 184.2 51.8;" xml:space="preserve">
<style type="text/css">
.st0{display:none;}
.st1{display:inline;}
.st2{fill:#01E397;}
.st3{display:inline;fill:#010032;}
.st4{display:inline;fill:#00E599;}
.st5{display:inline;fill:#00AF92;}
.st6{fill:#00C3E5;}
</style>
<g id="Layer_2">
<g id="Layer_1-2" class="st0">
<g class="st1">
<path class="st2" d="M146.6,18.3v7.2h10.9V29h-10.9v10.7h-4V14.8h18v3.5H146.6z"/>
<path class="st2" d="M180,15.7c1.7,0.9,3,2.2,4,3.8l-3,2.7c-0.6-1.3-1.5-2.4-2.6-3.3c-1.3-0.7-2.8-1-4.3-1
c-1.4-0.1-2.8,0.3-4,1.1c-0.9,0.5-1.5,1.5-1.4,2.6c0,1,0.5,1.9,1.4,2.4c1.5,0.8,3.2,1.3,4.9,1.5c1.9,0.3,3.7,0.8,5.4,1.6
c1.2,0.5,2.2,1.3,2.9,2.3c0.6,1,1,2.2,0.9,3.4c0,1.4-0.5,2.7-1.3,3.8c-0.9,1.2-2.1,2.1-3.5,2.6c-1.7,0.6-3.4,0.9-5.2,0.8
c-5,0-8.6-1.6-10.7-5l2.9-2.8c0.7,1.4,1.8,2.5,3.1,3.3c1.5,0.7,3.1,1.1,4.7,1c1.5,0.1,2.9-0.2,4.2-0.9c0.9-0.5,1.5-1.5,1.5-2.6
c0-0.9-0.5-1.8-1.3-2.2c-1.5-0.7-3.1-1.2-4.8-1.5c-1.9-0.3-3.7-0.8-5.5-1.5c-1.2-0.5-2.2-1.4-3-2.4c-0.6-1-1-2.2-0.9-3.4
c0-1.4,0.4-2.7,1.2-3.8c0.8-1.2,2-2.2,3.3-2.8c1.6-0.7,3.4-1.1,5.2-1C176.1,14.3,178.2,14.8,180,15.7z"/>
</g>
<path class="st3" d="M73.3,16.3c1.9,1.9,2.9,4.5,2.7,7.1v15.9h-4V24.8c0-2.6-0.5-4.5-1.6-5.7c-1.2-1.2-2.8-1.8-4.5-1.7
c-1.3,0-2.5,0.3-3.7,0.8c-1.2,0.7-2.2,1.7-2.9,2.9c-0.8,1.5-1.1,3.2-1.1,4.9v13.3h-4V15.1l3.6,1.5v1.7c0.8-1.5,2.1-2.6,3.6-3.3
c1.5-0.8,3.2-1.2,4.9-1.1C68.9,13.8,71.3,14.7,73.3,16.3z"/>
<path class="st3" d="M104.4,28.3H85.6c0.1,2.2,1,4.3,2.5,5.9c1.5,1.4,3.5,2.2,5.6,2.1c1.6,0.1,3.2-0.2,4.6-0.9
c1.1-0.6,2-1.6,2.5-2.8l3.3,1.8c-0.9,1.7-2.3,3.1-4,4c-2,1-4.2,1.5-6.4,1.4c-3.7,0-6.7-1.1-8.8-3.4s-3.2-5.5-3.2-9.6s1-7.2,3-9.5
s5-3.4,8.7-3.4c2.1-0.1,4.2,0.5,6.1,1.5c1.6,1,3,2.5,3.8,4.2c0.9,1.8,1.3,3.9,1.3,5.9C104.6,26.4,104.6,27.4,104.4,28.3z
M88.1,19.3c-1.4,1.5-2.2,3.4-2.4,5.5h15.1c-0.2-2-1-3.9-2.3-5.5c-1.4-1.3-3.2-2-5.1-1.9C91.5,17.3,89.6,18,88.1,19.3z"/>
<path class="st3" d="M131,17.3c2.2,2.3,3.2,5.5,3.2,9.5s-1,7.3-3.2,9.6s-5.1,3.4-8.8,3.4s-6.7-1.1-8.9-3.4s-3.2-5.5-3.2-9.6
s1.1-7.2,3.2-9.5s5.1-3.4,8.9-3.4S128.9,15,131,17.3z M116.2,19.9c-1.5,2-2.2,4.4-2.1,6.9c-0.2,2.5,0.6,5,2.1,7
c1.5,1.7,3.7,2.7,6,2.6c2.3,0.1,4.4-0.9,5.9-2.6c1.5-2,2.3-4.5,2.1-7c0.1-2.5-0.6-4.9-2.1-6.9c-1.5-1.7-3.6-2.7-5.9-2.6
C119.9,17.2,117.7,18.2,116.2,19.9z"/>
<polygon class="st4" points="0,9.1 0,43.7 22.5,51.8 22.5,16.9 46.8,7.9 24.8,0 "/>
<polygon class="st5" points="24.3,17.9 24.3,36.8 46.8,44.9 46.8,9.6 "/>
</g>
<g>
<g>
<path class="st6" d="M41.6,17.5H28.2v6.9h10.4v3.3H28.2v10.2h-3.9V14.2h17.2V17.5z"/>
<path class="st6" d="M45.8,37.9v-18h3.3l0.4,3.2c0.5-1.2,1.2-2.1,2.1-2.7c0.9-0.6,2.1-0.9,3.5-0.9c0.4,0,0.7,0,1.1,0.1
c0.4,0.1,0.7,0.2,0.9,0.3l-0.5,3.4c-0.3-0.1-0.6-0.2-0.9-0.2C55.4,23,54.9,23,54.4,23c-0.7,0-1.5,0.2-2.2,0.6
c-0.7,0.4-1.3,1-1.8,1.8s-0.7,1.8-0.7,3v9.5H45.8z"/>
<path class="st6" d="M68.6,19.6c1.8,0,3.3,0.4,4.6,1.1c1.3,0.7,2.4,1.8,3.1,3.2s1.1,3.1,1.1,5c0,1.9-0.4,3.6-1.1,5
c-0.8,1.4-1.8,2.5-3.1,3.2c-1.3,0.7-2.9,1.1-4.6,1.1s-3.3-0.4-4.6-1.1c-1.3-0.7-2.4-1.8-3.2-3.2c-0.8-1.4-1.2-3.1-1.2-5
c0-1.9,0.4-3.6,1.2-5s1.8-2.5,3.2-3.2C65.3,19.9,66.8,19.6,68.6,19.6z M68.6,22.6c-1.1,0-2,0.2-2.8,0.7c-0.8,0.5-1.3,1.2-1.7,2.1
s-0.6,2.1-0.6,3.5c0,1.3,0.2,2.5,0.6,3.4s1,1.7,1.7,2.2s1.7,0.7,2.8,0.7c1.1,0,2-0.2,2.7-0.7c0.7-0.5,1.3-1.2,1.7-2.2
s0.6-2.1,0.6-3.4c0-1.4-0.2-2.5-0.6-3.5s-1-1.6-1.7-2.1C70.6,22.8,69.6,22.6,68.6,22.6z"/>
<path class="st6" d="M89.2,38.3c-1.8,0-3.4-0.3-4.9-1c-1.5-0.7-2.7-1.7-3.5-3l2.7-2.3c0.5,1,1.3,1.8,2.3,2.4
c1,0.6,2.2,0.9,3.6,0.9c1.1,0,2-0.2,2.6-0.6c0.6-0.4,1-0.9,1-1.6c0-0.5-0.2-0.9-0.5-1.2s-0.9-0.6-1.7-0.8l-3.8-0.8
c-1.9-0.4-3.3-1-4.1-1.9c-0.8-0.9-1.2-1.9-1.2-3.3c0-1,0.3-1.9,0.9-2.7c0.6-0.8,1.4-1.5,2.5-2s2.5-0.8,4-0.8c1.8,0,3.3,0.3,4.6,1
c1.3,0.6,2.2,1.5,2.9,2.7l-2.7,2.2c-0.5-1-1.1-1.7-2-2.1c-0.9-0.5-1.8-0.7-2.8-0.7c-0.8,0-1.4,0.1-2,0.3c-0.6,0.2-1,0.5-1.3,0.8
c-0.3,0.3-0.4,0.7-0.4,1.2c0,0.5,0.2,0.9,0.5,1.3s1,0.6,1.9,0.8l4.1,0.9c1.7,0.3,2.9,0.9,3.7,1.7c0.7,0.8,1.1,1.8,1.1,2.9
c0,1.2-0.3,2.2-0.9,3c-0.6,0.9-1.5,1.6-2.6,2C92.1,38.1,90.7,38.3,89.2,38.3z"/>
<path class="st6" d="M112.8,19.9v3H99.3v-3H112.8z M106.6,14.6v17.9c0,0.9,0.2,1.5,0.7,1.9c0.5,0.4,1.1,0.6,1.9,0.6
c0.6,0,1.2-0.1,1.7-0.3c0.5-0.2,0.9-0.5,1.3-0.8l0.9,2.8c-0.6,0.5-1.2,0.9-2,1.1c-0.8,0.3-1.7,0.4-2.7,0.4c-1,0-2-0.2-2.8-0.5
s-1.5-0.9-2-1.6c-0.5-0.8-0.7-1.7-0.8-3V15.7L106.6,14.6z"/>
<path d="M137.9,17.5h-13.3v6.9h10.4v3.3h-10.4v10.2h-3.9V14.2h17.2V17.5z"/>
<path d="M150.9,13.8c2.1,0,4,0.4,5.5,1.2c1.6,0.8,2.9,2,4,3.5l-2.6,2.5c-0.9-1.4-1.9-2.4-3.1-3c-1.1-0.6-2.5-0.9-4-0.9
c-1.2,0-2.1,0.2-2.8,0.5c-0.7,0.3-1.3,0.7-1.6,1.2c-0.3,0.5-0.5,1.1-0.5,1.7c0,0.7,0.3,1.4,0.8,1.9c0.5,0.6,1.5,1,2.9,1.3
l4.8,1.1c2.3,0.5,3.9,1.3,4.9,2.3c1,1,1.4,2.3,1.4,3.9c0,1.5-0.4,2.7-1.2,3.8c-0.8,1.1-1.9,1.9-3.3,2.5s-3.1,0.9-5,0.9
c-1.7,0-3.2-0.2-4.5-0.6c-1.3-0.4-2.5-1-3.5-1.8c-1-0.7-1.8-1.6-2.5-2.6l2.7-2.7c0.5,0.8,1.1,1.6,1.9,2.2
c0.8,0.7,1.7,1.2,2.7,1.5c1,0.4,2.2,0.5,3.4,0.5c1.1,0,2.1-0.1,2.9-0.4c0.8-0.3,1.4-0.7,1.8-1.2c0.4-0.5,0.6-1.1,0.6-1.9
c0-0.7-0.2-1.3-0.7-1.8c-0.5-0.5-1.3-0.9-2.6-1.2l-5.2-1.2c-1.4-0.3-2.6-0.8-3.6-1.3c-0.9-0.6-1.6-1.3-2.1-2.1s-0.7-1.8-0.7-2.8
c0-1.3,0.4-2.6,1.1-3.7c0.7-1.1,1.8-2,3.2-2.6C147.3,14.1,148.9,13.8,150.9,13.8z"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.5 KiB

View file

@ -1,21 +0,0 @@
name: DCO check
on:
pull_request:
branches:
- master
jobs:
commits_check_job:
runs-on: ubuntu-latest
name: Commits Check
steps:
- name: Get PR Commits
id: 'get-pr-commits'
uses: tim-actions/get-pr-commits@master
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: DCO Check
uses: tim-actions/dco@master
with:
commits: ${{ steps.get-pr-commits.outputs.commits }}

View file

@ -1,64 +0,0 @@
name: neofs-api-go tests
on:
push:
branches:
- master
paths-ignore:
- '*.md'
pull_request:
branches:
- master
paths-ignore:
- '*.md'
jobs:
test:
runs-on: ubuntu-20.04
strategy:
matrix:
go: [ '1.17.x', '1.18.x', '1.19.x' ]
steps:
- name: Setup go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go }}
- name: Check out code
uses: actions/checkout@v3
- name: Cache go mod
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-${{ matrix.go }}-
- name: Get dependencies
run: make dep
- name: Run go test
run: go test -coverprofile=coverage.txt -covermode=atomic ./...
- name: Codecov
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
run: bash <(curl -s https://codecov.io/bash)
lint:
runs-on: ubuntu-20.04
steps:
- name: Setup go
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Check out code
uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.48.0
only-new-issues: true

10
.gitlint Normal file
View file

@ -0,0 +1,10 @@
[general]
fail-without-commits=true
contrib=CC1
[title-match-regex]
regex=^\[\#[0-9]+\]\s
[ignore-by-title]
regex=^Release(.*)
ignore=title-match-regex

View file

@ -11,7 +11,7 @@ run:
skip-files:
- (^|.*/)grpc/(.*)
# output configuration options
output:
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
@ -64,4 +64,4 @@ issues:
- linters: # ignore SA6002 since we use pool of []byte, however we can switch to *bytes.Buffer
- staticcheck
text: "SA6002:"
text: "SA6002:"

30
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,30 @@
ci:
autofix_prs: false
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-executables-have-shebangs
- id: check-shebang-scripts-are-executable
- id: check-merge-conflict
- id: check-json
- id: check-xml
- id: check-yaml
- id: trailing-whitespace
args: [--markdown-linebreak-ext=md]
- id: end-of-file-fixer
exclude: ".key$"
- repo: https://github.com/golangci/golangci-lint
rev: v1.51.2
hooks:
- id: golangci-lint
- repo: https://github.com/jorisroovers/gitlint
rev: v0.18.0
hooks:
- id: gitlint
stages: [commit-msg]

View file

@ -22,7 +22,7 @@
- Mark all expiration methods as deprecated (#417)
### Updated
- Minimal go version to 1.17 (#412)
- Minimal go version to 1.17 (#412)
- `neofs-crypto` to `v0.4.0` (#412)
- `google.golang.org/grpc` to `v1.48.0` (#415)
- `google.golang.org/protobuf` to `v1.28.0` (#415)
@ -60,7 +60,7 @@ NeoFS API v2.13 support
### Fixed
- Incompatible changes in signature scheme (#380)
### Added
- Public URI-parsing function `client.ParseURI()` (#383)
@ -92,9 +92,9 @@ NeoFS API v2.12 support
## [2.11.0] - 2021-12-02 - Sinjido (신지도, 薪智島)
NeoFS API v2.11 support. High level packages are moved to
[neofs-sdk-go](https://github.com/nspcc-dev/neofs-sdk-go) repository.
Repository restructured as Go module version 2 and synced with
NeoFS API v2.11 support. High level packages are moved to
[neofs-sdk-go](https://github.com/nspcc-dev/neofs-sdk-go) repository.
Repository restructured as Go module version 2 and synced with
[neofs-api](https://github.com/nspcc-dev/neofs-api) release version.
### Fixed
@ -109,7 +109,7 @@ Repository restructured as Go module version 2 and synced with
- neofs-api-go is now Go module version 2 (#201)
### Removed
- All packages from `pkg` moved to
- All packages from `pkg` moved to
[neofs-sdk-go](https://github.com/nspcc-dev/neofs-sdk-go) (#201)
## [1.30.0] - 2021-10-19 - Udo (우도, 牛島)
@ -126,7 +126,7 @@ NeoFS API v2.10 support.
- pkg/client callback to parse internal response information (#337)
- Service filter type in extended ACL from API v2.10 (#338)
- Enhanced network info structures from API v2.10 (#339)
- Well-known public-append basic ACL constant (#341)
- Well-known public-append basic ACL constant (#341)
- Native contract names support (#351)
### Changed
@ -258,7 +258,7 @@ Raw client and support of NeoFS API v2.5.0 "Jebudo" release.
### Added
- Raw client for peer to peer communication.
- `client.WithKey` option to sign messages with different keys within single
- `client.WithKey` option to sign messages with different keys within single
client.
- `Content-Type` well-known object attribute constant.
@ -271,7 +271,7 @@ Raw client and support of NeoFS API v2.5.0 "Jebudo" release.
Support changes from NeoFS API v2.4.0 "Ganghwado" release.
### Added
### Added
- `netmap.NetworkInfo` definitions in `v2` and `pkg/netmap`.
- `netmap.NetworkInfo` RPC support in `pkg/client`.
@ -322,7 +322,7 @@ Support changes from NeoFS API v2.2.1 release.
### Fixed
- Remarks of the updated linter.
- Remarks of the updated linter.
## [1.22.0] - 2020-12-30 - Yeouido (여의도, 汝矣島)
@ -335,7 +335,7 @@ Support changes from NeoFS API v2.2.0 "Yeouido" release.
- Support of `StorageGroup` message.
- Support of `DataAuditResult` message.
- Stringer and string parser for `Checksum` type of client library.
- Stringer and string parser for `Type` message.
- Stringer and string parser for `Type` message.
- Stringer and string parser for `Type` type of client library.
- `AddTypeFilter` method on `SearchFilters` type of client library
that adds filter by object type.
@ -350,7 +350,7 @@ Support changes from NeoFS API v2.2.0 "Yeouido" release.
- `Container.SetNonceUUID` setter of container nonce in UUID format.
- `NewVerifiedContainerFromV2` container constructor that preliminary
checks if container message argument meets NeoFS API V2 specification.
### Changed
- `Container.Nonce`/`Container.SetNonce` marked as deprecated.
@ -368,7 +368,7 @@ Support neofs-api v2.1.1.
### Added
- `client.GetVerifiedContainerStructure` function to check
- `client.GetVerifiedContainerStructure` function to check
that the container structure matches the requested identifier.
## [1.21.0] - 2020-12-11 - Modo (모도, 茅島)
@ -391,7 +391,7 @@ Support neofs-api v2.1.1.
### Renamed
- `AddLeafFilter` to `AddPhyFilter`
- `AddLeafFilter` to `AddPhyFilter`
### Fixed
@ -425,9 +425,9 @@ Support neofs-api v2.1.1.
## [1.20.0] - 2020-11-16 - Jindo (진도, 珍島)
Major API refactoring and simplification. From now on this library will have
backward compatibility and support of major versions of NeoFS-API by having
**version specific** files in `vN` dirs and **version independent** SDK
Major API refactoring and simplification. From now on this library will have
backward compatibility and support of major versions of NeoFS-API by having
**version specific** files in `vN` dirs and **version independent** SDK
structures and client in `pkg`. This version supports NeoFS-API v2.0.X

0
Makefile Normal file → Executable file
View file

View file

@ -1,7 +1,7 @@
package container
// SysAttributePrefix is a prefix of key to system attribute.
const SysAttributePrefix = "__NEOFS__"
const SysAttributePrefix = "__FROSTFS__"
const (
// SysAttributeSubnet is a string ID of container's storage subnet.
@ -17,6 +17,23 @@ const (
SysAttributeHomomorphicHashing = SysAttributePrefix + "DISABLE_HOMOMORPHIC_HASHING"
)
// SysAttributePrefixNeoFS is a prefix of key to system attribute.
const SysAttributePrefixNeoFS = "__NEOFS__"
const (
// SysAttributeSubnetNeoFS is a string ID of container's storage subnet.
SysAttributeSubnetNeoFS = SysAttributePrefixNeoFS + "SUBNET"
// SysAttributeNameNeoFS is a string of human-friendly container name registered as the domain in NNS contract.
SysAttributeNameNeoFS = SysAttributePrefixNeoFS + "NAME"
// SysAttributeZoneNeoFS is a string of zone for container name.
SysAttributeZoneNeoFS = SysAttributePrefixNeoFS + "ZONE"
// SysAttributeHomomorphicHashingNeoFS is a container's homomorphic hashing state.
SysAttributeHomomorphicHashingNeoFS = SysAttributePrefixNeoFS + "DISABLE_HOMOMORPHIC_HASHING"
)
// SysAttributeZoneDefault is a default value for SysAttributeZone attribute.
const SysAttributeZoneDefault = "container"
@ -33,7 +50,7 @@ const disabledHomomorphicHashingValue = "true"
// See also SetHomomorphicHashingState.
func (c Container) HomomorphicHashingState() bool {
for i := range c.attr {
if c.attr[i].GetKey() == SysAttributeHomomorphicHashing {
if c.attr[i].GetKey() == SysAttributeHomomorphicHashing || c.attr[i].GetKey() == SysAttributeHomomorphicHashingNeoFS {
return c.attr[i].GetValue() != disabledHomomorphicHashingValue
}
}
@ -50,7 +67,7 @@ func (c Container) HomomorphicHashingState() bool {
// See also HomomorphicHashingState.
func (c *Container) SetHomomorphicHashingState(enable bool) {
for i := range c.attr {
if c.attr[i].GetKey() == SysAttributeHomomorphicHashing {
if c.attr[i].GetKey() == SysAttributeHomomorphicHashing || c.attr[i].GetKey() == SysAttributeHomomorphicHashingNeoFS {
if enable {
// approach without allocation/waste
// coping works since the attributes

Binary file not shown.

6
go.mod
View file

@ -1,10 +1,11 @@
module git.frostfs.info/TrueCloudLab/frostfs-api-go/v2
go 1.17
go 1.18
require (
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0
github.com/stretchr/testify v1.7.0
golang.org/x/sync v0.1.0
google.golang.org/grpc v1.48.0
google.golang.org/protobuf v1.28.0
)
@ -13,10 +14,11 @@ require (
git.frostfs.info/TrueCloudLab/rfc6979 v0.4.0 // indirect
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 // indirect
golang.org/x/sys v0.1.0 // indirect
golang.org/x/text v0.3.3 // indirect
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect

9
go.sum
View file

@ -46,8 +46,9 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
@ -82,13 +83,16 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -98,7 +102,6 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=

BIN
lock/grpc/types.pb.go generated

Binary file not shown.

View file

@ -9,7 +9,10 @@ import (
)
// prefix of keys to subnet attributes.
const attrSubnetPrefix = "__NEOFS__SUBNET_"
const attrSubnetPrefix = "__FROSTFS__SUBNET_"
// prefix of keys to subnet attributes.
const attrSubnetPrefixNeoFS = "__NEOFS__SUBNET_"
const (
// subnet attribute's value denoting subnet entry
@ -62,7 +65,7 @@ func subnetAttributeKey(id *refs.SubnetID) string {
// - disables non-zero subnet;
// - enables zero subnet.
//
// Attribute key is calculated from ID using format `__NEOFS__SUBNET_%s`.
// Attribute key is calculated from ID using format `__FROSTFS__SUBNET_%s`.
// Attribute Value is:
// - `True` if node enters the subnet;
// - `False`, otherwise.
@ -143,8 +146,11 @@ func IterateSubnets(node *NodeInfo, f func(refs.SubnetID) error) error {
// cut subnet ID string
idTxt := strings.TrimPrefix(key, attrSubnetPrefix)
if len(idTxt) == len(key) {
// not a subnet attribute
continue
idTxt = strings.TrimPrefix(key, attrSubnetPrefixNeoFS)
if len(idTxt) == len(key) {
// not a subnet attribute
continue
}
}
// check value

View file

@ -11,7 +11,7 @@ import (
)
func subnetAttrKey(val string) string {
return "__NEOFS__SUBNET_" + val
return "__FROSTFS__SUBNET_" + val
}
func assertSubnetAttrKey(t *testing.T, attr *netmap.Attribute, num uint32) {

BIN
netmap/grpc/types.pb.go generated

Binary file not shown.

View file

@ -7,7 +7,7 @@ import (
)
// SysAttributePrefix is a prefix of key to system attribute.
const SysAttributePrefix = "__NEOFS__"
const SysAttributePrefix = "__FROSTFS__"
const (
// SysAttributeUploadID marks smaller parts of a split bigger object.
@ -25,6 +25,25 @@ const (
SysAttributeTickTopic = SysAttributePrefix + "TICK_TOPIC"
)
// SysAttributePrefixNeoFS is a prefix of key to system attribute.
const SysAttributePrefixNeoFS = "__NEOFS__"
const (
// SysAttributeUploadIDNeoFS marks smaller parts of a split bigger object.
SysAttributeUploadIDNeoFS = SysAttributePrefixNeoFS + "UPLOAD_ID"
// SysAttributeExpEpochNeoFS tells GC to delete object after that epoch.
SysAttributeExpEpochNeoFS = SysAttributePrefixNeoFS + "EXPIRATION_EPOCH"
// SysAttributeTickEpochNeoFS defines what epoch must produce object
// notification.
SysAttributeTickEpochNeoFS = SysAttributePrefixNeoFS + "TICK_EPOCH"
// SysAttributeTickTopicNeoFS defines what topic object notification
// must be sent to.
SysAttributeTickTopicNeoFS = SysAttributePrefixNeoFS + "TICK_TOPIC"
)
// NotificationInfo groups information about object notification
// that can be written to object.
//
@ -81,10 +100,10 @@ func WriteNotificationInfo(o *Object, ni NotificationInfo) {
for i := range attrs {
switch attrs[i].GetKey() {
case SysAttributeTickEpoch:
case SysAttributeTickEpoch, SysAttributeTickEpochNeoFS:
attrs[i].SetValue(epoch)
changedEpoch = true
case SysAttributeTickTopic:
case SysAttributeTickTopic, SysAttributeTickTopicNeoFS:
changedTopic = true
if topic == "" {
@ -141,7 +160,7 @@ func GetNotificationInfo(o *Object) (*NotificationInfo, error) {
for _, attr := range o.GetHeader().GetAttributes() {
switch key := attr.GetKey(); key {
case SysAttributeTickEpoch:
case SysAttributeTickEpoch, SysAttributeTickEpochNeoFS:
epoch, err := strconv.ParseUint(attr.GetValue(), 10, 64)
if err != nil {
return nil, fmt.Errorf("could not parse epoch: %w", err)
@ -150,7 +169,7 @@ func GetNotificationInfo(o *Object) (*NotificationInfo, error) {
ni.SetEpoch(epoch)
foundEpoch = true
case SysAttributeTickTopic:
case SysAttributeTickTopic, SysAttributeTickTopicNeoFS:
ni.SetTopic(attr.GetValue())
}
}

Binary file not shown.

BIN
object/grpc/types.pb.go generated

Binary file not shown.

BIN
session/grpc/types.pb.go generated

Binary file not shown.

View file

@ -1,7 +1,7 @@
package session
// ReservedXHeaderPrefix is a prefix of keys to "well-known" X-headers.
const ReservedXHeaderPrefix = "__NEOFS__"
const ReservedXHeaderPrefix = "__FROSTFS__"
const (
// XHeaderNetmapEpoch is a key to the reserved X-header that specifies netmap epoch
@ -14,3 +14,18 @@ const (
// set, the current epoch only will be used.
XHeaderNetmapLookupDepth = ReservedXHeaderPrefix + "NETMAP_LOOKUP_DEPTH"
)
// ReservedXHeaderPrefixNeoFS is a prefix of keys to "well-known" X-headers.
const ReservedXHeaderPrefixNeoFS = "__NEOFS__"
const (
// XHeaderNetmapEpochNeoFS is a key to the reserved X-header that specifies netmap epoch
// to use for object placement calculation. If set to '0' or not set, the current
// epoch only will be used.
XHeaderNetmapEpochNeoFS = ReservedXHeaderPrefixNeoFS + "NETMAP_EPOCH"
// XHeaderNetmapLookupDepthNeoFS is a key to the reserved X-header that limits
// how many past epochs back the node will can lookup. If set to '0' or not
// set, the current epoch only will be used.
XHeaderNetmapLookupDepthNeoFS = ReservedXHeaderPrefixNeoFS + "NETMAP_LOOKUP_DEPTH"
)

115
signature/body.go Normal file
View file

@ -0,0 +1,115 @@
package signature
import (
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/reputation"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
)
func serviceMessageBody(req interface{}) stableMarshaler {
switch v := req.(type) {
default:
panic(fmt.Sprintf("unsupported session message %T", req))
/* Accounting */
case *accounting.BalanceRequest:
return v.GetBody()
case *accounting.BalanceResponse:
return v.GetBody()
/* Session */
case *session.CreateRequest:
return v.GetBody()
case *session.CreateResponse:
return v.GetBody()
/* Container */
case *container.PutRequest:
return v.GetBody()
case *container.PutResponse:
return v.GetBody()
case *container.DeleteRequest:
return v.GetBody()
case *container.DeleteResponse:
return v.GetBody()
case *container.GetRequest:
return v.GetBody()
case *container.GetResponse:
return v.GetBody()
case *container.ListRequest:
return v.GetBody()
case *container.ListResponse:
return v.GetBody()
case *container.SetExtendedACLRequest:
return v.GetBody()
case *container.SetExtendedACLResponse:
return v.GetBody()
case *container.GetExtendedACLRequest:
return v.GetBody()
case *container.GetExtendedACLResponse:
return v.GetBody()
case *container.AnnounceUsedSpaceRequest:
return v.GetBody()
case *container.AnnounceUsedSpaceResponse:
return v.GetBody()
/* Object */
case *object.PutRequest:
return v.GetBody()
case *object.PutResponse:
return v.GetBody()
case *object.GetRequest:
return v.GetBody()
case *object.GetResponse:
return v.GetBody()
case *object.HeadRequest:
return v.GetBody()
case *object.HeadResponse:
return v.GetBody()
case *object.SearchRequest:
return v.GetBody()
case *object.SearchResponse:
return v.GetBody()
case *object.DeleteRequest:
return v.GetBody()
case *object.DeleteResponse:
return v.GetBody()
case *object.GetRangeRequest:
return v.GetBody()
case *object.GetRangeResponse:
return v.GetBody()
case *object.GetRangeHashRequest:
return v.GetBody()
case *object.GetRangeHashResponse:
return v.GetBody()
/* Netmap */
case *netmap.LocalNodeInfoRequest:
return v.GetBody()
case *netmap.LocalNodeInfoResponse:
return v.GetBody()
case *netmap.NetworkInfoRequest:
return v.GetBody()
case *netmap.NetworkInfoResponse:
return v.GetBody()
case *netmap.SnapshotRequest:
return v.GetBody()
case *netmap.SnapshotResponse:
return v.GetBody()
/* Reputation */
case *reputation.AnnounceLocalTrustRequest:
return v.GetBody()
case *reputation.AnnounceLocalTrustResponse:
return v.GetBody()
case *reputation.AnnounceIntermediateResultRequest:
return v.GetBody()
case *reputation.AnnounceIntermediateResultResponse:
return v.GetBody()
}
}

26
signature/marshaller.go Normal file
View file

@ -0,0 +1,26 @@
package signature
type stableMarshaler interface {
StableMarshal([]byte) []byte
StableSize() int
}
type StableMarshalerWrapper struct {
SM stableMarshaler
}
func (s StableMarshalerWrapper) ReadSignedData(buf []byte) ([]byte, error) {
if s.SM != nil {
return s.SM.StableMarshal(buf), nil
}
return nil, nil
}
func (s StableMarshalerWrapper) SignedDataSize() int {
if s.SM != nil {
return s.SM.StableSize()
}
return 0
}

View file

@ -2,17 +2,12 @@ package signature
import (
"crypto/ecdsa"
"errors"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/reputation"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/signature"
"golang.org/x/sync/errgroup"
)
type serviceRequest interface {
@ -27,179 +22,92 @@ type serviceResponse interface {
SetVerificationHeader(*session.ResponseVerificationHeader)
}
type stableMarshaler interface {
StableMarshal([]byte) []byte
StableSize() int
}
type StableMarshalerWrapper struct {
SM stableMarshaler
}
type metaHeader interface {
stableMarshaler
getOrigin() metaHeader
}
type verificationHeader interface {
stableMarshaler
GetBodySignature() *refs.Signature
type signatureReceiver interface {
SetBodySignature(*refs.Signature)
GetMetaSignature() *refs.Signature
SetMetaSignature(*refs.Signature)
GetOriginSignature() *refs.Signature
SetOriginSignature(*refs.Signature)
setOrigin(stableMarshaler)
getOrigin() verificationHeader
}
type requestMetaHeader struct {
*session.RequestMetaHeader
}
type responseMetaHeader struct {
*session.ResponseMetaHeader
}
type requestVerificationHeader struct {
*session.RequestVerificationHeader
}
type responseVerificationHeader struct {
*session.ResponseVerificationHeader
}
func (h *requestMetaHeader) getOrigin() metaHeader {
return &requestMetaHeader{
RequestMetaHeader: h.GetOrigin(),
}
}
func (h *responseMetaHeader) getOrigin() metaHeader {
return &responseMetaHeader{
ResponseMetaHeader: h.GetOrigin(),
}
}
func (h *requestVerificationHeader) getOrigin() verificationHeader {
if origin := h.GetOrigin(); origin != nil {
return &requestVerificationHeader{
RequestVerificationHeader: origin,
}
}
return nil
}
func (h *requestVerificationHeader) setOrigin(m stableMarshaler) {
if m != nil {
h.SetOrigin(m.(*session.RequestVerificationHeader))
}
}
func (r *responseVerificationHeader) getOrigin() verificationHeader {
if origin := r.GetOrigin(); origin != nil {
return &responseVerificationHeader{
ResponseVerificationHeader: origin,
}
}
return nil
}
func (r *responseVerificationHeader) setOrigin(m stableMarshaler) {
if m != nil {
r.SetOrigin(m.(*session.ResponseVerificationHeader))
}
}
func (s StableMarshalerWrapper) ReadSignedData(buf []byte) ([]byte, error) {
if s.SM != nil {
return s.SM.StableMarshal(buf), nil
}
return nil, nil
}
func (s StableMarshalerWrapper) SignedDataSize() int {
if s.SM != nil {
return s.SM.StableSize()
}
return 0
}
// SignServiceMessage signes service message with key.
func SignServiceMessage(key *ecdsa.PrivateKey, msg interface{}) error {
var (
body, meta, verifyOrigin stableMarshaler
verifyHdr verificationHeader
verifyHdrSetter func(verificationHeader)
)
switch v := msg.(type) {
case nil:
return nil
case serviceRequest:
body = serviceMessageBody(v)
meta = v.GetMetaHeader()
verifyHdr = &requestVerificationHeader{new(session.RequestVerificationHeader)}
verifyHdrSetter = func(h verificationHeader) {
v.SetVerificationHeader(h.(*requestVerificationHeader).RequestVerificationHeader)
}
if h := v.GetVerificationHeader(); h != nil {
verifyOrigin = h
}
return signServiceRequest(key, v)
case serviceResponse:
body = serviceMessageBody(v)
meta = v.GetMetaHeader()
verifyHdr = &responseVerificationHeader{new(session.ResponseVerificationHeader)}
verifyHdrSetter = func(h verificationHeader) {
v.SetVerificationHeader(h.(*responseVerificationHeader).ResponseVerificationHeader)
}
if h := v.GetVerificationHeader(); h != nil {
verifyOrigin = h
}
return signServiceResponse(key, v)
default:
panic(fmt.Sprintf("unsupported session message %T", v))
}
}
if verifyOrigin == nil {
func signServiceRequest(key *ecdsa.PrivateKey, v serviceRequest) error {
result := &session.RequestVerificationHeader{}
body := serviceMessageBody(v)
meta := v.GetMetaHeader()
header := v.GetVerificationHeader()
if err := signMessageParts(key, body, meta, header, header != nil, result); err != nil {
return err
}
result.SetOrigin(header)
v.SetVerificationHeader(result)
return nil
}
func signServiceResponse(key *ecdsa.PrivateKey, v serviceResponse) error {
result := &session.ResponseVerificationHeader{}
body := serviceMessageBody(v)
meta := v.GetMetaHeader()
header := v.GetVerificationHeader()
if err := signMessageParts(key, body, meta, header, header != nil, result); err != nil {
return err
}
result.SetOrigin(header)
v.SetVerificationHeader(result)
return nil
}
func signMessageParts(key *ecdsa.PrivateKey, body, meta, header stableMarshaler, hasHeader bool, result signatureReceiver) error {
eg := &errgroup.Group{}
if !hasHeader {
// sign session message body
if err := signServiceMessagePart(key, body, verifyHdr.SetBodySignature); err != nil {
return fmt.Errorf("could not sign body: %w", err)
}
eg.Go(func() error {
if err := signServiceMessagePart(key, body, result.SetBodySignature); err != nil {
return fmt.Errorf("could not sign body: %w", err)
}
return nil
})
}
// sign meta header
if err := signServiceMessagePart(key, meta, verifyHdr.SetMetaSignature); err != nil {
return fmt.Errorf("could not sign meta header: %w", err)
}
eg.Go(func() error {
if err := signServiceMessagePart(key, meta, result.SetMetaSignature); err != nil {
return fmt.Errorf("could not sign meta header: %w", err)
}
return nil
})
// sign verification header origin
if err := signServiceMessagePart(key, verifyOrigin, verifyHdr.SetOriginSignature); err != nil {
return fmt.Errorf("could not sign origin of verification header: %w", err)
}
// wrap origin verification header
verifyHdr.setOrigin(verifyOrigin)
// update matryoshka verification header
verifyHdrSetter(verifyHdr)
return nil
eg.Go(func() error {
if err := signServiceMessagePart(key, header, result.SetOriginSignature); err != nil {
return fmt.Errorf("could not sign origin of verification header: %w", err)
}
return nil
})
return eg.Wait()
}
func signServiceMessagePart(key *ecdsa.PrivateKey, part stableMarshaler, sigWrite func(*refs.Signature)) error {
var sig *refs.Signature
wrapper := StableMarshalerWrapper{
SM: part,
}
// sign part
if err := signature.SignDataWithHandler(
key,
&StableMarshalerWrapper{part},
wrapper,
func(s *refs.Signature) {
sig = s
},
@ -212,182 +120,3 @@ func signServiceMessagePart(key *ecdsa.PrivateKey, part stableMarshaler, sigWrit
return nil
}
func VerifyServiceMessage(msg interface{}) error {
var (
meta metaHeader
verify verificationHeader
)
switch v := msg.(type) {
case nil:
return nil
case serviceRequest:
meta = &requestMetaHeader{
RequestMetaHeader: v.GetMetaHeader(),
}
verify = &requestVerificationHeader{
RequestVerificationHeader: v.GetVerificationHeader(),
}
case serviceResponse:
meta = &responseMetaHeader{
ResponseMetaHeader: v.GetMetaHeader(),
}
verify = &responseVerificationHeader{
ResponseVerificationHeader: v.GetVerificationHeader(),
}
default:
panic(fmt.Sprintf("unsupported session message %T", v))
}
body := serviceMessageBody(msg)
size := body.StableSize()
if sz := meta.StableSize(); sz > size {
size = sz
}
if sz := verify.StableSize(); sz > size {
size = sz
}
buf := make([]byte, 0, size)
return verifyMatryoshkaLevel(body, meta, verify, buf)
}
func verifyMatryoshkaLevel(body stableMarshaler, meta metaHeader, verify verificationHeader, buf []byte) error {
if err := verifyServiceMessagePart(meta, verify.GetMetaSignature, buf); err != nil {
return fmt.Errorf("could not verify meta header: %w", err)
}
origin := verify.getOrigin()
if err := verifyServiceMessagePart(origin, verify.GetOriginSignature, buf); err != nil {
return fmt.Errorf("could not verify origin of verification header: %w", err)
}
if origin == nil {
if err := verifyServiceMessagePart(body, verify.GetBodySignature, buf); err != nil {
return fmt.Errorf("could not verify body: %w", err)
}
return nil
}
if verify.GetBodySignature() != nil {
return errors.New("body signature at the matryoshka upper level")
}
return verifyMatryoshkaLevel(body, meta.getOrigin(), origin, buf)
}
func verifyServiceMessagePart(part stableMarshaler, sigRdr func() *refs.Signature, buf []byte) error {
return signature.VerifyDataWithSource(
&StableMarshalerWrapper{part},
sigRdr,
signature.WithBuffer(buf),
)
}
func serviceMessageBody(req interface{}) stableMarshaler {
switch v := req.(type) {
default:
panic(fmt.Sprintf("unsupported session message %T", req))
/* Accounting */
case *accounting.BalanceRequest:
return v.GetBody()
case *accounting.BalanceResponse:
return v.GetBody()
/* Session */
case *session.CreateRequest:
return v.GetBody()
case *session.CreateResponse:
return v.GetBody()
/* Container */
case *container.PutRequest:
return v.GetBody()
case *container.PutResponse:
return v.GetBody()
case *container.DeleteRequest:
return v.GetBody()
case *container.DeleteResponse:
return v.GetBody()
case *container.GetRequest:
return v.GetBody()
case *container.GetResponse:
return v.GetBody()
case *container.ListRequest:
return v.GetBody()
case *container.ListResponse:
return v.GetBody()
case *container.SetExtendedACLRequest:
return v.GetBody()
case *container.SetExtendedACLResponse:
return v.GetBody()
case *container.GetExtendedACLRequest:
return v.GetBody()
case *container.GetExtendedACLResponse:
return v.GetBody()
case *container.AnnounceUsedSpaceRequest:
return v.GetBody()
case *container.AnnounceUsedSpaceResponse:
return v.GetBody()
/* Object */
case *object.PutRequest:
return v.GetBody()
case *object.PutResponse:
return v.GetBody()
case *object.GetRequest:
return v.GetBody()
case *object.GetResponse:
return v.GetBody()
case *object.HeadRequest:
return v.GetBody()
case *object.HeadResponse:
return v.GetBody()
case *object.SearchRequest:
return v.GetBody()
case *object.SearchResponse:
return v.GetBody()
case *object.DeleteRequest:
return v.GetBody()
case *object.DeleteResponse:
return v.GetBody()
case *object.GetRangeRequest:
return v.GetBody()
case *object.GetRangeResponse:
return v.GetBody()
case *object.GetRangeHashRequest:
return v.GetBody()
case *object.GetRangeHashResponse:
return v.GetBody()
/* Netmap */
case *netmap.LocalNodeInfoRequest:
return v.GetBody()
case *netmap.LocalNodeInfoResponse:
return v.GetBody()
case *netmap.NetworkInfoRequest:
return v.GetBody()
case *netmap.NetworkInfoResponse:
return v.GetBody()
case *netmap.SnapshotRequest:
return v.GetBody()
case *netmap.SnapshotResponse:
return v.GetBody()
/* Reputation */
case *reputation.AnnounceLocalTrustRequest:
return v.GetBody()
case *reputation.AnnounceLocalTrustResponse:
return v.GetBody()
case *reputation.AnnounceIntermediateResultRequest:
return v.GetBody()
case *reputation.AnnounceIntermediateResultResponse:
return v.GetBody()
}
}

View file

@ -70,3 +70,56 @@ func TestBalanceResponse(t *testing.T) {
// verification must fail
require.Error(t, VerifyServiceMessage(req))
}
func BenchmarkSignRequest(b *testing.B) {
key, _ := crypto.LoadPrivateKey("Kwk6k2eC3L3QuPvD8aiaNyoSXgQ2YL1bwS5CP1oKoA9waeAze97s")
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
b.StopTimer()
dec := new(accounting.Decimal)
dec.SetValue(100)
body := new(accounting.BalanceResponseBody)
body.SetBalance(dec)
meta := new(session.ResponseMetaHeader)
meta.SetTTL(1)
resp := new(accounting.BalanceResponse)
resp.SetBody(body)
resp.SetMetaHeader(meta)
b.StartTimer()
SignServiceMessage(key, resp)
}
}
func BenchmarkVerifyRequest(b *testing.B) {
key, _ := crypto.LoadPrivateKey("Kwk6k2eC3L3QuPvD8aiaNyoSXgQ2YL1bwS5CP1oKoA9waeAze97s")
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
b.StopTimer()
dec := new(accounting.Decimal)
dec.SetValue(100)
body := new(accounting.BalanceResponseBody)
body.SetBalance(dec)
meta := new(session.ResponseMetaHeader)
meta.SetTTL(1)
resp := new(accounting.BalanceResponse)
resp.SetBody(body)
resp.SetMetaHeader(meta)
SignServiceMessage(key, resp)
b.StartTimer()
VerifyServiceMessage(resp)
}
}

127
signature/verify.go Normal file
View file

@ -0,0 +1,127 @@
package signature
import (
"errors"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/signature"
"golang.org/x/sync/errgroup"
)
type signatureProvider interface {
GetBodySignature() *refs.Signature
GetMetaSignature() *refs.Signature
GetOriginSignature() *refs.Signature
}
// VerifyServiceMessage verifies service message.
func VerifyServiceMessage(msg interface{}) error {
switch v := msg.(type) {
case nil:
return nil
case serviceRequest:
return verifyServiceRequest(v)
case serviceResponse:
return verifyServiceResponse(v)
default:
panic(fmt.Sprintf("unsupported session message %T", v))
}
}
func verifyServiceRequest(v serviceRequest) error {
meta := v.GetMetaHeader()
verificationHeader := v.GetVerificationHeader()
body := serviceMessageBody(v)
return verifyServiceRequestRecursive(body, meta, verificationHeader)
}
func verifyServiceRequestRecursive(body stableMarshaler, meta *session.RequestMetaHeader, verify *session.RequestVerificationHeader) error {
verificationHeaderOrigin := verify.GetOrigin()
metaOrigin := meta.GetOrigin()
stop, err := verifyMessageParts(body, meta, verificationHeaderOrigin, verificationHeaderOrigin != nil, verify)
if err != nil {
return err
}
if stop {
return nil
}
return verifyServiceRequestRecursive(body, metaOrigin, verificationHeaderOrigin)
}
func verifyMessageParts(body, meta, originHeader stableMarshaler, hasOriginHeader bool, sigProvider signatureProvider) (stop bool, err error) {
eg := &errgroup.Group{}
eg.Go(func() error {
if err := verifyServiceMessagePart(meta, sigProvider.GetMetaSignature); err != nil {
return fmt.Errorf("could not verify meta header: %w", err)
}
return nil
})
eg.Go(func() error {
if err := verifyServiceMessagePart(originHeader, sigProvider.GetOriginSignature); err != nil {
return fmt.Errorf("could not verify origin of verification header: %w", err)
}
return nil
})
if !hasOriginHeader {
eg.Go(func() error {
if err := verifyServiceMessagePart(body, sigProvider.GetBodySignature); err != nil {
return fmt.Errorf("could not verify body: %w", err)
}
return nil
})
}
if err := eg.Wait(); err != nil {
return false, err
}
if !hasOriginHeader {
return true, nil
}
if sigProvider.GetBodySignature() != nil {
return false, errors.New("body signature misses at the matryoshka upper level")
}
return false, nil
}
func verifyServiceResponse(v serviceResponse) error {
meta := v.GetMetaHeader()
verificationHeader := v.GetVerificationHeader()
body := serviceMessageBody(v)
return verifyServiceResponseRecursive(body, meta, verificationHeader)
}
func verifyServiceResponseRecursive(body stableMarshaler, meta *session.ResponseMetaHeader, verify *session.ResponseVerificationHeader) error {
verificationHeaderOrigin := verify.GetOrigin()
metaOrigin := meta.GetOrigin()
stop, err := verifyMessageParts(body, meta, verificationHeaderOrigin, verificationHeaderOrigin != nil, verify)
if err != nil {
return err
}
if stop {
return nil
}
return verifyServiceResponseRecursive(body, metaOrigin, verificationHeaderOrigin)
}
func verifyServiceMessagePart(part stableMarshaler, sigRdr func() *refs.Signature) error {
wrapper := StableMarshalerWrapper{
SM: part,
}
return signature.VerifyDataWithSource(
wrapper,
sigRdr,
)
}

Binary file not shown.

Binary file not shown.

29
util/signature/buffer.go Normal file
View file

@ -0,0 +1,29 @@
package signature
import "sync"
const poolSliceMaxSize = 64 * 1024
var buffersPool = sync.Pool{
New: func() any {
return make([]byte, 0)
},
}
func newBufferFromPool(size int) []byte {
result := buffersPool.Get().([]byte)
if cap(result) < size {
result = make([]byte, size)
} else {
result = result[:size]
}
return result
}
func returnBufferToPool(buf []byte) {
if cap(buf) > poolSliceMaxSize {
return
}
buf = buf[:0]
buffersPool.Put(buf)
}

View file

@ -35,7 +35,10 @@ func SignDataWithHandler(key *ecdsa.PrivateKey, src DataSource, handler KeySigna
opts[i](cfg)
}
data, err := readSignedData(cfg, src)
buffer := newBufferFromPool(src.SignedDataSize())
defer returnBufferToPool(buffer)
data, err := src.ReadSignedData(buffer)
if err != nil {
return err
}
@ -61,7 +64,10 @@ func VerifyDataWithSource(dataSrc DataSource, sigSrc KeySignatureSource, opts ..
opts[i](cfg)
}
data, err := readSignedData(cfg, dataSrc)
buffer := newBufferFromPool(dataSrc.SignedDataSize())
defer returnBufferToPool(buffer)
data, err := dataSrc.ReadSignedData(buffer)
if err != nil {
return err
}
@ -76,13 +82,3 @@ func SignData(key *ecdsa.PrivateKey, v DataWithSignature, opts ...SignOption) er
func VerifyData(src DataWithSignature, opts ...SignOption) error {
return VerifyDataWithSource(src, src.GetSignature, opts...)
}
func readSignedData(cfg *cfg, src DataSource) ([]byte, error) {
size := src.SignedDataSize()
if cfg.buffer == nil || cap(cfg.buffer) < size {
cfg.buffer = make([]byte, size)
} else {
cfg.buffer = cfg.buffer[:size]
}
return src.ReadSignedData(cfg.buffer)
}

View file

@ -13,7 +13,6 @@ import (
type cfg struct {
schemeFixed bool
scheme refs.SignatureScheme
buffer []byte
}
func defaultCfg() *cfg {
@ -36,9 +35,10 @@ func verify(cfg *cfg, data []byte, sig *refs.Signature) error {
case refs.ECDSA_RFC6979_SHA256:
return crypto.VerifyRFC6979(pub, data, sig.GetSign())
case refs.ECDSA_RFC6979_SHA256_WALLET_CONNECT:
buf := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
base64.StdEncoding.Encode(buf, data)
if !walletconnect.Verify(pub, buf, sig.GetSign()) {
buffer := newBufferFromPool(base64.StdEncoding.EncodedLen(len(data)))
defer returnBufferToPool(buffer)
base64.StdEncoding.Encode(buffer, data)
if !walletconnect.Verify(pub, buffer, sig.GetSign()) {
return crypto.ErrInvalidSignature
}
return nil
@ -54,9 +54,10 @@ func sign(cfg *cfg, key *ecdsa.PrivateKey, data []byte) ([]byte, error) {
case refs.ECDSA_RFC6979_SHA256:
return crypto.SignRFC6979(key, data)
case refs.ECDSA_RFC6979_SHA256_WALLET_CONNECT:
buf := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
base64.StdEncoding.Encode(buf, data)
return walletconnect.Sign(key, buf)
buffer := newBufferFromPool(base64.StdEncoding.EncodedLen(len(data)))
defer returnBufferToPool(buffer)
base64.StdEncoding.Encode(buffer, data)
return walletconnect.Sign(key, buffer)
default:
panic(fmt.Sprintf("unsupported scheme %s", cfg.scheme))
}
@ -69,13 +70,6 @@ func SignWithRFC6979() SignOption {
}
}
// WithBuffer allows providing pre-allocated buffer for signature verification.
func WithBuffer(buf []byte) SignOption {
return func(c *cfg) {
c.buffer = buf
}
}
func SignWithWalletConnect() SignOption {
return func(c *cfg) {
c.scheme = refs.ECDSA_RFC6979_SHA256_WALLET_CONNECT